diff --git a/.ci/Jenkinsfile_flaky b/.ci/Jenkinsfile_flaky index e1cbac0528b1f..8ad02b7162b6a 100644 --- a/.ci/Jenkinsfile_flaky +++ b/.ci/Jenkinsfile_flaky @@ -34,7 +34,7 @@ stage("Kibana Pipeline") { if (!IS_XPACK) { kibanaPipeline.buildOss() if (CI_GROUP == '1') { - runbld "./test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh" + runbld("./test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh", "Build kbn tp sample panel action for ciGroup1") } } else { kibanaPipeline.buildXpack() @@ -62,18 +62,18 @@ stage("Kibana Pipeline") { def getWorkerFromParams(isXpack, job, ciGroup) { if (!isXpack) { if (job == 'firefoxSmoke') { - return kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }) + return kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld('./test/scripts/jenkins_firefox_smoke.sh', 'Execute kibana-firefoxSmoke') }) } else if(job == 'visualRegression') { - return kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }) + return kibanaPipeline.getPostBuildWorker('visualRegression', { runbld('./test/scripts/jenkins_visual_regression.sh', 'Execute kibana-visualRegression') }) } else { return kibanaPipeline.getOssCiGroupWorker(ciGroup) } } if (job == 'firefoxSmoke') { - return kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }) + return kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld('./test/scripts/jenkins_xpack_firefox_smoke.sh', 'Execute xpack-firefoxSmoke') }) } else if(job == 'visualRegression') { - return kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }) + return kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld('./test/scripts/jenkins_xpack_visual_regression.sh', 'Execute xpack-visualRegression') }) } else { return kibanaPipeline.getXpackCiGroupWorker(ciGroup) } diff --git a/.eslintrc.js b/.eslintrc.js index 16a80f01278a5..daf49d9d08281 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -214,13 +214,6 @@ module.exports = { 'jsx-a11y/click-events-have-key-events': 'off', }, }, - { - files: ['x-pack/legacy/plugins/siem/**/*.{js,ts,tsx}'], - rules: { - 'react-hooks/exhaustive-deps': 'off', - 'react-hooks/rules-of-hooks': 'off', - }, - }, { files: ['x-pack/legacy/plugins/snapshot_restore/**/*.{js,ts,tsx}'], rules: { @@ -839,6 +832,8 @@ module.exports = { // might be introduced after the other warns are fixed // 'react/jsx-sort-props': 'error', 'react/jsx-tag-spacing': 'error', + // might be introduced after the other warns are fixed + 'react-hooks/exhaustive-deps': 'off', 'require-atomic-updates': 'error', 'rest-spread-spacing': ['error', 'never'], 'symbol-description': 'error', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 94296d076189b..0f1136fd5334b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -28,11 +28,6 @@ # Canvas /x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas -# Code -/x-pack/legacy/plugins/code/ @teams/code -/x-pack/test/functional/apps/code/ @teams/code -/x-pack/test/api_integration/apis/code/ @teams/code - # Logs & Metrics UI /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui /x-pack/legacy/plugins/integrations_manager/ @elastic/epm diff --git a/Jenkinsfile b/Jenkinsfile index 8d8579736f639..c002832d4d51a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,9 +24,9 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'oss-ciGroup10': kibanaPipeline.getOssCiGroupWorker(10), 'oss-ciGroup11': kibanaPipeline.getOssCiGroupWorker(11), 'oss-ciGroup12': kibanaPipeline.getOssCiGroupWorker(12), - 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }), - 'oss-accessibility': kibanaPipeline.getPostBuildWorker('accessibility', { runbld './test/scripts/jenkins_accessibility.sh' }), - 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }), + 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld('./test/scripts/jenkins_firefox_smoke.sh', 'Execute kibana-firefoxSmoke') }), + 'oss-accessibility': kibanaPipeline.getPostBuildWorker('accessibility', { runbld('./test/scripts/jenkins_accessibility.sh', 'Execute kibana-accessibility') }), + 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld('./test/scripts/jenkins_visual_regression.sh', 'Execute kibana-visualRegression') }), ]), 'kibana-xpack-agent': kibanaPipeline.withWorkers('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ 'xpack-ciGroup1': kibanaPipeline.getXpackCiGroupWorker(1), @@ -39,9 +39,9 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'xpack-ciGroup8': kibanaPipeline.getXpackCiGroupWorker(8), 'xpack-ciGroup9': kibanaPipeline.getXpackCiGroupWorker(9), 'xpack-ciGroup10': kibanaPipeline.getXpackCiGroupWorker(10), - 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }), - 'xpack-accessibility': kibanaPipeline.getPostBuildWorker('xpack-accessibility', { runbld './test/scripts/jenkins_xpack_accessibility.sh' }), - 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }), + 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld('./test/scripts/jenkins_xpack_firefox_smoke.sh', 'Execute xpack-firefoxSmoke') }), + 'xpack-accessibility': kibanaPipeline.getPostBuildWorker('xpack-accessibility', { runbld('./test/scripts/jenkins_xpack_accessibility.sh', 'Execute xpack-accessibility') }), + 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld('./test/scripts/jenkins_xpack_visual_regression.sh', 'Execute xpack-visualRegression') }), ]), ]) } diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 5fd3ef5e8ff4b..461d51a3e76e3 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -120,6 +120,7 @@ You should prefer modern language features in a lot of cases, e.g.: * Prefer arrow function over storing `this` (no `const self = this;`) * Prefer template strings over string concatenation * Prefer the spread operator for copying arrays (`[...arr]`) over `arr.slice()` +* Use optional chaining (`?.`) and nullish Coalescing (`??`) over `lodash.get` (and similar utilities) ### Avoid mutability and state diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md index 6431589c55bd1..71a7fd8fb6a22 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md @@ -21,7 +21,7 @@ Each route can have only one handler function, which is executed when the route ```ts const router = createRouter(); -// handler is called when '${my-plugin-id}/path' resource is requested with `GET` method +// handler is called when '/path' resource is requested with `GET` method router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); ``` diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md index d943ad53af843..dba0ad8c8560c 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md @@ -27,7 +27,7 @@ export interface HttpServiceSetup ## Example -To handle an incoming request in your plugin you should: - Create a `Router` instance. Router is already configured to use `plugin-id` to prefix path segment for your routes. +To handle an incoming request in your plugin you should: - Create a `Router` instance. ```ts const router = httpSetup.createRouter(); @@ -61,7 +61,7 @@ const handler = async (context: RequestHandlerContext, request: KibanaRequest, r } ``` -- Register route handler for GET request to 'my-app/path/{id}' path +- Register route handler for GET request to 'path/{id}' path ```ts import { schema, TypeOf } from '@kbn/config-schema'; diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 11a50fea92f05..f4434ea7a09f4 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -320,6 +320,18 @@ supported protocols with versions. Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2 setting this to `true` enables unauthenticated users to access the Kibana server status API and status page. +`telemetry.allowChangingOptInStatus`:: *Default: true*. If `true`, +users are able to change the telemetry setting at a later time in +<>. If `false`, +{kib} looks at the value of `telemetry.optIn` to determine whether to send +telemetry data or not. `telemetry.allowChangingOptInStatus` and `telemetry.optIn` +cannot be `false` at the same time. + +`telemetry.optIn`:: *Default: true* If `true`, telemetry data is sent to Elastic. + If `false`, collection of telemetry data is disabled. + To enable telemetry and prevent users from disabling it, + set `telemetry.allowChangingOptInStatus` to `false` and `telemetry.optIn` to `true`. + `vega.enableExternalUrls:`:: *Default: false* Set this value to true to allow Vega to use any URL to access external data sources and images. If false, Vega can only get data from Elasticsearch. `xpack.license_management.enabled`:: *Default: true* Set this value to false to diff --git a/package.json b/package.json index 8fa9bf1847eb8..b0a74a4a5e04f 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "**/@types/react": "16.8.3", "**/@types/hapi": "^17.0.18", "**/@types/angular": "^1.6.56", - "**/typescript": "3.5.3", + "**/typescript": "3.7.2", "**/graphql-toolkit/lodash": "^4.17.13", "**/isomorphic-git/**/base64-js": "^1.2.1", "**/image-diff/gm/debug": "^2.6.9" @@ -327,6 +327,7 @@ "@types/pngjs": "^3.3.2", "@types/podium": "^1.0.0", "@types/prop-types": "^15.5.3", + "@types/reach__router": "^1.2.6", "@types/react": "^16.8.0", "@types/react-dom": "^16.8.0", "@types/react-redux": "^6.0.6", @@ -346,8 +347,8 @@ "@types/uuid": "^3.4.4", "@types/vinyl-fs": "^2.4.11", "@types/zen-observable": "^0.8.0", - "@typescript-eslint/eslint-plugin": "^2.5.0", - "@typescript-eslint/parser": "^2.5.0", + "@typescript-eslint/eslint-plugin": "^2.7.0", + "@typescript-eslint/parser": "^2.7.0", "angular-mocks": "^1.7.8", "archiver": "^3.1.1", "axe-core": "^3.3.2", @@ -360,7 +361,7 @@ "chance": "1.0.18", "cheerio": "0.22.0", "chokidar": "3.2.1", - "chromedriver": "^77.0.0", + "chromedriver": "78.0.1", "classnames": "2.2.6", "dedent": "^0.7.0", "delete-empty": "^2.0.0", @@ -444,7 +445,7 @@ "supertest": "^3.1.0", "supertest-as-promised": "^4.0.2", "tree-kill": "^1.2.1", - "typescript": "3.5.3", + "typescript": "3.7.2", "typings-tester": "^0.3.2", "vinyl-fs": "^3.0.3", "xml2js": "^0.4.22", diff --git a/packages/eslint-config-kibana/package.json b/packages/eslint-config-kibana/package.json index b5079a49c8385..c67629f058d5a 100644 --- a/packages/eslint-config-kibana/package.json +++ b/packages/eslint-config-kibana/package.json @@ -15,8 +15,8 @@ }, "homepage": "https://github.com/elastic/eslint-config-kibana#readme", "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^2.5.0", - "@typescript-eslint/parser": "^2.5.0", + "@typescript-eslint/eslint-plugin": "^2.7.0", + "@typescript-eslint/parser": "^2.7.0", "babel-eslint": "^10.0.3", "eslint": "^6.5.1", "eslint-plugin-babel": "^5.3.0", diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json index b0ac86b465a62..f59fbf4720835 100644 --- a/packages/kbn-analytics/package.json +++ b/packages/kbn-analytics/package.json @@ -17,6 +17,6 @@ "@babel/cli": "7.5.5", "@kbn/dev-utils": "1.0.0", "@kbn/babel-preset": "1.0.0", - "typescript": "3.5.3" + "typescript": "3.7.2" } } diff --git a/packages/kbn-babel-preset/common_preset.js b/packages/kbn-babel-preset/common_preset.js index d2bad41e8de86..d1b7bc20dd9f9 100644 --- a/packages/kbn-babel-preset/common_preset.js +++ b/packages/kbn-babel-preset/common_preset.js @@ -27,6 +27,13 @@ const plugins = [ // // See https://github.com/babel/proposals/issues/12 for progress require.resolve('@babel/plugin-proposal-class-properties'), + + // Optional Chaining proposal is stage 3 (https://github.com/tc39/proposal-optional-chaining) + // Need this since we are using TypeScript 3.7+ + require.resolve('@babel/plugin-proposal-optional-chaining'), + // Nullish coalescing proposal is stage 3 (https://github.com/tc39/proposal-nullish-coalescing) + // Need this since we are using TypeScript 3.7+ + require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), ]; const isTestEnv = process.env.BABEL_ENV === 'test' || process.env.NODE_ENV === 'test'; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index c22cf175b29e5..1913301e21a76 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -5,6 +5,8 @@ "license": "Apache-2.0", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.5.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4", + "@babel/plugin-proposal-optional-chaining": "^7.6.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-modules-commonjs": "^7.5.0", "@babel/preset-env": "^7.5.5", diff --git a/packages/kbn-config-schema/package.json b/packages/kbn-config-schema/package.json index 4880fb4ebfdee..71c0ae4bff1f9 100644 --- a/packages/kbn-config-schema/package.json +++ b/packages/kbn-config-schema/package.json @@ -10,7 +10,7 @@ "kbn:bootstrap": "yarn build" }, "devDependencies": { - "typescript": "3.5.3" + "typescript": "3.7.2" }, "peerDependencies": { "joi": "^13.5.2", diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index e8781f6d901d9..09753afeb120f 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -21,7 +21,7 @@ "tslib": "^1.9.3" }, "devDependencies": { - "typescript": "3.5.3", + "typescript": "3.7.2", "@kbn/expect": "1.0.0", "chance": "1.0.18" } diff --git a/packages/kbn-elastic-idx/package.json b/packages/kbn-elastic-idx/package.json index abfaea75357dd..9532983942d6b 100644 --- a/packages/kbn-elastic-idx/package.json +++ b/packages/kbn-elastic-idx/package.json @@ -20,7 +20,7 @@ "@babel/core": "^7.5.5", "@babel/plugin-transform-async-to-generator": "^7.5.0", "jest": "^24.9.0", - "typescript": "3.5.3" + "typescript": "3.7.2" }, "jest": { "testEnvironment": "node" diff --git a/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js b/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js deleted file mode 100644 index d9f559987f58b..0000000000000 --- a/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import _ from 'lodash'; -import { migrateFilter } from '../migrate_filter'; - -describe('migrateFilter', function () { - - const oldMatchPhraseFilter = { - match: { - fieldFoo: { - query: 'foobar', - type: 'phrase' - } - } - }; - - const newMatchPhraseFilter = { - match_phrase: { - fieldFoo: { - query: 'foobar' - } - } - }; - - // https://github.com/elastic/elasticsearch/pull/17508 - it('should migrate match filters of type phrase', function () { - const migratedFilter = migrateFilter(oldMatchPhraseFilter); - expect(_.isEqual(migratedFilter, newMatchPhraseFilter)).to.be(true); - }); - - it('should not modify the original filter', function () { - const oldMatchPhraseFilterCopy = _.clone(oldMatchPhraseFilter, true); - migrateFilter(oldMatchPhraseFilter); - expect(_.isEqual(oldMatchPhraseFilter, oldMatchPhraseFilterCopy)).to.be(true); - }); - - it('should return the original filter if no migration is necessary', function () { - const originalFilter = { - match_all: {} - }; - const migratedFilter = migrateFilter(originalFilter); - expect(migratedFilter).to.be(originalFilter); - expect(_.isEqual(migratedFilter, originalFilter)).to.be(true); - }); - -}); diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js b/packages/kbn-es-query/src/es_query/__tests__/from_filters.js deleted file mode 100644 index 676992e4dddc8..0000000000000 --- a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { buildQueryFromFilters } from '../from_filters'; - -describe('build query', function () { - describe('buildQueryFromFilters', function () { - it('should return the parameters of an Elasticsearch bool query', function () { - const result = buildQueryFromFilters([]); - const expected = { - must: [], - filter: [], - should: [], - must_not: [], - }; - expect(result).to.eql(expected); - }); - - it('should transform an array of kibana filters into ES queries combined in the bool clauses', function () { - const filters = [ - { - match_all: {}, - meta: { type: 'match_all' }, - }, - { - exists: { field: 'foo' }, - meta: { type: 'exists' }, - }, - ]; - - const expectedESQueries = [ - { match_all: {} }, - { exists: { field: 'foo' } }, - ]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - - it('should remove disabled filters', function () { - const filters = [ - { - match_all: {}, - meta: { type: 'match_all', negate: true, disabled: true }, - }, - ]; - - const expectedESQueries = []; - - const result = buildQueryFromFilters(filters); - - expect(result.must_not).to.eql(expectedESQueries); - }); - - it('should remove falsy filters', function () { - const filters = [null, undefined]; - - const expectedESQueries = []; - - const result = buildQueryFromFilters(filters); - - expect(result.must_not).to.eql(expectedESQueries); - expect(result.must).to.eql(expectedESQueries); - }); - - it('should place negated filters in the must_not clause', function () { - const filters = [ - { - match_all: {}, - meta: { type: 'match_all', negate: true }, - }, - ]; - - const expectedESQueries = [{ match_all: {} }]; - - const result = buildQueryFromFilters(filters); - - expect(result.must_not).to.eql(expectedESQueries); - }); - - it('should translate old ES filter syntax into ES 5+ query objects', function () { - const filters = [ - { - query: { exists: { field: 'foo' } }, - meta: { type: 'exists' }, - }, - ]; - - const expectedESQueries = [ - { - exists: { field: 'foo' }, - }, - ]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - - it('should migrate deprecated match syntax', function () { - const filters = [ - { - query: { match: { extension: { query: 'foo', type: 'phrase' } } }, - meta: { type: 'phrase' }, - }, - ]; - - const expectedESQueries = [ - { - match_phrase: { extension: { query: 'foo' } }, - }, - ]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - - it('should not add query:queryString:options to query_string filters', function () { - const filters = [ - { - query: { query_string: { query: 'foo' } }, - meta: { type: 'query_string' }, - }, - ]; - const expectedESQueries = [{ query_string: { query: 'foo' } }]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - }); -}); diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_lucene.js b/packages/kbn-es-query/src/es_query/__tests__/from_lucene.js deleted file mode 100644 index 4361659021bd5..0000000000000 --- a/packages/kbn-es-query/src/es_query/__tests__/from_lucene.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { buildQueryFromLucene } from '../from_lucene'; -import { decorateQuery } from '../decorate_query'; -import { luceneStringToDsl } from '../lucene_string_to_dsl'; - -describe('build query', function () { - - describe('buildQueryFromLucene', function () { - - it('should return the parameters of an Elasticsearch bool query', function () { - const result = buildQueryFromLucene(); - const expected = { - must: [], - filter: [], - should: [], - must_not: [], - }; - expect(result).to.eql(expected); - }); - - it('should transform an array of lucene queries into ES queries combined in the bool\'s must clause', function () { - const queries = [ - { query: 'foo:bar', language: 'lucene' }, - { query: 'bar:baz', language: 'lucene' }, - ]; - - const expectedESQueries = queries.map( - (query) => { - return decorateQuery(luceneStringToDsl(query.query), {}); - } - ); - - const result = buildQueryFromLucene(queries, {}); - - expect(result.must).to.eql(expectedESQueries); - }); - - it('should also accept queries in ES query DSL format, simply passing them through', function () { - const queries = [ - { query: { match_all: {} }, language: 'lucene' }, - ]; - - const result = buildQueryFromLucene(queries, {}); - - expect(result.must).to.eql([queries[0].query]); - }); - - }); - - it('should accept a date format in the decorated queries and combine that into the bool\'s must clause', function () { - const queries = [ - { query: 'foo:bar', language: 'lucene' }, - { query: 'bar:baz', language: 'lucene' }, - ]; - const dateFormatTZ = 'America/Phoenix'; - - const expectedESQueries = queries.map( - (query) => { - return decorateQuery(luceneStringToDsl(query.query), {}, dateFormatTZ); - } - ); - - const result = buildQueryFromLucene(queries, {}, dateFormatTZ); - - expect(result.must).to.eql(expectedESQueries); - }); - -}); diff --git a/packages/kbn-es-query/src/es_query/index.d.ts b/packages/kbn-es-query/src/es_query/index.d.ts deleted file mode 100644 index 9510a18441e53..0000000000000 --- a/packages/kbn-es-query/src/es_query/index.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export function buildQueryFromFilters(filters: unknown[], indexPattern: unknown): unknown; -export function buildEsQuery( - indexPattern: unknown, - queries: unknown, - filters: unknown, - config?: { - allowLeadingWildcards: boolean; - queryStringOptions: unknown; - ignoreFilterIfFieldNotInIndex: boolean; - dateFormatTZ?: string | null; - } -): unknown; -export function getEsQueryConfig(config: { - get: (name: string) => unknown; -}): { - allowLeadingWildcards: boolean; - queryStringOptions: unknown; - ignoreFilterIfFieldNotInIndex: boolean; - dateFormatTZ?: string | null; -}; diff --git a/packages/kbn-es-query/src/es_query/migrate_filter.js b/packages/kbn-es-query/src/es_query/migrate_filter.js deleted file mode 100644 index b74fc485a6184..0000000000000 --- a/packages/kbn-es-query/src/es_query/migrate_filter.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { getConvertedValueForField } from '../utils/filters'; - -export function migrateFilter(filter, indexPattern) { - if (filter.match) { - const fieldName = Object.keys(filter.match)[0]; - - - if (isMatchPhraseFilter(filter, fieldName)) { - const params = _.get(filter, ['match', fieldName]); - if (indexPattern) { - const field = indexPattern.fields.find(f => f.name === fieldName); - if (field) { - params.query = getConvertedValueForField(field, params.query); - } - } - return { - match_phrase: { - [fieldName]: _.omit(params, 'type'), - }, - }; - } - } - - return filter; -} - -function isMatchPhraseFilter(filter, fieldName) { - return _.get(filter, ['match', fieldName, 'type']) === 'phrase'; -} diff --git a/packages/kbn-es-query/src/index.d.ts b/packages/kbn-es-query/src/index.d.ts index c06cef6367fe7..79e6903b18644 100644 --- a/packages/kbn-es-query/src/index.d.ts +++ b/packages/kbn-es-query/src/index.d.ts @@ -17,5 +17,4 @@ * under the License. */ -export * from './es_query'; export * from './kuery'; diff --git a/packages/kbn-es-query/src/index.js b/packages/kbn-es-query/src/index.js index 963999bd0999b..79e6903b18644 100644 --- a/packages/kbn-es-query/src/index.js +++ b/packages/kbn-es-query/src/index.js @@ -18,4 +18,3 @@ */ export * from './kuery'; -export * from './es_query'; diff --git a/packages/kbn-es-query/src/kuery/ast/ast.d.ts b/packages/kbn-es-query/src/kuery/ast/ast.d.ts index 06f4940e8ed3b..ef3d0ee828874 100644 --- a/packages/kbn-es-query/src/kuery/ast/ast.d.ts +++ b/packages/kbn-es-query/src/kuery/ast/ast.d.ts @@ -25,18 +25,26 @@ import { JsonObject } from '..'; export type KueryNode = any; +export type DslQuery = any; + export interface KueryParseOptions { helpers: { [key: string]: any; }; startRule: string; + allowLeadingWildcards: boolean; } export function fromKueryExpression( - expression: string, - parseOptions?: KueryParseOptions + expression: string | DslQuery, + parseOptions?: Partial ): KueryNode; -export function toElasticsearchQuery(node: KueryNode, indexPattern?: any): JsonObject; +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: any, + config?: Record, + context?: Record +): JsonObject; export function doesKueryExpressionHaveLuceneSyntaxError(expression: string): boolean; diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index 8a88626bffbe8..3e25ceb8714df 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -21,7 +21,7 @@ "del": "^5.1.0", "getopts": "^2.2.4", "supports-color": "^7.0.0", - "typescript": "3.5.3" + "typescript": "3.7.2" }, "dependencies": { "intl-format-cache": "^2.1.0", diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index ac46dd02757cf..2f9b177be6532 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -58,7 +58,7 @@ "strip-ansi": "^4.0.0", "strong-log-transformer": "^2.1.0", "tempy": "^0.3.0", - "typescript": "3.5.3", + "typescript": "3.7.2", "unlazy-loader": "^0.1.3", "webpack": "^4.41.0", "webpack-cli": "^3.3.9", diff --git a/renovate.json5 b/renovate.json5 index 0c288bb85c72c..aefbc61e8dc12 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -513,6 +513,14 @@ '@types/podium', ], }, + { + groupSlug: '@reach/router', + groupName: '@reach/router related packages', + packageNames: [ + '@reach/router', + '@types/reach__router', + ], + }, { groupSlug: 'request', groupName: 'request related packages', diff --git a/src/core/server/config/config_service.test.ts b/src/core/server/config/config_service.test.ts index 61da9af7baa7c..131e1dd501792 100644 --- a/src/core/server/config/config_service.test.ts +++ b/src/core/server/config/config_service.test.ts @@ -55,7 +55,7 @@ test('throws if config at path does not match schema', async () => { await expect( configService.setSchema('key', schema.string()) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"[key]: expected value of type [string] but got [number]"` + `"[config validation of [key]]: expected value of type [string] but got [number]"` ); }); @@ -78,11 +78,11 @@ test('re-validate config when updated', async () => { config$.next(new ObjectToConfigAdapter({ key: 123 })); await expect(valuesReceived).toMatchInlineSnapshot(` -Array [ - "value", - [Error: [key]: expected value of type [string] but got [number]], -] -`); + Array [ + "value", + [Error: [config validation of [key]]: expected value of type [string] but got [number]], + ] + `); }); test("returns undefined if fetching optional config at a path that doesn't exist", async () => { @@ -143,7 +143,7 @@ test("throws error if 'schema' is not defined for a key", async () => { const configs = configService.atPath('key'); await expect(configs.pipe(first()).toPromise()).rejects.toMatchInlineSnapshot( - `[Error: No validation schema has been defined for key]` + `[Error: No validation schema has been defined for [key]]` ); }); @@ -153,7 +153,7 @@ test("throws error if 'setSchema' called several times for the same key", async const addSchema = async () => await configService.setSchema('key', schema.string()); await addSchema(); await expect(addSchema()).rejects.toMatchInlineSnapshot( - `[Error: Validation schema for key was already registered.]` + `[Error: Validation schema for [key] was already registered.]` ); }); @@ -280,6 +280,33 @@ test('handles disabled path and marks config as used', async () => { expect(unusedPaths).toEqual([]); }); +test('does not throw if schema does not define "enabled" schema', async () => { + const initialConfig = { + pid: { + file: '/some/file.pid', + }, + }; + + const config$ = new BehaviorSubject(new ObjectToConfigAdapter(initialConfig)); + const configService = new ConfigService(config$, defaultEnv, logger); + expect( + configService.setSchema( + 'pid', + schema.object({ + file: schema.string(), + }) + ) + ).resolves.toBeUndefined(); + + const value$ = configService.atPath('pid'); + const value: any = await value$.pipe(first()).toPromise(); + expect(value.enabled).toBe(undefined); + + const valueOptional$ = configService.optionalAtPath('pid'); + const valueOptional: any = await valueOptional$.pipe(first()).toPromise(); + expect(valueOptional.enabled).toBe(undefined); +}); + test('treats config as enabled if config path is not present in config', async () => { const initialConfig = {}; @@ -292,3 +319,45 @@ test('treats config as enabled if config path is not present in config', async ( const unusedPaths = await configService.getUnusedPaths(); expect(unusedPaths).toEqual([]); }); + +test('read "enabled" even if its schema is not present', async () => { + const initialConfig = { + foo: { + enabled: true, + }, + }; + + const config$ = new BehaviorSubject(new ObjectToConfigAdapter(initialConfig)); + const configService = new ConfigService(config$, defaultEnv, logger); + + const isEnabled = await configService.isEnabledAtPath('foo'); + expect(isEnabled).toBe(true); +}); + +test('allows plugins to specify "enabled" flag via validation schema', async () => { + const initialConfig = {}; + + const config$ = new BehaviorSubject(new ObjectToConfigAdapter(initialConfig)); + const configService = new ConfigService(config$, defaultEnv, logger); + + await configService.setSchema( + 'foo', + schema.object({ enabled: schema.boolean({ defaultValue: false }) }) + ); + + expect(await configService.isEnabledAtPath('foo')).toBe(false); + + await configService.setSchema( + 'bar', + schema.object({ enabled: schema.boolean({ defaultValue: true }) }) + ); + + expect(await configService.isEnabledAtPath('bar')).toBe(true); + + await configService.setSchema( + 'baz', + schema.object({ different: schema.boolean({ defaultValue: true }) }) + ); + + expect(await configService.isEnabledAtPath('baz')).toBe(true); +}); diff --git a/src/core/server/config/config_service.ts b/src/core/server/config/config_service.ts index 8d3cc733cf250..c18a5b2000e01 100644 --- a/src/core/server/config/config_service.ts +++ b/src/core/server/config/config_service.ts @@ -54,7 +54,7 @@ export class ConfigService { public async setSchema(path: ConfigPath, schema: Type) { const namespace = pathToString(path); if (this.schemas.has(namespace)) { - throw new Error(`Validation schema for ${path} was already registered.`); + throw new Error(`Validation schema for [${path}] was already registered.`); } this.schemas.set(namespace, schema); @@ -98,14 +98,28 @@ export class ConfigService { } public async isEnabledAtPath(path: ConfigPath) { - const enabledPath = createPluginEnabledPath(path); + const namespace = pathToString(path); + + const validatedConfig = this.schemas.has(namespace) + ? await this.atPath<{ enabled?: boolean }>(path) + .pipe(first()) + .toPromise() + : undefined; + const enabledPath = createPluginEnabledPath(path); const config = await this.config$.pipe(first()).toPromise(); - if (!config.has(enabledPath)) { + + // if plugin hasn't got a config schema, we try to read "enabled" directly + const isEnabled = + validatedConfig && validatedConfig.enabled !== undefined + ? validatedConfig.enabled + : config.get(enabledPath); + + // not declared. consider that plugin is enabled by default + if (isEnabled === undefined) { return true; } - const isEnabled = config.get(enabledPath); if (isEnabled === false) { // If the plugin is _not_ enabled, we mark the entire plugin path as // handled, as it's expected that it won't be used. @@ -138,7 +152,7 @@ export class ConfigService { const namespace = pathToString(path); const schema = this.schemas.get(namespace); if (!schema) { - throw new Error(`No validation schema has been defined for ${namespace}`); + throw new Error(`No validation schema has been defined for [${namespace}]`); } return schema.validate( config, @@ -147,7 +161,7 @@ export class ConfigService { prod: this.env.mode.prod, ...this.env.packageInfo, }, - namespace + `config validation of [${namespace}]` ); } diff --git a/src/core/server/http/base_path_proxy_server.ts b/src/core/server/http/base_path_proxy_server.ts index ff7fee0198f68..cde35f3cbe995 100644 --- a/src/core/server/http/base_path_proxy_server.ts +++ b/src/core/server/http/base_path_proxy_server.ts @@ -143,6 +143,7 @@ export class BasePathProxyServer { return responseToolkit.continue; }, ], + validate: { payload: true }, }, path: `${this.httpConfig.basePath}/{kbnPath*}`, }); @@ -175,6 +176,7 @@ export class BasePathProxyServer { return responseToolkit.continue; }, ], + validate: { payload: true }, }, path: `/__UNSAFE_bypassBasePath/{kbnPath*}`, }); diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 3354324c12407..da97ab535516c 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -128,6 +128,8 @@ export class HttpServer { for (const route of router.getRoutes()) { this.log.debug(`registering route handler for [${route.path}]`); const { authRequired = true, tags } = route.options; + // Hapi does not allow payload validation to be specified for 'head' or 'get' requests + const validate = ['head', 'get'].includes(route.method) ? undefined : { payload: true }; this.server.route({ handler: route.handler, method: route.method, @@ -135,6 +137,11 @@ export class HttpServer { options: { auth: authRequired ? undefined : false, tags: tags ? Array.from(tags) : undefined, + // TODO: This 'validate' section can be removed once the legacy platform is completely removed. + // We are telling Hapi that NP routes can accept any payload, so that it can bypass the default + // validation applied in ./http_tools#getServerOptions + // (All NP routes are already required to specify their own validation in order to access the payload) + validate, }, }); } diff --git a/src/core/server/http/http_tools.ts b/src/core/server/http/http_tools.ts index 88164a76c66f0..22468a5b252f4 100644 --- a/src/core/server/http/http_tools.ts +++ b/src/core/server/http/http_tools.ts @@ -23,6 +23,7 @@ import Hoek from 'hoek'; import { ServerOptions as TLSOptions } from 'https'; import { ValidationError } from 'joi'; import { HttpConfig } from './http_config'; +import { validateObject } from './prototype_pollution'; /** * Converts Kibana `HttpConfig` into `ServerOptions` that are accepted by the Hapi server. @@ -45,6 +46,11 @@ export function getServerOptions(config: HttpConfig, { configureTLS = true } = { options: { abortEarly: false, }, + // TODO: This payload validation can be removed once the legacy platform is completely removed. + // This is a default payload validation which applies to all LP routes which do not specify their own + // `validate.payload` handler, in order to reduce the likelyhood of prototype pollution vulnerabilities. + // (All NP routes are already required to specify their own validation in order to access the payload) + payload: value => Promise.resolve(validateObject(value)), }, }, state: { diff --git a/src/core/server/http/prototype_pollution/__snapshots__/validate_object.test.ts.snap b/src/core/server/http/prototype_pollution/__snapshots__/validate_object.test.ts.snap new file mode 100644 index 0000000000000..937e040c771ee --- /dev/null +++ b/src/core/server/http/prototype_pollution/__snapshots__/validate_object.test.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`can't submit {"__proto__":null} 1`] = `"'__proto__' is an invalid key"`; + +exports[`can't submit {"constructor":{"prototype":null}} 1`] = `"'constructor.prototype' is an invalid key"`; + +exports[`can't submit {"foo":{"__proto__":true}} 1`] = `"'__proto__' is an invalid key"`; + +exports[`can't submit {"foo":{"bar":{"__proto__":{}}}} 1`] = `"'__proto__' is an invalid key"`; + +exports[`can't submit {"foo":{"bar":{"constructor":{"prototype":null}}}} 1`] = `"'constructor.prototype' is an invalid key"`; + +exports[`can't submit {"foo":{"constructor":{"prototype":null}}} 1`] = `"'constructor.prototype' is an invalid key"`; diff --git a/src/core/server/http/prototype_pollution/index.ts b/src/core/server/http/prototype_pollution/index.ts new file mode 100644 index 0000000000000..e1a33ffba155e --- /dev/null +++ b/src/core/server/http/prototype_pollution/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { validateObject } from './validate_object'; diff --git a/src/core/server/http/prototype_pollution/validate_object.test.ts b/src/core/server/http/prototype_pollution/validate_object.test.ts new file mode 100644 index 0000000000000..9e23d6cec6444 --- /dev/null +++ b/src/core/server/http/prototype_pollution/validate_object.test.ts @@ -0,0 +1,79 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { validateObject } from './validate_object'; + +test(`fails on circular references`, () => { + const foo: Record = {}; + foo.myself = foo; + + expect(() => + validateObject({ + payload: foo, + }) + ).toThrowErrorMatchingInlineSnapshot(`"circular reference detected"`); +}); + +[ + { + foo: true, + bar: '__proto__', + baz: 1.1, + qux: undefined, + quux: () => null, + quuz: Object.create(null), + }, + { + foo: { + foo: true, + bar: '__proto__', + baz: 1.1, + qux: undefined, + quux: () => null, + quuz: Object.create(null), + }, + }, + { constructor: { foo: { prototype: null } } }, + { prototype: { foo: { constructor: null } } }, +].forEach(value => { + ['headers', 'payload', 'query', 'params'].forEach(property => { + const obj = { + [property]: value, + }; + test(`can submit ${JSON.stringify(obj)}`, () => { + expect(() => validateObject(obj)).not.toThrowError(); + }); + }); +}); + +// if we use the object literal syntax to create the following values, we end up +// actually reassigning the __proto__ which makes it be a non-enumerable not-own property +// which isn't what we want to test here +[ + JSON.parse(`{ "__proto__": null }`), + JSON.parse(`{ "foo": { "__proto__": true } }`), + JSON.parse(`{ "foo": { "bar": { "__proto__": {} } } }`), + JSON.parse(`{ "constructor": { "prototype" : null } }`), + JSON.parse(`{ "foo": { "constructor": { "prototype" : null } } }`), + JSON.parse(`{ "foo": { "bar": { "constructor": { "prototype" : null } } } }`), +].forEach(value => { + test(`can't submit ${JSON.stringify(value)}`, () => { + expect(() => validateObject(value)).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/src/core/server/http/prototype_pollution/validate_object.ts b/src/core/server/http/prototype_pollution/validate_object.ts new file mode 100644 index 0000000000000..cab6ce295ce92 --- /dev/null +++ b/src/core/server/http/prototype_pollution/validate_object.ts @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +interface StackItem { + value: any; + previousKey: string | null; +} + +// we have to do Object.prototype.hasOwnProperty because when you create an object using +// Object.create(null), and I assume other methods, you get an object without a prototype, +// so you can't use current.hasOwnProperty +const hasOwnProperty = (obj: any, property: string) => + Object.prototype.hasOwnProperty.call(obj, property); + +const isObject = (obj: any) => typeof obj === 'object' && obj !== null; + +// we're using a stack instead of recursion so we aren't limited by the call stack +export function validateObject(obj: any) { + if (!isObject(obj)) { + return; + } + + const stack: StackItem[] = [ + { + value: obj, + previousKey: null, + }, + ]; + const seen = new WeakSet([obj]); + + while (stack.length > 0) { + const { value, previousKey } = stack.pop()!; + + if (!isObject(value)) { + continue; + } + + if (hasOwnProperty(value, '__proto__')) { + throw new Error(`'__proto__' is an invalid key`); + } + + if (hasOwnProperty(value, 'prototype') && previousKey === 'constructor') { + throw new Error(`'constructor.prototype' is an invalid key`); + } + + // iterating backwards through an array is reportedly more performant + const entries = Object.entries(value); + for (let i = entries.length - 1; i >= 0; --i) { + const [key, childValue] = entries[i]; + if (isObject(childValue)) { + if (seen.has(childValue)) { + throw new Error('circular reference detected'); + } + + seen.add(childValue); + } + + stack.push({ + value: childValue, + previousKey: key, + }); + } + } +} diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index 6f5cb02fd8cba..2c3dfedd1d181 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -52,7 +52,7 @@ export type RequestHandlerContextProvider< * * @example * To handle an incoming request in your plugin you should: - * - Create a `Router` instance. Router is already configured to use `plugin-id` to prefix path segment for your routes. + * - Create a `Router` instance. * ```ts * const router = httpSetup.createRouter(); * ``` @@ -87,7 +87,7 @@ export type RequestHandlerContextProvider< * } * ``` * - * - Register route handler for GET request to 'my-app/path/{id}' path + * - Register route handler for GET request to 'path/{id}' path * ```ts * import { schema, TypeOf } from '@kbn/config-schema'; * const router = httpSetup.createRouter(); @@ -184,7 +184,7 @@ export interface HttpServiceSetup { * @example * ```ts * const router = createRouter(); - * // handler is called when '${my-plugin-id}/path' resource is requested with `GET` method + * // handler is called when '/path' resource is requested with `GET` method * router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); * ``` * @public diff --git a/src/dev/i18n/extractors/code.js b/src/dev/i18n/extractors/code.js index fa0d834824e97..6439f8ceff332 100644 --- a/src/dev/i18n/extractors/code.js +++ b/src/dev/i18n/extractors/code.js @@ -67,7 +67,16 @@ export function* extractCodeMessages(buffer, reporter) { try { ast = parse(buffer.toString(), { sourceType: 'module', - plugins: ['jsx', 'typescript', 'objectRestSpread', 'classProperties', 'asyncGenerators', 'dynamicImport'], + plugins: [ + 'jsx', + 'typescript', + 'objectRestSpread', + 'classProperties', + 'asyncGenerators', + 'dynamicImport', + 'nullishCoalescingOperator', + 'optionalChaining', + ], }); } catch (error) { if (error instanceof SyntaxError) { diff --git a/src/es_archiver/lib/indices/delete_index.js b/src/es_archiver/lib/indices/delete_index.js index 44a83be741063..b732989f02cb6 100644 --- a/src/es_archiver/lib/indices/delete_index.js +++ b/src/es_archiver/lib/indices/delete_index.js @@ -32,7 +32,7 @@ export async function deleteIndex(options) { stats, index, log, - retryIfSnapshottingCount = 3 + retryIfSnapshottingCount = 10 } = options; const getIndicesToDelete = async () => { diff --git a/src/fixtures/logstash_fields.js b/src/fixtures/logstash_fields.js index ab96b69851b71..f054c4d53fd8d 100644 --- a/src/fixtures/logstash_fields.js +++ b/src/fixtures/logstash_fields.js @@ -17,9 +17,10 @@ * under the License. */ -import { castEsToKbnFieldTypeName } from '../plugins/data/common'; -// eslint-disable-next-line max-len -import { shouldReadFieldFromDocValues } from '../plugins/data/server'; +import { + shouldReadFieldFromDocValues, + castEsToKbnFieldTypeName, +} from '../plugins/data/server'; function stubbedLogstashFields() { return [ diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx index 87558a73087d8..03d5b3f1d8f44 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx @@ -62,10 +62,6 @@ describe('Legacy (Ace) Console Editor Component Smoke Test', () => { updateCurrentState: () => {}, }, }, - // eslint-disable-next-line - ResizeChecker: function() { - return { on: () => {} }; - }, docLinkVersion: 'NA', }; editor = mount( diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx index b2a38a996f6a2..10f1ef34602a6 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -63,7 +63,6 @@ const DEFAULT_INPUT_VALUE = `GET _search function _Editor({ previousStateLocation = 'stored' }: EditorProps) { const { services: { history, notifications }, - ResizeChecker, docLinkVersion, } = useAppContext(); @@ -130,7 +129,6 @@ function _Editor({ previousStateLocation = 'stored' }: EditorProps) { mappings.retrieveAutoCompleteInfo(); const unsubscribeResizer = subscribeResizeChecker( - ResizeChecker, editorRef.current!, editorInstanceRef.current ); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx index fcf9f17e3ebd7..d38e86df41464 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx @@ -31,7 +31,6 @@ function _EditorOuput() { const editorInstanceRef = useRef(null); const { services: { settings }, - ResizeChecker, } = useAppContext(); const dispatch = useEditorActionContext(); @@ -42,11 +41,7 @@ function _EditorOuput() { const editor$ = $(editorRef.current!); editorInstanceRef.current = initializeOutput(editor$, settings); editorInstanceRef.current.update(''); - const unsubscribe = subscribeResizeChecker( - ResizeChecker, - editorRef.current!, - editorInstanceRef.current - ); + const unsubscribe = subscribeResizeChecker(editorRef.current!, editorInstanceRef.current); dispatch({ type: 'setOutputEditor', value: editorInstanceRef.current }); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx index 881b59e6b3a1c..fdfe9ecc7b94c 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx @@ -45,7 +45,6 @@ const CHILD_ELEMENT_PREFIX = 'historyReq'; export function ConsoleHistory({ close }: Props) { const { services: { history }, - ResizeChecker, } = useAppContext(); const dispatch = useEditorActionContext(); @@ -200,7 +199,6 @@ export function ConsoleHistory({ close }: Props) { diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx index d531e143a79d0..c15bec0563049 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx @@ -31,10 +31,9 @@ import { applyCurrentSettings } from '../console_editor/apply_editor_settings'; interface Props { settings: DevToolsSettings; req: any | null; - ResizeChecker: any; } -export function HistoryViewer({ settings, ResizeChecker, req }: Props) { +export function HistoryViewer({ settings, req }: Props) { const divRef = useRef(null); const viewerRef = useRef(null); @@ -43,7 +42,7 @@ export function HistoryViewer({ settings, ResizeChecker, req }: Props) { viewerRef.current = viewer; viewer.renderer.setShowPrintMargin(false); viewer.$blockScrolling = Infinity; - const unsubscribe = subscribeResizeChecker(ResizeChecker, divRef.current!, viewer); + const unsubscribe = subscribeResizeChecker(divRef.current!, viewer); return () => unsubscribe(); }, []); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts index c83c593ef404d..4ecd5d415833c 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ +import { ResizeChecker } from '../../../../../../../../../plugins/kibana_utils/public'; -export function subscribeResizeChecker(ResizeChecker: any, $el: any, ...editors: any[]) { - const checker = new ResizeChecker($el); +export function subscribeResizeChecker(el: HTMLElement, ...editors: any[]) { + const checker = new ResizeChecker(el); checker.on('resize', () => editors.forEach(e => { e.resize(); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx b/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx index 7bbdf731407e3..be7aa87ac2894 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx @@ -29,7 +29,6 @@ interface ContextValue { notifications: NotificationsSetup; }; docLinkVersion: string; - ResizeChecker: any; } interface ContextProps { diff --git a/src/legacy/core_plugins/console/np_ready/public/application/index.tsx b/src/legacy/core_plugins/console/np_ready/public/application/index.tsx index d8933e60470c2..aaacfd3894d18 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/index.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/index.tsx @@ -32,10 +32,9 @@ export function legacyBackDoorToSettings() { export function boot(deps: { docLinkVersion: string; I18nContext: any; - ResizeChecker: any; notifications: NotificationsSetup; }) { - const { I18nContext, ResizeChecker, notifications, docLinkVersion } = deps; + const { I18nContext, notifications, docLinkVersion } = deps; const storage = createStorage({ engine: window.localStorage, @@ -51,7 +50,6 @@ export function boot(deps: { value={{ docLinkVersion, services: { storage, history, settings, notifications }, - ResizeChecker, }} > diff --git a/src/legacy/core_plugins/console/np_ready/public/legacy.ts b/src/legacy/core_plugins/console/np_ready/public/legacy.ts index 8c60ff23648be..463aac74da944 100644 --- a/src/legacy/core_plugins/console/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/console/np_ready/public/legacy.ts @@ -26,7 +26,6 @@ import 'brace/mode/text'; /* eslint-disable @kbn/eslint/no-restricted-paths */ import { npSetup, npStart } from 'ui/new_platform'; import { I18nContext } from 'ui/i18n'; -import { ResizeChecker } from 'ui/resize_checker'; /* eslint-enable @kbn/eslint/no-restricted-paths */ export interface XPluginSet { @@ -34,7 +33,6 @@ export interface XPluginSet { feature_catalogue: FeatureCatalogueSetup; __LEGACY: { I18nContext: any; - ResizeChecker: any; }; } @@ -48,7 +46,6 @@ pluginInstance.setup(npSetup.core, { ...npSetup.plugins, __LEGACY: { I18nContext, - ResizeChecker, }, }); pluginInstance.start(npStart.core); diff --git a/src/legacy/core_plugins/console/np_ready/public/plugin.ts b/src/legacy/core_plugins/console/np_ready/public/plugin.ts index f02b0b5e72999..301b85b6e7395 100644 --- a/src/legacy/core_plugins/console/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/console/np_ready/public/plugin.ts @@ -30,7 +30,7 @@ export class ConsoleUIPlugin implements Plugin { async setup({ notifications }: CoreSetup, pluginSet: XPluginSet) { const { - __LEGACY: { I18nContext, ResizeChecker }, + __LEGACY: { I18nContext }, devTools, feature_catalogue, } = pluginSet; @@ -62,7 +62,6 @@ export class ConsoleUIPlugin implements Plugin { boot({ docLinkVersion: ctx.core.docLinks.DOC_LINK_VERSION, I18nContext, - ResizeChecker, notifications, }), element diff --git a/src/legacy/core_plugins/console/server/proxy_route.js b/src/legacy/core_plugins/console/server/proxy_route.js index 8ce828879a677..856128f3d4c03 100644 --- a/src/legacy/core_plugins/console/server/proxy_route.js +++ b/src/legacy/core_plugins/console/server/proxy_route.js @@ -71,6 +71,7 @@ export const createProxyRoute = ({ parse: false, }, validate: { + payload: true, query: Joi.object() .keys({ method: Joi.string() diff --git a/src/legacy/core_plugins/console/server/request.test.ts b/src/legacy/core_plugins/console/server/request.test.ts index 463649a090295..d5504c0f3a3c2 100644 --- a/src/legacy/core_plugins/console/server/request.test.ts +++ b/src/legacy/core_plugins/console/server/request.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import http from 'http'; +import http, { ClientRequest } from 'http'; import * as sinon from 'sinon'; import { sendRequest } from './request'; import { URL } from 'url'; @@ -24,7 +24,7 @@ import { fail } from 'assert'; describe(`Console's send request`, () => { let sandbox: sinon.SinonSandbox; - let stub: sinon.SinonStub; + let stub: sinon.SinonStub, ClientRequest>; let fakeRequest: http.ClientRequest; beforeEach(() => { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx index 333e1e328651d..5b389f5b98aba 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx @@ -22,13 +22,12 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { useState } from 'react'; import { CoreStart } from 'src/core/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; import { IndexPattern } from '../../index_patterns'; import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; import { FilterOptions } from './filter_options'; import { useKibana, KibanaContextProvider } from '../../../../../../plugins/kibana_react/public'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { DataPublicPluginStart, esFilters } from '../../../../../../plugins/data/public'; interface Props { filters: esFilters.Filter[]; diff --git a/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts b/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts index dc5023795bf19..6084b4c106452 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts @@ -22,40 +22,24 @@ import { fieldFormats } from 'ui/registry/field_formats'; import { i18n } from '@kbn/i18n'; // @ts-ignore import { ObjDefine } from './obj_define'; -import { FieldFormat } from '../../../../../../plugins/data/common/field_formats'; // @ts-ignore import { shortenDottedString } from '../../../../../core_plugins/kibana/common/utils/shorten_dotted_string'; import { IndexPattern } from '../index_patterns'; import { getNotifications } from '../services'; -import { getKbnFieldType } from '../../../../../../plugins/data/public'; - -interface FieldSubType { - multi?: { parent: string }; - nested?: { path: string }; -} +import { + FieldFormat, + getKbnFieldType, + IFieldType, + IFieldSubType, +} from '../../../../../../plugins/data/public'; export type FieldSpec = Record; -export interface FieldType { - name: string; - type: string; - script?: string; - lang?: string; - count?: number; - // esTypes might be undefined on old index patterns that have not been refreshed since we added - // this prop. It is also undefined on scripted fields. - esTypes?: string[]; - aggregatable?: boolean; - filterable?: boolean; - searchable?: boolean; - sortable?: boolean; - visualizable?: boolean; - readFromDocValues?: boolean; - scripted?: boolean; - subType?: FieldSubType; - displayName?: string; - format?: any; -} + +/** @deprecated + * Please use IFieldType instead + * */ +export type FieldType = IFieldType; export class Field implements FieldType { name: string; @@ -72,7 +56,7 @@ export class Field implements FieldType { sortable?: boolean; visualizable?: boolean; scripted?: boolean; - subType?: FieldSubType; + subType?: IFieldSubType; displayName?: string; format: any; routes: Record = { diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts index bf0d79e960d9b..12aa3c2fb0d51 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts @@ -37,21 +37,18 @@ import { createFieldsFetcher } from './_fields_fetcher'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; import { IIndexPatternsApiClient } from './index_patterns_api_client'; -import { ES_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { ES_FIELD_TYPES, IIndexPattern } from '../../../../../../plugins/data/public'; import { getNotifications } from '../services'; const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const type = 'index-pattern'; -export interface StaticIndexPattern { - fields: FieldType[]; - title: string; - id?: string; - type?: string; - timeFieldName?: string; -} +/** @deprecated + * Please use IIndexPattern instead + * */ +export type StaticIndexPattern = IIndexPattern; -export class IndexPattern implements StaticIndexPattern { +export class IndexPattern implements IIndexPattern { [key: string]: any; public id?: string; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx index ca0ac3c371849..cd64b1ecf2549 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx @@ -82,8 +82,11 @@ function QueryBarTopRowUI(props: Props) { const queryLanguage = props.query && props.query.language; const persistedLog: PersistedLog | undefined = React.useMemo( - () => (queryLanguage ? getQueryLog(uiSettings!, storage, appName, queryLanguage) : undefined), - [queryLanguage] + () => + queryLanguage && uiSettings && storage && appName + ? getQueryLog(uiSettings!, storage, appName, queryLanguage) + : undefined, + [appName, queryLanguage, uiSettings, storage] ); function onClickSubmitButton(event: React.MouseEvent) { diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx index 4485b74ca0901..125c6b8dad006 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx @@ -20,12 +20,11 @@ import React, { useState, useEffect } from 'react'; import { Subscription } from 'rxjs'; import { CoreStart } from 'src/core/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { SearchBar } from '../../../'; import { SearchBarOwnProps } from '.'; -import { esFilters } from '../../../../../../../plugins/data/public'; +import { DataPublicPluginStart, esFilters } from '../../../../../../../plugins/data/public'; interface StatefulSearchBarDeps { core: CoreStart; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index ea0f6775e4831..e97c06ace1579 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -24,7 +24,6 @@ import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; import { get, isEqual } from 'lodash'; -import { TimeRange, Query, TimeHistoryContract } from 'src/plugins/data/public'; import { IndexPattern, FilterBar } from '../../../../../data/public'; import { QueryBarTopRow } from '../../../query'; import { SavedQuery, SavedQueryAttributes } from '../index'; @@ -37,7 +36,12 @@ import { KibanaReactContextValue, } from '../../../../../../../plugins/kibana_react/public'; import { IDataPluginServices } from '../../../types'; -import { esFilters } from '../../../../../../../plugins/data/public'; +import { + TimeRange, + Query, + esFilters, + TimeHistoryContract, +} from '../../../../../../../plugins/data/public'; interface SearchBarInjectedDeps { kibana: KibanaReactContextValue; @@ -72,6 +76,7 @@ export interface SearchBarOwnProps { // Show when user has privileges to save showSaveQuery?: boolean; savedQuery?: SavedQuery; + onQueryChange?: (payload: { dateRange: TimeRange; query?: Query }) => void; onQuerySubmit?: (payload: { dateRange: TimeRange; query?: Query }) => void; // User has saved the current state as a saved query onSaved?: (savedQuery: SavedQuery) => void; @@ -206,6 +211,18 @@ class SearchBarUI extends Component { ); } + /* + * This Function is here to show the toggle in saved query form + * in case you the date range (from/to) + */ + private shouldRenderTimeFilterInSavedQueryForm() { + const { dateRangeFrom, dateRangeTo, showDatePicker } = this.props; + return ( + showDatePicker || + (!showDatePicker && dateRangeFrom !== undefined && dateRangeTo !== undefined) + ); + } + public setFilterBarHeight = () => { requestAnimationFrame(() => { const height = @@ -299,6 +316,9 @@ class SearchBarUI extends Component { dateRangeFrom: queryAndDateRange.dateRange.from, dateRangeTo: queryAndDateRange.dateRange.to, }); + if (this.props.onQueryChange) { + this.props.onQueryChange(queryAndDateRange); + } }; public onQueryBarSubmit = (queryAndDateRange: { dateRange?: TimeRange; query?: Query }) => { @@ -440,7 +460,7 @@ class SearchBarUI extends Component { onSave={this.onSave} onClose={() => this.setState({ showSaveQueryModal: false })} showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.props.showDatePicker} + showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} /> ) : null} {this.state.showSaveNewQueryModal ? ( @@ -449,7 +469,7 @@ class SearchBarUI extends Component { onSave={savedQueryMeta => this.onSave(savedQueryMeta, true)} onClose={() => this.setState({ showSaveNewQueryModal: false })} showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.props.showDatePicker} + showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} /> ) : null} diff --git a/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts b/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts index 9d7b4fb6d0480..c8870b9f97957 100644 --- a/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts +++ b/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts @@ -17,9 +17,8 @@ * under the License. */ -import { TimeRange } from '../../../../../../plugins/data/public'; import { Adapters } from '../../../../../../plugins/inspector/public'; -import { Query } from '../../../../../../plugins/data/public'; +import { TimeRange, Query } from '../../../../../../plugins/data/public'; export { TimeRange, Adapters, Query }; export * from '../../../../../../plugins/expressions/public'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 656b54040ad99..d5da4ba51e55b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -32,7 +32,6 @@ import { } from 'ui/state_management/app_state'; import { KbnUrl } from 'ui/url/kbn_url'; -import { TimeRange, Query } from 'src/plugins/data/public'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; import { StaticIndexPattern, SavedQuery } from 'plugins/data'; @@ -42,7 +41,7 @@ import { Subscription } from 'rxjs'; import { ViewMode } from '../../../embeddable_api/public/np_ready/public'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; import { DashboardAppState, SavedDashboardPanel, ConfirmModalFn } from './types'; -import { esFilters } from '../../../../../../src/plugins/data/public'; +import { TimeRange, Query, esFilters } from '../../../../../../src/plugins/data/public'; import { DashboardAppController } from './dashboard_app_controller'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index 1a42ed837a9de..d5af4c93d0e0c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -27,9 +27,8 @@ import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; import { Moment } from 'moment'; import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; -import { Query } from 'src/plugins/data/public'; import { ViewMode } from '../../../../../../src/plugins/embeddable/public'; -import { esFilters } from '../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../src/plugins/data/public'; import { getAppStateDefaults, migrateAppState } from './lib'; import { convertPanelStateToSavedDashboardPanel } from './lib/embeddable_saved_object_converters'; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/_doc_table.scss b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/_doc_table.scss index c0bcc3ab8a614..8b754d23f9604 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/_doc_table.scss +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/_doc_table.scss @@ -62,6 +62,15 @@ doc-table { margin: $euiSizeXS $euiSizeXS 0; } +.kbnDocTable__bar--footer { + position: relative; + margin: -($euiSize * 3) $euiSizeXS 0; +} + +.kbnDocTable__padBottom { + padding-bottom: $euiSizeXL; +} + .kbnDocTable__error { display: flex; flex-direction: column; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.html b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.html index b6ac1d3fd8b4a..61bb5cbf39cbe 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.html +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.html @@ -1,5 +1,4 @@
@@ -26,32 +25,34 @@ >
- - - - +
+ - -
+ > + + + + + -
+