diff --git a/.ci/Jenkinsfile_flaky b/.ci/Jenkinsfile_flaky index e1cbac0528b1f99..8ad02b7162b6ab1 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 16a80f01278a54a..daf49d9d08281da 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/CONTRIBUTING.md b/CONTRIBUTING.md index 2903f23f55c9e04..64a1dd0526d5855 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -325,7 +325,7 @@ Note that for VSCode, to enable "live" linting of TypeScript (and other) file ty "eslint.autoFixOnSave": true, ``` -It is **not** recommended to use `prettier` plugin on Kibana project. Because settings are in `eslintrc.js` file and it is applied to too many files that shouldn't be prettier-ized. +:warning: It is **not** recommended to use the [`Prettier` extension/IDE plugin](https://prettier.io/) while maintaining the Kibana project. Formatting and styling roles are set in the multiple `.eslintrc.js` files across the project and some of them use the [NPM version of Prettier](https://www.npmjs.com/package/prettier). Using the IDE extension might cause conflicts, applying the formatting to too many files that shouldn't be prettier-ized and/or highlighting errors that are actually OK. ### Internationalization diff --git a/Jenkinsfile b/Jenkinsfile index 8d8579736f63922..c002832d4d51a32 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/docs/canvas/canvas-share-workpad.asciidoc b/docs/canvas/canvas-share-workpad.asciidoc index 119ede16fe1e7b0..412019efc7f3535 100644 --- a/docs/canvas/canvas-share-workpad.asciidoc +++ b/docs/canvas/canvas-share-workpad.asciidoc @@ -55,7 +55,7 @@ image::images/canvas-create-URL.gif[Create POST URL] [[add-workpad-website]] === Share the workpad on a website -beta[] Download the workpad and share it on any website, then customize the workpad behavior to autoplay the pages or hide the toolbar. +beta[] Canvas allows you to create _shareables_, which are workpads that you download and securely share on any website. To customize the behavior of the workpad on your website, you can choose to autoplay the pages or hide the workpad toolbar. . If you are using a Gold or Platinum license, enable reporting in your `config/kibana.yml` file. @@ -74,7 +74,7 @@ NOTE: Shareable workpads encode the current state of the workpad in a JSON file. [float] [[change-the-workpad-settings]] -=== Change the shareable workpad settings +=== Change the settings After you've added the workpad to your website, you can change the autoplay and toolbar settings. 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 6431589c55bd122..71a7fd8fb6a2283 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 d943ad53af84371..dba0ad8c8560cb7 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/package.json b/package.json index 3abd69a6166829a..b0a74a4a5e04fd7 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/kbn-babel-preset/common_preset.js b/packages/kbn-babel-preset/common_preset.js index d2bad41e8de860f..d1b7bc20dd9f94c 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 c22cf175b29e599..1913301e21a764c 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-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 d9f559987f58b21..000000000000000 --- 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 676992e4dddc865..000000000000000 --- 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 4361659021bd5ad..000000000000000 --- 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 9510a18441e53cc..000000000000000 --- 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/index.d.ts b/packages/kbn-es-query/src/index.d.ts index c06cef6367fe78c..79e6903b1864488 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 963999bd0999b2d..79e6903b1864488 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 06f4940e8ed3b5a..ef3d0ee82887460 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/renovate.json5 b/renovate.json5 index 0c288bb85c72c8c..aefbc61e8dc12ff 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 61da9af7baa7c80..131e1dd50179287 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 8d3cc733cf250c7..c18a5b2000e011f 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/elasticsearch/__snapshots__/elasticsearch_config.test.ts.snap b/src/core/server/elasticsearch/__snapshots__/elasticsearch_config.test.ts.snap new file mode 100644 index 000000000000000..e81336c8863f562 --- /dev/null +++ b/src/core/server/elasticsearch/__snapshots__/elasticsearch_config.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#username throws if equal to "elastic", only while running from source 1`] = `"[username]: value of \\"elastic\\" is forbidden. This is a superuser account that can obfuscate privilege-related issues. You should use the \\"kibana\\" user instead."`; diff --git a/src/core/server/elasticsearch/elasticsearch_config.test.ts b/src/core/server/elasticsearch/elasticsearch_config.test.ts index 383ba77f177799f..5a52e1ea51ccc99 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.test.ts @@ -107,3 +107,11 @@ test('#ssl.certificateAuthorities accepts both string and array of strings', () ); expect(configValue.ssl.certificateAuthorities).toEqual(['some-path', 'another-path']); }); + +test('#username throws if equal to "elastic", only while running from source', () => { + const obj = { + username: 'elastic', + }; + expect(() => config.schema.validate(obj, { dist: false })).toThrowErrorMatchingSnapshot(); + expect(() => config.schema.validate(obj, { dist: true })).not.toThrow(); +}); diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts index 947a0d27546b120..23a1c69d055bce5 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.ts @@ -19,6 +19,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { Duration } from 'moment'; +import { Logger } from '../logging'; const hostURISchema = schema.uri({ scheme: ['http', 'https'] }); @@ -39,7 +40,23 @@ export const config = { defaultValue: 'http://localhost:9200', }), preserveHost: schema.boolean({ defaultValue: true }), - username: schema.maybe(schema.string()), + username: schema.maybe( + schema.conditional( + schema.contextRef('dist'), + false, + schema.string({ + validate: rawConfig => { + if (rawConfig === 'elastic') { + return ( + 'value of "elastic" is forbidden. This is a superuser account that can obfuscate ' + + 'privilege-related issues. You should use the "kibana" user instead.' + ); + } + }, + }), + schema.string() + ) + ), password: schema.maybe(schema.string()), requestHeadersWhitelist: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], { defaultValue: ['authorization'], @@ -166,7 +183,7 @@ export class ElasticsearchConfig { */ public readonly customHeaders: ElasticsearchConfigType['customHeaders']; - constructor(rawConfig: ElasticsearchConfigType) { + constructor(rawConfig: ElasticsearchConfigType, log?: Logger) { this.ignoreVersionMismatch = rawConfig.ignoreVersionMismatch; this.apiVersion = rawConfig.apiVersion; this.logQueries = rawConfig.logQueries; @@ -195,5 +212,14 @@ export class ElasticsearchConfig { ...rawConfig.ssl, certificateAuthorities, }; + + if (this.username === 'elastic' && log !== undefined) { + // logger is optional / not used during tests + // TODO: logger can be removed when issue #40255 is resolved to support deprecations in NP config service + log.warn( + `Setting the elasticsearch username to "elastic" is deprecated. You should use the "kibana" user instead.`, + { tags: ['deprecation'] } + ); + } } } diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index 1f062412edaf225..be0a817c54146c6 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -51,7 +51,7 @@ export class ElasticsearchService implements CoreService('elasticsearch') - .pipe(map(rawConfig => new ElasticsearchConfig(rawConfig))); + .pipe(map(rawConfig => new ElasticsearchConfig(rawConfig, coreContext.logger.get('config')))); } public async setup(deps: SetupDeps): Promise { diff --git a/src/core/server/http/base_path_proxy_server.ts b/src/core/server/http/base_path_proxy_server.ts index ff7fee0198f6818..cde35f3cbe995ac 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 3354324c12407db..da97ab535516c8b 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 88164a76c66f071..22468a5b252f424 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 000000000000000..937e040c771ee1a --- /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 000000000000000..e1a33ffba155e8c --- /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 000000000000000..9e23d6cec644457 --- /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 000000000000000..cab6ce295ce92a8 --- /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 6f5cb02fd8cbafc..2c3dfedd1d18169 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 fa0d834824e97c2..6439f8ceff332d0 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 44a83be7410631a..b732989f02cb609 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 ab96b69851b7136..f054c4d53fd8d3d 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 87558a73087d87e..03d5b3f1d8f4428 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 b2a38a996f6a214..10f1ef34602a63d 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 fcf9f17e3ebd75c..d38e86df414649f 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 881b59e6b3a1cb8..fdfe9ecc7b94c47 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 d531e143a79d015..c15bec0563049e7 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 c83c593ef404ddd..4ecd5d415833cc6 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 7bbdf731407e3dd..be7aa87ac28940d 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 d8933e60470c233..aaacfd3894d189f 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 8c60ff23648be32..463aac74da944e4 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 f02b0b5e7299923..301b85b6e7395d4 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 8ce828879a677ae..856128f3d4c0316 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/data/public/filter/filter_bar/filter_bar.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx index 333e1e328651d4a..5b389f5b98aba12 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 dc5023795bf19bd..6084b4c106452ca 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 bf0d79e960d9b8f..12aa3c2fb0d51a2 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/search/search_bar/components/create_search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx index 4485b74ca09010d..125c6b8dad006b9 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 d713139366eef43..e97c06ace157979 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; 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 9d7b4fb6d048065..c8870b9f97957ea 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 656b54040ad99a7..d5da4ba51e55b6e 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 1a42ed837a9de36..d5af4c93d0e0cc8 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 c0bcc3ab8a61476..8b754d23f960445 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 b6ac1d3fd8b4a20..61bb5cbf39cbea5 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 @@ >
- - - - +
+ - -
+ > + + + + + -
+