diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 8ad810717d86e..8e9b041d32d3e 100644 --- a/.ci/end2end.groovy +++ b/.ci/end2end.groovy @@ -13,7 +13,7 @@ pipeline { BASE_DIR = 'src/github.com/elastic/kibana' HOME = "${env.WORKSPACE}" APM_ITS = 'apm-integration-testing' - CYPRESS_DIR = 'x-pack/legacy/plugins/apm/e2e' + CYPRESS_DIR = 'x-pack/plugins/apm/e2e' PIPELINE_LOG_LEVEL = 'DEBUG' } options { @@ -39,7 +39,7 @@ pipeline { shallow: false, reference: "/var/lib/jenkins/.git-references/kibana.git") script { dir("${BASE_DIR}"){ - def regexps =[ "^x-pack/legacy/plugins/apm/.*" ] + def regexps =[ "^x-pack/plugins/apm/.*" ] env.APM_UPDATED = isGitRegionMatch(patterns: regexps) } } diff --git a/.ci/es-snapshots/Jenkinsfile_verify_es b/.ci/es-snapshots/Jenkinsfile_verify_es index ade79f27e10e9..c87ca01354315 100644 --- a/.ci/es-snapshots/Jenkinsfile_verify_es +++ b/.ci/es-snapshots/Jenkinsfile_verify_es @@ -19,42 +19,48 @@ currentBuild.description = "ES: ${SNAPSHOT_VERSION}
Kibana: ${params.branch def SNAPSHOT_MANIFEST = "https://storage.googleapis.com/kibana-ci-es-snapshots-daily/${SNAPSHOT_VERSION}/archives/${SNAPSHOT_ID}/manifest.json" -kibanaPipeline(timeoutMinutes: 120) { +kibanaPipeline(timeoutMinutes: 150) { catchErrors { - withEnv(["ES_SNAPSHOT_MANIFEST=${SNAPSHOT_MANIFEST}"]) { - parallel([ - 'kibana-intake-agent': workers.intake('kibana-intake', './test/scripts/jenkins_unit.sh'), - 'x-pack-intake-agent': workers.intake('x-pack-intake', './test/scripts/jenkins_xpack.sh'), - 'kibana-oss-agent': workers.functional('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ - 'oss-ciGroup1': kibanaPipeline.ossCiGroupProcess(1), - 'oss-ciGroup2': kibanaPipeline.ossCiGroupProcess(2), - 'oss-ciGroup3': kibanaPipeline.ossCiGroupProcess(3), - 'oss-ciGroup4': kibanaPipeline.ossCiGroupProcess(4), - 'oss-ciGroup5': kibanaPipeline.ossCiGroupProcess(5), - 'oss-ciGroup6': kibanaPipeline.ossCiGroupProcess(6), - 'oss-ciGroup7': kibanaPipeline.ossCiGroupProcess(7), - 'oss-ciGroup8': kibanaPipeline.ossCiGroupProcess(8), - 'oss-ciGroup9': kibanaPipeline.ossCiGroupProcess(9), - 'oss-ciGroup10': kibanaPipeline.ossCiGroupProcess(10), - 'oss-ciGroup11': kibanaPipeline.ossCiGroupProcess(11), - 'oss-ciGroup12': kibanaPipeline.ossCiGroupProcess(12), - ]), - 'kibana-xpack-agent': workers.functional('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ - 'xpack-ciGroup1': kibanaPipeline.xpackCiGroupProcess(1), - 'xpack-ciGroup2': kibanaPipeline.xpackCiGroupProcess(2), - 'xpack-ciGroup3': kibanaPipeline.xpackCiGroupProcess(3), - 'xpack-ciGroup4': kibanaPipeline.xpackCiGroupProcess(4), - 'xpack-ciGroup5': kibanaPipeline.xpackCiGroupProcess(5), - 'xpack-ciGroup6': kibanaPipeline.xpackCiGroupProcess(6), - 'xpack-ciGroup7': kibanaPipeline.xpackCiGroupProcess(7), - 'xpack-ciGroup8': kibanaPipeline.xpackCiGroupProcess(8), - 'xpack-ciGroup9': kibanaPipeline.xpackCiGroupProcess(9), - 'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10), - ]), - ]) - } + slackNotifications.onFailure( + title: ":broken_heart: *<${env.BUILD_URL}|[${SNAPSHOT_VERSION}] ES Snapshot Verification Failure>*", + message: ":broken_heart: [${SNAPSHOT_VERSION}] ES Snapshot Verification Failure", + ) { + retryable.enable(2) + withEnv(["ES_SNAPSHOT_MANIFEST=${SNAPSHOT_MANIFEST}"]) { + parallel([ + 'kibana-intake-agent': workers.intake('kibana-intake', './test/scripts/jenkins_unit.sh'), + 'x-pack-intake-agent': workers.intake('x-pack-intake', './test/scripts/jenkins_xpack.sh'), + 'kibana-oss-agent': workers.functional('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ + 'oss-ciGroup1': kibanaPipeline.ossCiGroupProcess(1), + 'oss-ciGroup2': kibanaPipeline.ossCiGroupProcess(2), + 'oss-ciGroup3': kibanaPipeline.ossCiGroupProcess(3), + 'oss-ciGroup4': kibanaPipeline.ossCiGroupProcess(4), + 'oss-ciGroup5': kibanaPipeline.ossCiGroupProcess(5), + 'oss-ciGroup6': kibanaPipeline.ossCiGroupProcess(6), + 'oss-ciGroup7': kibanaPipeline.ossCiGroupProcess(7), + 'oss-ciGroup8': kibanaPipeline.ossCiGroupProcess(8), + 'oss-ciGroup9': kibanaPipeline.ossCiGroupProcess(9), + 'oss-ciGroup10': kibanaPipeline.ossCiGroupProcess(10), + 'oss-ciGroup11': kibanaPipeline.ossCiGroupProcess(11), + 'oss-ciGroup12': kibanaPipeline.ossCiGroupProcess(12), + ]), + 'kibana-xpack-agent': workers.functional('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ + 'xpack-ciGroup1': kibanaPipeline.xpackCiGroupProcess(1), + 'xpack-ciGroup2': kibanaPipeline.xpackCiGroupProcess(2), + 'xpack-ciGroup3': kibanaPipeline.xpackCiGroupProcess(3), + 'xpack-ciGroup4': kibanaPipeline.xpackCiGroupProcess(4), + 'xpack-ciGroup5': kibanaPipeline.xpackCiGroupProcess(5), + 'xpack-ciGroup6': kibanaPipeline.xpackCiGroupProcess(6), + 'xpack-ciGroup7': kibanaPipeline.xpackCiGroupProcess(7), + 'xpack-ciGroup8': kibanaPipeline.xpackCiGroupProcess(8), + 'xpack-ciGroup9': kibanaPipeline.xpackCiGroupProcess(9), + 'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10), + ]), + ]) + } - promoteSnapshot(SNAPSHOT_VERSION, SNAPSHOT_ID) + promoteSnapshot(SNAPSHOT_VERSION, SNAPSHOT_ID) + } } kibanaPipeline.sendMail() diff --git a/.eslintignore b/.eslintignore index 4913192e81c1d..362b3e42d48e5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -25,11 +25,11 @@ target /src/plugins/vis_type_timelion/public/_generated_/** /src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.* /x-pack/legacy/plugins/**/__tests__/fixtures/** -/x-pack/legacy/plugins/apm/e2e/cypress/**/snapshots.js -/x-pack/legacy/plugins/canvas/canvas_plugin -/x-pack/legacy/plugins/canvas/canvas_plugin_src/lib/flot-charts -/x-pack/legacy/plugins/canvas/shareable_runtime/build -/x-pack/legacy/plugins/canvas/storybook +/x-pack/plugins/apm/e2e/cypress/**/snapshots.js +/x-pack/plugins/canvas/canvas_plugin +/x-pack/plugins/canvas/canvas_plugin_src/lib/flot-charts +/x-pack/plugins/canvas/shareable_runtime/build +/x-pack/plugins/canvas/storybook /x-pack/plugins/monitoring/public/lib/jquery_flot /x-pack/legacy/plugins/infra/common/graphql/types.ts /x-pack/legacy/plugins/infra/public/graphql/types.ts diff --git a/.eslintrc.js b/.eslintrc.js index 8b33ec83347a8..dde0ce010d4d4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -89,7 +89,7 @@ module.exports = { }, }, { - files: ['x-pack/legacy/plugins/canvas/**/*.{js,ts,tsx}'], + files: ['x-pack/plugins/canvas/**/*.{js,ts,tsx}'], rules: { 'react-hooks/exhaustive-deps': 'off', 'jsx-a11y/click-events-have-key-events': 'off', @@ -151,6 +151,16 @@ module.exports = { }, }, + /** + * New Platform client-side + */ + { + files: ['{src,x-pack}/plugins/*/public/**/*.{js,ts,tsx}'], + rules: { + 'import/no-commonjs': 'error', + }, + }, + /** * Files that require Elastic license headers instead of Apache 2.0 header */ @@ -183,6 +193,11 @@ module.exports = { { basePath: __dirname, zones: [ + { + target: ['(src|x-pack)/**/*', '!src/core/**/*'], + from: ['src/core/utils/**/*'], + errorMessage: `Plugins may only import from src/core/server and src/core/public.`, + }, { target: [ '(src|x-pack)/legacy/**/*', @@ -306,7 +321,7 @@ module.exports = { { files: [ 'x-pack/test/functional/apps/**/*.js', - 'x-pack/legacy/plugins/apm/**/*.js', + 'x-pack/plugins/apm/**/*.js', 'test/*/config.ts', 'test/*/config_open.ts', 'test/*/{tests,test_suites,apis,apps}/**/*', @@ -393,7 +408,7 @@ module.exports = { 'x-pack/**/*.test.js', 'x-pack/test_utils/**/*', 'x-pack/gulpfile.js', - 'x-pack/legacy/plugins/apm/public/utils/testHelpers.js', + 'x-pack/plugins/apm/public/utils/testHelpers.js', ], rules: { 'import/no-extraneous-dependencies': [ @@ -519,7 +534,7 @@ module.exports = { * APM overrides */ { - files: ['x-pack/legacy/plugins/apm/**/*.js'], + files: ['x-pack/plugins/apm/**/*.js'], rules: { 'no-unused-vars': ['error', { ignoreRestSiblings: true }], 'no-console': ['warn', { allow: ['error'] }], @@ -527,7 +542,7 @@ module.exports = { }, { plugins: ['react-hooks'], - files: ['x-pack/legacy/plugins/apm/**/*.{ts,tsx}'], + files: ['x-pack/plugins/apm/**/*.{ts,tsx}'], rules: { 'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks 'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useFetcher$' }], @@ -878,7 +893,7 @@ module.exports = { * Canvas overrides */ { - files: ['x-pack/legacy/plugins/canvas/**/*.js'], + files: ['x-pack/plugins/canvas/**/*.js'], rules: { radix: 'error', @@ -922,12 +937,12 @@ module.exports = { }, { files: [ - 'x-pack/legacy/plugins/canvas/gulpfile.js', - 'x-pack/legacy/plugins/canvas/scripts/*.js', - 'x-pack/legacy/plugins/canvas/tasks/*.js', - 'x-pack/legacy/plugins/canvas/tasks/**/*.js', - 'x-pack/legacy/plugins/canvas/__tests__/**/*.js', - 'x-pack/legacy/plugins/canvas/**/{__tests__,__test__,__jest__,__fixtures__,__mocks__}/**/*.js', + 'x-pack/plugins/canvas/gulpfile.js', + 'x-pack/plugins/canvas/scripts/*.js', + 'x-pack/plugins/canvas/tasks/*.js', + 'x-pack/plugins/canvas/tasks/**/*.js', + 'x-pack/plugins/canvas/__tests__/**/*.js', + 'x-pack/plugins/canvas/**/{__tests__,__test__,__jest__,__fixtures__,__mocks__}/**/*.js', ], rules: { 'import/no-extraneous-dependencies': [ @@ -940,7 +955,7 @@ module.exports = { }, }, { - files: ['x-pack/legacy/plugins/canvas/canvas_plugin_src/**/*.js'], + files: ['x-pack/plugins/canvas/canvas_plugin_src/**/*.js'], globals: { canvas: true, $: true }, rules: { 'import/no-unresolved': [ @@ -952,13 +967,13 @@ module.exports = { }, }, { - files: ['x-pack/legacy/plugins/canvas/public/**/*.js'], + files: ['x-pack/plugins/canvas/public/**/*.js'], env: { browser: true, }, }, { - files: ['x-pack/legacy/plugins/canvas/canvas_plugin_src/lib/flot-charts/**/*.js'], + files: ['x-pack/plugins/canvas/canvas_plugin_src/lib/flot-charts/**/*.js'], env: { jquery: true, }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b4692a4ddb3b7..638e86ef375fe 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,12 +3,11 @@ # For more info, see https://help.github.com/articles/about-codeowners/ # App +/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-app /x-pack/plugins/lens/ @elastic/kibana-app /x-pack/plugins/graph/ @elastic/kibana-app /src/legacy/server/url_shortening/ @elastic/kibana-app /src/legacy/server/sample_data/ @elastic/kibana-app -/src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app -/src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app /src/plugins/dashboard/ @elastic/kibana-app @@ -67,7 +66,7 @@ /x-pack/plugins/drilldowns/ @elastic/kibana-app-arch # APM -/x-pack/legacy/plugins/apm/ @elastic/apm-ui +/x-pack/plugins/apm/ @elastic/apm-ui /x-pack/plugins/apm/ @elastic/apm-ui /x-pack/test/functional/apps/apm/ @elastic/apm-ui /src/legacy/core_plugins/apm_oss/ @elastic/apm-ui @@ -78,7 +77,7 @@ /x-pack/legacy/plugins/beats_management/ @elastic/beats # Canvas -/x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas +/x-pack/plugins/canvas/ @elastic/kibana-canvas # Observability UIs /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui diff --git a/.github/paths-labeller.yml b/.github/paths-labeller.yml index 89e0af270c54d..d9d99fc1416e4 100644 --- a/.github/paths-labeller.yml +++ b/.github/paths-labeller.yml @@ -8,9 +8,8 @@ - "Feature:ExpressionLanguage": - "src/plugins/expressions/**/*.*" - "src/plugins/bfetch/**/*.*" - - "Team:apm" + - "Team:apm": + - "x-pack/plugins/apm/**/*.*" - "x-pack/plugins/apm/**/*.*" - - "x-pack/legacy/plugins/apm/**/*.*" - "Team:uptime": - "x-pack/plugins/uptime/**/*.*" - - "x-pack/legacy/plugins/uptime/**/*.*" diff --git a/.gitignore b/.gitignore index bd7a954f950e9..13c7cd5fb2769 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,6 @@ package-lock.json *.sublime-* npm-debug.log* .tern-project -x-pack/legacy/plugins/apm/tsconfig.json +x-pack/plugins/apm/tsconfig.json apm.tsconfig.json -/x-pack/legacy/plugins/apm/e2e/snapshots.js +/x-pack/plugins/apm/e2e/snapshots.js diff --git a/.i18nrc.json b/.i18nrc.json index b04c02f6b2265..be3c043b6e52f 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -8,6 +8,7 @@ "data": "src/plugins/data", "embeddableApi": "src/plugins/embeddable", "embeddableExamples": "examples/embeddable_examples", + "uiActionsExamples": "examples/ui_action_examples", "share": "src/plugins/share", "home": "src/plugins/home", "charts": "src/plugins/charts", diff --git a/.sass-lint.yml b/.sass-lint.yml index 44b4d49384136..c8985108dabf2 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -5,14 +5,14 @@ files: - 'src/plugins/vis_type_vislib/**/*.s+(a|c)ss' - 'src/plugins/vis_type_xy/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss' - - 'x-pack/legacy/plugins/canvas/**/*.s+(a|c)ss' + - 'x-pack/plugins/canvas/**/*.s+(a|c)ss' - 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss' - 'x-pack/plugins/lens/**/*.s+(a|c)ss' - 'x-pack/plugins/cross_cluster_replication/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/maps/**/*.s+(a|c)ss' - 'x-pack/plugins/maps/**/*.s+(a|c)ss' ignore: - - 'x-pack/legacy/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss' + - 'x-pack/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss' rules: quotes: - 2 diff --git a/config/kibana.yml b/config/kibana.yml index 0780841ca057e..8725888159506 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -40,7 +40,7 @@ # the username and password that the Kibana server uses to perform maintenance on the Kibana # index at startup. Your Kibana users still need to authenticate with Elasticsearch, which # is proxied through the Kibana server. -#elasticsearch.username: "kibana" +#elasticsearch.username: "kibana_system" #elasticsearch.password: "pass" # Enables SSL and paths to the PEM-format SSL certificate and SSL key files, respectively. diff --git a/docs/apm/spans.asciidoc b/docs/apm/spans.asciidoc index 2eed339160fc4..c35fb115d2db4 100644 --- a/docs/apm/spans.asciidoc +++ b/docs/apm/spans.asciidoc @@ -1,38 +1,53 @@ [role="xpack"] [[spans]] -=== Span timeline +=== Trace sample timeline -TIP: A {apm-overview-ref-v}/transaction-spans.html[span] is the duration of a single event. -Spans are automatically captured by APM agents, and you can also define custom spans. -Each span has a type and is defined by a different color in the timeline/waterfall visualization. - -The span timeline visualization is a bird's-eye view of what your application was doing while it was trying to respond to the request that came in. +The trace sample timeline visualization is a bird's-eye view of what your application was doing while it was trying to respond to a request. This makes it useful for visualizing where the selected transaction spent most of its time. [role="screenshot"] image::apm/images/apm-transaction-sample.png[Example of distributed trace colors in the APM app in Kibana] View a span in detail by clicking on it in the timeline waterfall. -When you click on an SQL Select database query, +For example, when you click on an SQL Select database query, the information displayed includes the actual SQL that was executed, how long it took, and the percentage of the trace's total time. You also get a stack trace, which shows the SQL query in your code. Finally, APM knows which files are your code and which are just modules or libraries that you've installed. These library frames will be minimized by default in order to show you the most relevant stack trace. +TIP: A {apm-overview-ref-v}/transaction-spans.html[span] is the duration of a single event. +Spans are automatically captured by APM agents, and you can also define custom spans. +Each span has a type and is defined by a different color in the timeline/waterfall visualization. + [role="screenshot"] image::apm/images/apm-span-detail.png[Example view of a span detail in the APM app in Kibana] -If your span timeline is colorful, it's indicative of a <>. +[float] +[[distributed-tracing]] +==== Distributed tracing + +If your trace sample timeline is colorful, it's indicative of a distributed trace. Services in a distributed trace are separated by color and listed in the order they occur. [role="screenshot"] image::apm/images/apm-services-trace.png[Example of distributed trace colors in the APM app in Kibana] -Don't forget; a distributed trace includes more than one transaction. +As application architectures are shifting from monolithic to more distributed, service-based architectures, +distributed tracing has become a crucial feature of modern application performance monitoring. +It allows you to trace requests through your service architecture automatically, and visualize those traces in one single view in the APM app. +From initial web requests to your front-end service, to queries made to your back-end services, +this makes finding possible bottlenecks throughout your application much easier and faster. + +[role="screenshot"] +image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM app in Kibana] + +Don't forget; by definition, a distributed trace includes more than one transaction. When viewing these distributed traces in the timeline waterfall, you'll see this image:apm/images/transaction-icon.png[APM icon] icon, which indicates the next transaction in the trace. These transactions can be expanded and viewed in detail by clicking on them. After exploring these traces, you can return to the full trace by clicking *View full trace*. + +TIP: Distributed tracing is supported by all APM agents, and there's no additional configuration needed. diff --git a/docs/apm/traces.asciidoc b/docs/apm/traces.asciidoc index 8eef3d9bed4db..52b4b618de466 100644 --- a/docs/apm/traces.asciidoc +++ b/docs/apm/traces.asciidoc @@ -4,7 +4,7 @@ TIP: Traces link together related transactions to show an end-to-end performance of how a request was served and which services were part of it. -In addition to the Traces overview, you can view your application traces in the <>. +In addition to the Traces overview, you can view your application traces in the <>. The *Traces* overview displays the entry transaction for all traces in your application. If you're using <>, this view is key to finding the critical paths within your application. @@ -17,25 +17,3 @@ If there's a particular endpoint you're worried about, you can click on it to vi [role="screenshot"] image::apm/images/apm-traces.png[Example view of the Traces overview in APM app in Kibana] - -[float] -[[distributed-tracing]] -==== Distributed tracing - -Elastic APM supports distributed tracing. -Distributed tracing is a key feature of modern application performance monitoring as application architectures are shifting from monolithic to more distributed, -service-based architectures. - -Distributed tracing allows APM users to automatically trace requests all the way through the service architecture, -and visualize those traces in one single view in the APM app. -This is accomplished by tracing all of the requests, from the initial web request to your front-end service, -to queries made to your back-end services. -This makes finding possible bottlenecks throughout your application much easier and faster. - -By definition, a distributed trace includes more than one transaction. -You can use the <> to view a waterfall display of all of the transactions from individual services that are connected in a trace. - -[role="screenshot"] -image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM app in Kibana] - -TIP: Distributed tracing is supported by all APM agents, and there's no additional configuration needed. \ No newline at end of file diff --git a/docs/apm/transactions.asciidoc b/docs/apm/transactions.asciidoc index 2e1022e6d684c..8012c9108ca5e 100644 --- a/docs/apm/transactions.asciidoc +++ b/docs/apm/transactions.asciidoc @@ -95,7 +95,7 @@ It's the requests on the right, the ones taking longer than average, that we pro When you select one of these buckets, you're presented with up to ten trace samples. -Each sample has a span timeline waterfall that shows what a typical request in that bucket was doing. +Each sample has a trace timeline waterfall that shows what a typical request in that bucket was doing. By investigating this timeline waterfall, we can hopefully determine _why_ this request was slow and then implement a fix. [role="screenshot"] diff --git a/docs/canvas/canvas-function-reference.asciidoc b/docs/canvas/canvas-function-reference.asciidoc index 16aaf55802b17..657e3ec8b8bb1 100644 --- a/docs/canvas/canvas-function-reference.asciidoc +++ b/docs/canvas/canvas-function-reference.asciidoc @@ -42,10 +42,10 @@ filters | metric "Average uptime" metricFont={ font size=48 family="'Open Sans', Helvetica, Arial, sans-serif" - color={ - if {all {gte 0} {lt 0.8}} then="red" else="green" - } - align="center" lHeight=48 + color={ + if {all {gte 0} {lt 0.8}} then="red" else="green" + } + align="center" lHeight=48 } | render ---- @@ -324,12 +324,14 @@ case if={lte 50} then="green" ---- math "random()" | progress shape="gauge" label={formatnumber "0%"} - font={font size=24 family="'Open Sans', Helvetica, Arial, sans-serif" align="center" - color={ - switch {case if={lte 0.5} then="green"} - {case if={all {gt 0.5} {lte 0.75}} then="orange"} - default="red" - }} + font={ + font size=24 family="'Open Sans', Helvetica, Arial, sans-serif" align="center" + color={ + switch {case if={lte 0.5} then="green"} + {case if={all {gt 0.5} {lte 0.75}} then="orange"} + default="red" + } + } valueColor={ switch {case if={lte 0.5} then="green"} {case if={all {gt 0.5} {lte 0.75}} then="orange"} @@ -693,7 +695,25 @@ Alias: `value` [[demodata_fn]] === `demodata` -A mock data set that includes project CI times with usernames, countries, and run phases. +A sample data set that includes project CI times with usernames, countries, and run phases. + +*Expression syntax* +[source,js] +---- +demodata +demodata "ci" +demodata type="shirts" +---- + +*Code example* +[source,text] +---- +filters +| demodata +| table +| render +---- +`demodata` is a mock data set that you can use to start playing around in Canvas. *Accepts:* `filter` @@ -837,6 +857,28 @@ Alias: `value` Query Elasticsearch for the number of hits matching the specified query. +*Expression syntax* +[source,js] +---- +escount index="logstash-*" +escount "currency:"EUR"" index="kibana_sample_data_ecommerce" +escount query="response:404" index="kibana_sample_data_logs" +---- + +*Code example* +[source,text] +---- +filters +| escount "Cancelled:true" index="kibana_sample_data_flights" +| math "value" +| progress shape="semicircle" + label={formatnumber 0,0} + font={font size=24 family="'Open Sans', Helvetica, Arial, sans-serif" color="#000000" align=center} + max={filters | escount index="kibana_sample_data_flights"} +| render +---- +The first `escount` expression retrieves the number of flights that were cancelled. The second `escount` expression retrieves the total number of flights. + *Accepts:* `filter` [cols="3*^<"] @@ -867,6 +909,34 @@ Default: `_all` Query Elasticsearch for raw documents. Specify the fields you want to retrieve, especially if you are asking for a lot of rows. +*Expression syntax* +[source,js] +---- +esdocs index="logstash-*" +esdocs "currency:"EUR"" index="kibana_sample_data_ecommerce" +esdocs query="response:404" index="kibana_sample_data_logs" +esdocs index="kibana_sample_data_flights" count=100 +esdocs index="kibana_sample_data_flights" sort="AvgTicketPrice, asc" +---- + +*Code example* +[source,text] +---- +filters +| esdocs index="kibana_sample_data_ecommerce" + fields="customer_gender, taxful_total_price, order_date" + sort="order_date, asc" + count=10000 +| mapColumn "order_date" + fn={getCell "order_date" | date {context} | rounddate "YYYY-MM-DD"} +| alterColumn "order_date" type="date" +| pointseries x="order_date" y="sum(taxful_total_price)" color="customer_gender" +| plot defaultStyle={seriesStyle lines=3} + palette={palette "#7ECAE3" "#003A4D" gradient=true} +| render +---- +This retrieves the first 10000 documents data from the `kibana_sample_data_ecommerce` index sorted by `order_date` in ascending order, and only requests the `customer_gender`, `taxful_total_price`, and `order_date` fields. + *Accepts:* `filter` [cols="3*^<"] @@ -915,6 +985,23 @@ Default: `_all` Queries Elasticsearch using Elasticsearch SQL. +*Expression syntax* +[source,js] +---- +essql query="SELECT * FROM "logstash*"" +essql "SELECT * FROM "apm*"" count=10000 +---- + +*Code example* +[source,text] +---- +filters +| essql query="SELECT Carrier, FlightDelayMin, AvgTicketPrice FROM "kibana_sample_data_flights"" +| table +| render +---- +This retrieves the `Carrier`, `FlightDelayMin`, and `AvgTicketPrice` fields from the "kibana_sample_data_flights" index. + *Accepts:* `filter` [cols="3*^<"] @@ -1107,7 +1194,7 @@ Default: `false` [[font_fn]] === `font` -Creates a font style. +Create a font style. *Expression syntax* [source,js] @@ -1244,7 +1331,7 @@ Alias: `format` [[formatnumber_fn]] === `formatnumber` -Formats a number into a formatted number string using the <>. +Formats a number into a formatted number string using the Numeral pattern. *Expression syntax* [source,js] @@ -1276,7 +1363,7 @@ The `formatnumber` subexpression receives the same `context` as the `progress` f Alias: `format` |`string` -|A <> string. For example, `"0.0a"` or `"0%"`. +|A Numeral pattern format string. For example, `"0.0a"` or `"0%"`. |=== *Returns:* `string` @@ -1559,6 +1646,34 @@ Alias: `value` [[m_fns]] == M +[float] +[[mapCenter_fn]] +=== `mapCenter` + +Returns an object with the center coordinates and zoom level of the map. + +*Accepts:* `null` + +[cols="3*^<"] +|=== +|Argument |Type |Description + +|`lat` *** +|`number` +|Latitude for the center of the map + +|`lon` *** +|`number` +|Longitude for the center of the map + +|`zoom` *** +|`number` +|Zoom level of the map +|=== + +*Returns:* `mapCenter` + + [float] [[mapColumn_fn]] === `mapColumn` @@ -1612,6 +1727,12 @@ Default: `""` |The CSS font properties for the content. For example, "font-family" or "font-weight". Default: `${font}` + +|`openLinksInNewTab` +|`boolean` +|A true or false value for opening links in a new tab. The default value is `false`. Setting to `true` opens all links in a new tab. + +Default: `false` |=== *Returns:* `render` @@ -1675,7 +1796,7 @@ Default: `${font size=48 family="'Open Sans', Helvetica, Arial, sans-serif" colo Alias: `format` |`string` -|A <> string. For example, `"0.0a"` or `"0%"`. +|A Numeral pattern format string. For example, `"0.0a"` or `"0%"`. |=== *Returns:* `render` @@ -2184,6 +2305,102 @@ Returns the number of rows. Pairs with <> to get the count of unique col [[s_fns]] == S +[float] +[[savedLens_fn]] +=== `savedLens` + +Returns an embeddable for a saved Lens visualization object. + +*Accepts:* `any` + +[cols="3*^<"] +|=== +|Argument |Type |Description + +|`id` +|`string` +|The ID of the saved Lens visualization object + +|`timerange` +|`timerange` +|The timerange of data that should be included + +|`title` +|`string` +|The title for the Lens visualization object +|=== + +*Returns:* `embeddable` + + +[float] +[[savedMap_fn]] +=== `savedMap` + +Returns an embeddable for a saved map object. + +*Accepts:* `any` + +[cols="3*^<"] +|=== +|Argument |Type |Description + +|`center` +|`mapCenter` +|The center and zoom level the map should have + +|`hideLayer` † +|`string` +|The IDs of map layers that should be hidden + +|`id` +|`string` +|The ID of the saved map object + +|`timerange` +|`timerange` +|The timerange of data that should be included + +|`title` +|`string` +|The title for the map +|=== + +*Returns:* `embeddable` + + +[float] +[[savedVisualization_fn]] +=== `savedVisualization` + +Returns an embeddable for a saved visualization object. + +*Accepts:* `any` + +[cols="3*^<"] +|=== +|Argument |Type |Description + +|`colors` † +|`seriesStyle` +|Defines the color to use for a specific series + +|`hideLegend` +|`boolean` +|Specifies the option to hide the legend + +|`id` +|`string` +|The ID of the saved visualization object + +|`timerange` +|`timerange` +|The timerange of data that should be included +|=== + +*Returns:* `embeddable` + + [float] [[seriesStyle_fn]] === `seriesStyle` @@ -2579,6 +2796,30 @@ Default: `"now"` *Returns:* `datatable` +[float] +[[timerange_fn]] +=== `timerange` + +An object that represents a span of time. + +*Accepts:* `null` + +[cols="3*^<"] +|=== +|Argument |Type |Description + +|`from` *** +|`string` +|The start of the time range + +|`to` *** +|`string` +|The end of the time range +|=== + +*Returns:* `timerange` + + [float] [[to_fn]] === `to` diff --git a/docs/development/core/public/kibana-plugin-core-public.appcategory.id.md b/docs/development/core/public/kibana-plugin-core-public.appcategory.id.md new file mode 100644 index 0000000000000..0342a1d9ee95b --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.appcategory.id.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppCategory](./kibana-plugin-core-public.appcategory.md) > [id](./kibana-plugin-core-public.appcategory.id.md) + +## AppCategory.id property + +Unique identifier for the categories + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.appcategory.md b/docs/development/core/public/kibana-plugin-core-public.appcategory.md index b115baa1be1a3..d91727a1bbf29 100644 --- a/docs/development/core/public/kibana-plugin-core-public.appcategory.md +++ b/docs/development/core/public/kibana-plugin-core-public.appcategory.md @@ -18,6 +18,7 @@ export interface AppCategory | --- | --- | --- | | [ariaLabel](./kibana-plugin-core-public.appcategory.arialabel.md) | string | If the visual label isn't appropriate for screen readers, can override it here | | [euiIconType](./kibana-plugin-core-public.appcategory.euiicontype.md) | string | Define an icon to be used for the category If the category is only 1 item, and no icon is defined, will default to the product icon Defaults to initials if no icon is defined | +| [id](./kibana-plugin-core-public.appcategory.id.md) | string | Unique identifier for the categories | | [label](./kibana-plugin-core-public.appcategory.label.md) | string | Label used for cateogry name. Also used as aria-label if one isn't set. | | [order](./kibana-plugin-core-public.appcategory.order.md) | number | The order that categories will be sorted in Prefer large steps between categories to allow for further editing (Default categories are in steps of 1000) | diff --git a/docs/development/core/public/kibana-plugin-core-public.assertnever.md b/docs/development/core/public/kibana-plugin-core-public.assertnever.md new file mode 100644 index 0000000000000..8fefd4450d49b --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.assertnever.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [assertNever](./kibana-plugin-core-public.assertnever.md) + +## assertNever() function + +Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking + +Signature: + +```typescript +export declare function assertNever(x: never): never; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| x | never | | + +Returns: + +`never` + diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.getnavtype_.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.getnavtype_.md new file mode 100644 index 0000000000000..09864be43996d --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.chromestart.getnavtype_.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [getNavType$](./kibana-plugin-core-public.chromestart.getnavtype_.md) + +## ChromeStart.getNavType$() method + +Get the navigation type TODO \#64541 Can delete + +Signature: + +```typescript +getNavType$(): Observable; +``` +Returns: + +`Observable` + diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.md index c179e089d7cfd..b4eadc93fe78d 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromestart.md @@ -58,6 +58,7 @@ core.chrome.setHelpExtension(elem => { | [getHelpExtension$()](./kibana-plugin-core-public.chromestart.gethelpextension_.md) | Get an observable of the current custom help conttent | | [getIsNavDrawerLocked$()](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) | Get an observable of the current locked state of the nav drawer. | | [getIsVisible$()](./kibana-plugin-core-public.chromestart.getisvisible_.md) | Get an observable of the current visibility state of the chrome. | +| [getNavType$()](./kibana-plugin-core-public.chromestart.getnavtype_.md) | Get the navigation type TODO \#64541 Can delete | | [removeApplicationClass(className)](./kibana-plugin-core-public.chromestart.removeapplicationclass.md) | Remove a className added with addApplicationClass(). If className is unknown it is ignored. | | [setAppTitle(appTitle)](./kibana-plugin-core-public.chromestart.setapptitle.md) | Sets the current app's title | | [setBadge(badge)](./kibana-plugin-core-public.chromestart.setbadge.md) | Override the current badge | diff --git a/docs/development/core/public/kibana-plugin-core-public.deepfreeze.md b/docs/development/core/public/kibana-plugin-core-public.deepfreeze.md new file mode 100644 index 0000000000000..7c879b659a852 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.deepfreeze.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [deepFreeze](./kibana-plugin-core-public.deepfreeze.md) + +## deepFreeze() function + +Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively + +Signature: + +```typescript +export declare function deepFreeze(object: T): RecursiveReadonly; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| object | T | | + +Returns: + +`RecursiveReadonly` + diff --git a/docs/development/core/public/kibana-plugin-core-public.freezable.md b/docs/development/core/public/kibana-plugin-core-public.freezable.md new file mode 100644 index 0000000000000..fee87dde25c28 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.freezable.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [Freezable](./kibana-plugin-core-public.freezable.md) + +## Freezable type + + +Signature: + +```typescript +export declare type Freezable = { + [k: string]: any; +} | any[]; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.getflattenedobject.md b/docs/development/core/public/kibana-plugin-core-public.getflattenedobject.md new file mode 100644 index 0000000000000..3ef9b6bf703eb --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.getflattenedobject.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [getFlattenedObject](./kibana-plugin-core-public.getflattenedobject.md) + +## getFlattenedObject() function + +Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from `rootValue`. + +example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } + +Signature: + +```typescript +export declare function getFlattenedObject(rootValue: Record): { + [key: string]: any; +}; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| rootValue | Record<string, any> | | + +Returns: + +`{ + [key: string]: any; +}` + diff --git a/docs/development/core/public/kibana-plugin-core-public.isrelativeurl.md b/docs/development/core/public/kibana-plugin-core-public.isrelativeurl.md new file mode 100644 index 0000000000000..3c2ffa6340a97 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.isrelativeurl.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [isRelativeUrl](./kibana-plugin-core-public.isrelativeurl.md) + +## isRelativeUrl() function + +Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* + +Signature: + +```typescript +export declare function isRelativeUrl(candidatePath: string): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| candidatePath | string | | + +Returns: + +`boolean` + diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index adc87de2b9e7e..eafc81447ee03 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -27,6 +27,16 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AppNavLinkStatus](./kibana-plugin-core-public.appnavlinkstatus.md) | Status of the application's navLink. | | [AppStatus](./kibana-plugin-core-public.appstatus.md) | Accessibility status of an application. | +## Functions + +| Function | Description | +| --- | --- | +| [assertNever(x)](./kibana-plugin-core-public.assertnever.md) | Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking | +| [deepFreeze(object)](./kibana-plugin-core-public.deepfreeze.md) | Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively | +| [getFlattenedObject(rootValue)](./kibana-plugin-core-public.getflattenedobject.md) | Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from rootValue.example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } | +| [isRelativeUrl(candidatePath)](./kibana-plugin-core-public.isrelativeurl.md) | Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* | +| [modifyUrl(url, urlModifier)](./kibana-plugin-core-public.modifyurl.md) | Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url.Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hashWhy? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints | + ## Interfaces | Interface | Description | @@ -118,6 +128,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ToastOptions](./kibana-plugin-core-public.toastoptions.md) | Options available for [IToasts](./kibana-plugin-core-public.itoasts.md) APIs. | | [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) | UiSettings parameters defined by the plugins. | | [UiSettingsState](./kibana-plugin-core-public.uisettingsstate.md) | | +| [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) | We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". | | [UserProvidedValues](./kibana-plugin-core-public.userprovidedvalues.md) | Describes the values explicitly set by user. | ## Type Aliases @@ -139,6 +150,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ChromeHelpExtensionMenuLink](./kibana-plugin-core-public.chromehelpextensionmenulink.md) | | | [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md) | | | [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. | +| [Freezable](./kibana-plugin-core-public.freezable.md) | | | [HandlerContextType](./kibana-plugin-core-public.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-core-public.handlerfunction.md) to represent the type of the context. | | [HandlerFunction](./kibana-plugin-core-public.handlerfunction.md) | A function that accepts a context object and an optional number of additional arguments. Used for the generic types in [IContextContainer](./kibana-plugin-core-public.icontextcontainer.md) | | [HandlerParameters](./kibana-plugin-core-public.handlerparameters.md) | Extracts the types of the additional arguments of a [HandlerFunction](./kibana-plugin-core-public.handlerfunction.md), excluding the [HandlerContextType](./kibana-plugin-core-public.handlercontexttype.md). | @@ -146,6 +158,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [IContextProvider](./kibana-plugin-core-public.icontextprovider.md) | A function that returns a context value for a specific key of given context type. | | [IToasts](./kibana-plugin-core-public.itoasts.md) | Methods for adding and removing global toast messages. See [ToastsApi](./kibana-plugin-core-public.toastsapi.md). | | [MountPoint](./kibana-plugin-core-public.mountpoint.md) | A function that should mount DOM content inside the provided container element and return a handler to unmount it. | +| [NavType](./kibana-plugin-core-public.navtype.md) | | | [PluginInitializer](./kibana-plugin-core-public.plugininitializer.md) | The plugin export at the root of a plugin's public directory should conform to this interface. | | [PluginOpaqueId](./kibana-plugin-core-public.pluginopaqueid.md) | | | [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. | diff --git a/docs/development/core/public/kibana-plugin-core-public.modifyurl.md b/docs/development/core/public/kibana-plugin-core-public.modifyurl.md new file mode 100644 index 0000000000000..b174f733a5c64 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.modifyurl.md @@ -0,0 +1,31 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [modifyUrl](./kibana-plugin-core-public.modifyurl.md) + +## modifyUrl() function + +Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url. + +Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hash + +Why? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints + +Signature: + +```typescript +export declare function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| url | string | | +| urlModifier | (urlParts: URLMeaningfulParts) => Partial<URLMeaningfulParts> | void | | + +Returns: + +`string` + +The modified and reformatted url + diff --git a/docs/development/core/public/kibana-plugin-core-public.navtype.md b/docs/development/core/public/kibana-plugin-core-public.navtype.md new file mode 100644 index 0000000000000..8f1d9a4351754 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.navtype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [NavType](./kibana-plugin-core-public.navtype.md) + +## NavType type + +Signature: + +```typescript +export declare type NavType = 'modern' | 'legacy'; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.auth.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.auth.md new file mode 100644 index 0000000000000..238dd66885896 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.auth.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [auth](./kibana-plugin-core-public.urlmeaningfulparts.auth.md) + +## URLMeaningfulParts.auth property + +Signature: + +```typescript +auth?: string | null; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hash.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hash.md new file mode 100644 index 0000000000000..161e7dc7ebfae --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hash.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [hash](./kibana-plugin-core-public.urlmeaningfulparts.hash.md) + +## URLMeaningfulParts.hash property + +Signature: + +```typescript +hash?: string | null; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hostname.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hostname.md new file mode 100644 index 0000000000000..f1884718337b5 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hostname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [hostname](./kibana-plugin-core-public.urlmeaningfulparts.hostname.md) + +## URLMeaningfulParts.hostname property + +Signature: + +```typescript +hostname?: string | null; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.md new file mode 100644 index 0000000000000..2816d4c7df541 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) + +## URLMeaningfulParts interface + +We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". + +Signature: + +```typescript +export interface URLMeaningfulParts +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [auth](./kibana-plugin-core-public.urlmeaningfulparts.auth.md) | string | null | | +| [hash](./kibana-plugin-core-public.urlmeaningfulparts.hash.md) | string | null | | +| [hostname](./kibana-plugin-core-public.urlmeaningfulparts.hostname.md) | string | null | | +| [pathname](./kibana-plugin-core-public.urlmeaningfulparts.pathname.md) | string | null | | +| [port](./kibana-plugin-core-public.urlmeaningfulparts.port.md) | string | null | | +| [protocol](./kibana-plugin-core-public.urlmeaningfulparts.protocol.md) | string | null | | +| [query](./kibana-plugin-core-public.urlmeaningfulparts.query.md) | ParsedQuery | | +| [slashes](./kibana-plugin-core-public.urlmeaningfulparts.slashes.md) | boolean | null | | + diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.pathname.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.pathname.md new file mode 100644 index 0000000000000..5ad21f004481c --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.pathname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [pathname](./kibana-plugin-core-public.urlmeaningfulparts.pathname.md) + +## URLMeaningfulParts.pathname property + +Signature: + +```typescript +pathname?: string | null; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.port.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.port.md new file mode 100644 index 0000000000000..2e70da2f17421 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.port.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [port](./kibana-plugin-core-public.urlmeaningfulparts.port.md) + +## URLMeaningfulParts.port property + +Signature: + +```typescript +port?: string | null; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.protocol.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.protocol.md new file mode 100644 index 0000000000000..cedc7f0b878e3 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.protocol.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [protocol](./kibana-plugin-core-public.urlmeaningfulparts.protocol.md) + +## URLMeaningfulParts.protocol property + +Signature: + +```typescript +protocol?: string | null; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.query.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.query.md new file mode 100644 index 0000000000000..a9541efe0882a --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.query.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [query](./kibana-plugin-core-public.urlmeaningfulparts.query.md) + +## URLMeaningfulParts.query property + +Signature: + +```typescript +query: ParsedQuery; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.slashes.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.slashes.md new file mode 100644 index 0000000000000..cb28a25f9e162 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.slashes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [slashes](./kibana-plugin-core-public.urlmeaningfulparts.slashes.md) + +## URLMeaningfulParts.slashes property + +Signature: + +```typescript +slashes?: boolean | null; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.assertnever.md b/docs/development/core/server/kibana-plugin-core-server.assertnever.md new file mode 100644 index 0000000000000..c13c88df9b9bf --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.assertnever.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [assertNever](./kibana-plugin-core-server.assertnever.md) + +## assertNever() function + +Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking + +Signature: + +```typescript +export declare function assertNever(x: never): never; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| x | never | | + +Returns: + +`never` + diff --git a/docs/development/core/server/kibana-plugin-core-server.deepfreeze.md b/docs/development/core/server/kibana-plugin-core-server.deepfreeze.md new file mode 100644 index 0000000000000..946050bff0585 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.deepfreeze.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [deepFreeze](./kibana-plugin-core-server.deepfreeze.md) + +## deepFreeze() function + +Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively + +Signature: + +```typescript +export declare function deepFreeze(object: T): RecursiveReadonly; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| object | T | | + +Returns: + +`RecursiveReadonly` + diff --git a/docs/development/core/server/kibana-plugin-core-server.freezable.md b/docs/development/core/server/kibana-plugin-core-server.freezable.md new file mode 100644 index 0000000000000..32ba89e8370c1 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.freezable.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Freezable](./kibana-plugin-core-server.freezable.md) + +## Freezable type + + +Signature: + +```typescript +export declare type Freezable = { + [k: string]: any; +} | any[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.getflattenedobject.md b/docs/development/core/server/kibana-plugin-core-server.getflattenedobject.md new file mode 100644 index 0000000000000..2e7850ca579f6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.getflattenedobject.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [getFlattenedObject](./kibana-plugin-core-server.getflattenedobject.md) + +## getFlattenedObject() function + +Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from `rootValue`. + +example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } + +Signature: + +```typescript +export declare function getFlattenedObject(rootValue: Record): { + [key: string]: any; +}; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| rootValue | Record<string, any> | | + +Returns: + +`{ + [key: string]: any; +}` + diff --git a/docs/development/core/server/kibana-plugin-core-server.isrelativeurl.md b/docs/development/core/server/kibana-plugin-core-server.isrelativeurl.md new file mode 100644 index 0000000000000..bff9eb05419be --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.isrelativeurl.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [isRelativeUrl](./kibana-plugin-core-server.isrelativeurl.md) + +## isRelativeUrl() function + +Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* + +Signature: + +```typescript +export declare function isRelativeUrl(candidatePath: string): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| candidatePath | string | | + +Returns: + +`boolean` + diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index a91a5bec988b7..14e01fda3d287 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -41,8 +41,13 @@ The plugin integrates with the core system via lifecycle events: `setup` | Function | Description | | --- | --- | +| [assertNever(x)](./kibana-plugin-core-server.assertnever.md) | Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking | +| [deepFreeze(object)](./kibana-plugin-core-server.deepfreeze.md) | Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively | | [exportSavedObjectsToStream({ types, objects, search, savedObjectsClient, exportSizeLimit, includeReferencesDeep, excludeExportDetails, namespace, })](./kibana-plugin-core-server.exportsavedobjectstostream.md) | Generates sorted saved object stream to be used for export. See the [options](./kibana-plugin-core-server.savedobjectsexportoptions.md) for more detailed information. | +| [getFlattenedObject(rootValue)](./kibana-plugin-core-server.getflattenedobject.md) | Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from rootValue.example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } | | [importSavedObjectsFromStream({ readStream, objectLimit, overwrite, savedObjectsClient, supportedTypes, namespace, })](./kibana-plugin-core-server.importsavedobjectsfromstream.md) | Import saved objects from given stream. See the [options](./kibana-plugin-core-server.savedobjectsimportoptions.md) for more detailed information. | +| [isRelativeUrl(candidatePath)](./kibana-plugin-core-server.isrelativeurl.md) | Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* | +| [modifyUrl(url, urlModifier)](./kibana-plugin-core-server.modifyurl.md) | Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url.Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hashWhy? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints | | [resolveSavedObjectsImportErrors({ readStream, objectLimit, retries, savedObjectsClient, supportedTypes, namespace, })](./kibana-plugin-core-server.resolvesavedobjectsimporterrors.md) | Resolve and return saved object import errors. See the [options](./kibana-plugin-core-server.savedobjectsresolveimporterrorsoptions.md) for more detailed informations. | ## Interfaces @@ -186,6 +191,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | UiSettings parameters defined by the plugins. | | [UiSettingsServiceSetup](./kibana-plugin-core-server.uisettingsservicesetup.md) | | | [UiSettingsServiceStart](./kibana-plugin-core-server.uisettingsservicestart.md) | | +| [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) | We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". | | [UserProvidedValues](./kibana-plugin-core-server.userprovidedvalues.md) | Describes the values explicitly set by user. | | [UuidServiceSetup](./kibana-plugin-core-server.uuidservicesetup.md) | APIs to access the application's instance uuid. | @@ -212,6 +218,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ConfigPath](./kibana-plugin-core-server.configpath.md) | | | [DestructiveRouteMethod](./kibana-plugin-core-server.destructiveroutemethod.md) | Set of HTTP methods changing the state of the server. | | [ElasticsearchClientConfig](./kibana-plugin-core-server.elasticsearchclientconfig.md) | | +| [Freezable](./kibana-plugin-core-server.freezable.md) | | | [GetAuthHeaders](./kibana-plugin-core-server.getauthheaders.md) | Get headers to authenticate a user against Elasticsearch. | | [GetAuthState](./kibana-plugin-core-server.getauthstate.md) | Gets authentication state for a request. Returned by auth interceptor. | | [HandlerContextType](./kibana-plugin-core-server.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-core-server.handlerfunction.md) to represent the type of the context. | diff --git a/docs/development/core/server/kibana-plugin-core-server.modifyurl.md b/docs/development/core/server/kibana-plugin-core-server.modifyurl.md new file mode 100644 index 0000000000000..fc0bc354a3ca3 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.modifyurl.md @@ -0,0 +1,31 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [modifyUrl](./kibana-plugin-core-server.modifyurl.md) + +## modifyUrl() function + +Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url. + +Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hash + +Why? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints + +Signature: + +```typescript +export declare function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| url | string | | +| urlModifier | (urlParts: URLMeaningfulParts) => Partial<URLMeaningfulParts> | void | | + +Returns: + +`string` + +The modified and reformatted url + diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.auth.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.auth.md new file mode 100644 index 0000000000000..0422738669a70 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.auth.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [auth](./kibana-plugin-core-server.urlmeaningfulparts.auth.md) + +## URLMeaningfulParts.auth property + +Signature: + +```typescript +auth?: string | null; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hash.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hash.md new file mode 100644 index 0000000000000..13a3f4a9c95c8 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hash.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [hash](./kibana-plugin-core-server.urlmeaningfulparts.hash.md) + +## URLMeaningfulParts.hash property + +Signature: + +```typescript +hash?: string | null; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hostname.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hostname.md new file mode 100644 index 0000000000000..6631f6f6744c5 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hostname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [hostname](./kibana-plugin-core-server.urlmeaningfulparts.hostname.md) + +## URLMeaningfulParts.hostname property + +Signature: + +```typescript +hostname?: string | null; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.md new file mode 100644 index 0000000000000..257f7b4b634ab --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) + +## URLMeaningfulParts interface + +We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". + +Signature: + +```typescript +export interface URLMeaningfulParts +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [auth](./kibana-plugin-core-server.urlmeaningfulparts.auth.md) | string | null | | +| [hash](./kibana-plugin-core-server.urlmeaningfulparts.hash.md) | string | null | | +| [hostname](./kibana-plugin-core-server.urlmeaningfulparts.hostname.md) | string | null | | +| [pathname](./kibana-plugin-core-server.urlmeaningfulparts.pathname.md) | string | null | | +| [port](./kibana-plugin-core-server.urlmeaningfulparts.port.md) | string | null | | +| [protocol](./kibana-plugin-core-server.urlmeaningfulparts.protocol.md) | string | null | | +| [query](./kibana-plugin-core-server.urlmeaningfulparts.query.md) | ParsedQuery | | +| [slashes](./kibana-plugin-core-server.urlmeaningfulparts.slashes.md) | boolean | null | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.pathname.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.pathname.md new file mode 100644 index 0000000000000..8fee8c8e146ca --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.pathname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [pathname](./kibana-plugin-core-server.urlmeaningfulparts.pathname.md) + +## URLMeaningfulParts.pathname property + +Signature: + +```typescript +pathname?: string | null; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.port.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.port.md new file mode 100644 index 0000000000000..dcf3517d92ba2 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.port.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [port](./kibana-plugin-core-server.urlmeaningfulparts.port.md) + +## URLMeaningfulParts.port property + +Signature: + +```typescript +port?: string | null; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.protocol.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.protocol.md new file mode 100644 index 0000000000000..914dcd4e8a8a5 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.protocol.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [protocol](./kibana-plugin-core-server.urlmeaningfulparts.protocol.md) + +## URLMeaningfulParts.protocol property + +Signature: + +```typescript +protocol?: string | null; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.query.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.query.md new file mode 100644 index 0000000000000..358adcfd3d180 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.query.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [query](./kibana-plugin-core-server.urlmeaningfulparts.query.md) + +## URLMeaningfulParts.query property + +Signature: + +```typescript +query: ParsedQuery; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.slashes.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.slashes.md new file mode 100644 index 0000000000000..d5b598167f2f2 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.slashes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [slashes](./kibana-plugin-core-server.urlmeaningfulparts.slashes.md) + +## URLMeaningfulParts.slashes property + +Signature: + +```typescript +slashes?: boolean | null; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index 7fd65e5db35f3..37142cf1794c3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -49,6 +49,7 @@ esFilters: { generateFilters: typeof generateFilters; onlyDisabledFiltersChanged: (newFilters?: import("../common").Filter[] | undefined, oldFilters?: import("../common").Filter[] | undefined) => boolean; changeTimeFilter: typeof changeTimeFilter; + convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString; mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[]; extractTimeFilter: typeof extractTimeFilter; } diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.__spec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.__spec.md deleted file mode 100644 index 43ff9a930b974..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.__spec.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [$$spec](./kibana-plugin-plugins-data-public.field.__spec.md) - -## Field.$$spec property - -Signature: - -```typescript -$$spec: FieldSpec; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.aggregatable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.aggregatable.md deleted file mode 100644 index fcfd7d73c8b0c..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.aggregatable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [aggregatable](./kibana-plugin-plugins-data-public.field.aggregatable.md) - -## Field.aggregatable property - -Signature: - -```typescript -aggregatable?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.conflictdescriptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.conflictdescriptions.md deleted file mode 100644 index 21b6917c4aad4..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.conflictdescriptions.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [conflictDescriptions](./kibana-plugin-plugins-data-public.field.conflictdescriptions.md) - -## Field.conflictDescriptions property - -Signature: - -```typescript -conflictDescriptions?: Record; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.count.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.count.md deleted file mode 100644 index 4f51d88a3046e..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.count.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [count](./kibana-plugin-plugins-data-public.field.count.md) - -## Field.count property - -Signature: - -```typescript -count?: number; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.displayname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.displayname.md deleted file mode 100644 index 0846a7595cf90..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.displayname.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [displayName](./kibana-plugin-plugins-data-public.field.displayname.md) - -## Field.displayName property - -Signature: - -```typescript -displayName?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.estypes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.estypes.md deleted file mode 100644 index efe1bceb43361..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.estypes.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [esTypes](./kibana-plugin-plugins-data-public.field.estypes.md) - -## Field.esTypes property - -Signature: - -```typescript -esTypes?: string[]; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.filterable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.filterable.md deleted file mode 100644 index fd7be589e87a7..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.filterable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [filterable](./kibana-plugin-plugins-data-public.field.filterable.md) - -## Field.filterable property - -Signature: - -```typescript -filterable?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.format.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.format.md deleted file mode 100644 index 431e043d1fecc..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.format.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [format](./kibana-plugin-plugins-data-public.field.format.md) - -## Field.format property - -Signature: - -```typescript -format: any; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.indexpattern.md deleted file mode 100644 index 59420747e0958..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.indexpattern.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [indexPattern](./kibana-plugin-plugins-data-public.field.indexpattern.md) - -## Field.indexPattern property - -Signature: - -```typescript -indexPattern?: IndexPattern; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.lang.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.lang.md deleted file mode 100644 index d51857090356f..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.lang.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [lang](./kibana-plugin-plugins-data-public.field.lang.md) - -## Field.lang property - -Signature: - -```typescript -lang?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.md deleted file mode 100644 index 86ff2b2c28ae9..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.md +++ /dev/null @@ -1,41 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) - -## Field class - -Signature: - -```typescript -export declare class Field implements IFieldType -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(indexPattern, spec, shortDotsEnable)](./kibana-plugin-plugins-data-public.field._constructor_.md) | | Constructs a new instance of the Field class | - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [$$spec](./kibana-plugin-plugins-data-public.field.__spec.md) | | FieldSpec | | -| [aggregatable](./kibana-plugin-plugins-data-public.field.aggregatable.md) | | boolean | | -| [conflictDescriptions](./kibana-plugin-plugins-data-public.field.conflictdescriptions.md) | | Record<string, string[]> | | -| [count](./kibana-plugin-plugins-data-public.field.count.md) | | number | | -| [displayName](./kibana-plugin-plugins-data-public.field.displayname.md) | | string | | -| [esTypes](./kibana-plugin-plugins-data-public.field.estypes.md) | | string[] | | -| [filterable](./kibana-plugin-plugins-data-public.field.filterable.md) | | boolean | | -| [format](./kibana-plugin-plugins-data-public.field.format.md) | | any | | -| [indexPattern](./kibana-plugin-plugins-data-public.field.indexpattern.md) | | IndexPattern | | -| [lang](./kibana-plugin-plugins-data-public.field.lang.md) | | string | | -| [name](./kibana-plugin-plugins-data-public.field.name.md) | | string | | -| [script](./kibana-plugin-plugins-data-public.field.script.md) | | string | | -| [scripted](./kibana-plugin-plugins-data-public.field.scripted.md) | | boolean | | -| [searchable](./kibana-plugin-plugins-data-public.field.searchable.md) | | boolean | | -| [sortable](./kibana-plugin-plugins-data-public.field.sortable.md) | | boolean | | -| [subType](./kibana-plugin-plugins-data-public.field.subtype.md) | | IFieldSubType | | -| [type](./kibana-plugin-plugins-data-public.field.type.md) | | string | | -| [visualizable](./kibana-plugin-plugins-data-public.field.visualizable.md) | | boolean | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.name.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.name.md deleted file mode 100644 index d2a9b9b86aefc..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [name](./kibana-plugin-plugins-data-public.field.name.md) - -## Field.name property - -Signature: - -```typescript -name: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.script.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.script.md deleted file mode 100644 index 676ff9bdfc35a..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.script.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [script](./kibana-plugin-plugins-data-public.field.script.md) - -## Field.script property - -Signature: - -```typescript -script?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.scripted.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.scripted.md deleted file mode 100644 index 1f6c8105e3f61..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.scripted.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [scripted](./kibana-plugin-plugins-data-public.field.scripted.md) - -## Field.scripted property - -Signature: - -```typescript -scripted?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.searchable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.searchable.md deleted file mode 100644 index 186d344f50378..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.searchable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [searchable](./kibana-plugin-plugins-data-public.field.searchable.md) - -## Field.searchable property - -Signature: - -```typescript -searchable?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.sortable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.sortable.md deleted file mode 100644 index 0cd4b14d0e1e5..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.sortable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [sortable](./kibana-plugin-plugins-data-public.field.sortable.md) - -## Field.sortable property - -Signature: - -```typescript -sortable?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.subtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.subtype.md deleted file mode 100644 index bef3b2131fa47..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.subtype.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [subType](./kibana-plugin-plugins-data-public.field.subtype.md) - -## Field.subType property - -Signature: - -```typescript -subType?: IFieldSubType; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.type.md deleted file mode 100644 index 490615edcf097..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [type](./kibana-plugin-plugins-data-public.field.type.md) - -## Field.type property - -Signature: - -```typescript -type: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.visualizable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.visualizable.md deleted file mode 100644 index f32a5c456dc5d..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field.visualizable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [visualizable](./kibana-plugin-plugins-data-public.field.visualizable.md) - -## Field.visualizable property - -Signature: - -```typescript -visualizable?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md new file mode 100644 index 0000000000000..60302286cbd72 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [getIndexPatternFieldListCreator](./kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md) + +## getIndexPatternFieldListCreator variable + +Signature: + +```typescript +getIndexPatternFieldListCreator: ({ fieldFormats, toastNotifications, }: FieldListDependencies) => CreateIndexPatternFieldList +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.add.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.add.md new file mode 100644 index 0000000000000..0f3469ae9c550 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.add.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) > [add](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.add.md) + +## IIndexPatternFieldList.add() method + +Signature: + +```typescript +add(field: FieldSpec): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | FieldSpec | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md new file mode 100644 index 0000000000000..14b5aa7137dc2 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) > [getByName](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md) + +## IIndexPatternFieldList.getByName() method + +Signature: + +```typescript +getByName(name: Field['name']): Field | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | Field['name'] | | + +Returns: + +`Field | undefined` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md new file mode 100644 index 0000000000000..3c65b78e5291d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) > [getByType](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md) + +## IIndexPatternFieldList.getByType() method + +Signature: + +```typescript +getByType(type: Field['type']): Field[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | Field['type'] | | + +Returns: + +`Field[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.md new file mode 100644 index 0000000000000..47d7c7491aa86 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) + +## IIndexPatternFieldList interface + +Signature: + +```typescript +export interface IIndexPatternFieldList extends Array +``` + +## Methods + +| Method | Description | +| --- | --- | +| [add(field)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.add.md) | | +| [getByName(name)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md) | | +| [getByType(type)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md) | | +| [remove(field)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.remove.md) | | +| [update(field)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.update.md) | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.remove.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.remove.md new file mode 100644 index 0000000000000..3b6bbb0691930 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.remove.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) > [remove](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.remove.md) + +## IIndexPatternFieldList.remove() method + +Signature: + +```typescript +remove(field: IFieldType): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | IFieldType | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.update.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.update.md new file mode 100644 index 0000000000000..121ffb65f26a5 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.update.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) > [update](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.update.md) + +## IIndexPatternFieldList.update() method + +Signature: + +```typescript +update(field: FieldSpec): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | FieldSpec | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md index fcd682340eb53..9a93148e4a466 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md @@ -7,5 +7,5 @@ Signature: ```typescript -fields: IFieldList; +fields: IIndexPatternFieldList; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md index 35075e19dcaf6..21a155ba977c9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md @@ -21,7 +21,7 @@ export declare class IndexPattern implements IIndexPattern | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md) | | any | | -| [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | IFieldList | | +| [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | IIndexPatternFieldList | | | [fieldsFetcher](./kibana-plugin-plugins-data-public.indexpattern.fieldsfetcher.md) | | any | | | [flattenHit](./kibana-plugin-plugins-data-public.indexpattern.flattenhit.md) | | any | | | [formatField](./kibana-plugin-plugins-data-public.indexpattern.formatfield.md) | | any | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.__spec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.__spec.md new file mode 100644 index 0000000000000..f52a3324af36f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.__spec.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [$$spec](./kibana-plugin-plugins-data-public.indexpatternfield.__spec.md) + +## IndexPatternField.$$spec property + +Signature: + +```typescript +$$spec: FieldSpec; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md similarity index 55% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field._constructor_.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md index c3b2ac8d30b5a..8ee9acc684fb1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.field._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md @@ -1,15 +1,15 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Field](./kibana-plugin-plugins-data-public.field.md) > [(constructor)](./kibana-plugin-plugins-data-public.field._constructor_.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [(constructor)](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) -## Field.(constructor) +## IndexPatternField.(constructor) Constructs a new instance of the `Field` class Signature: ```typescript -constructor(indexPattern: IndexPattern, spec: FieldSpec | Field, shortDotsEnable?: boolean); +constructor(indexPattern: IndexPattern, spec: FieldSpec | Field, shortDotsEnable: boolean, { fieldFormats, toastNotifications }: FieldDependencies); ``` ## Parameters @@ -19,4 +19,5 @@ constructor(indexPattern: IndexPattern, spec: FieldSpec | Field, shortDotsEnable | indexPattern | IndexPattern | | | spec | FieldSpec | Field | | | shortDotsEnable | boolean | | +| { fieldFormats, toastNotifications } | FieldDependencies | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md new file mode 100644 index 0000000000000..267c8f786b5dd --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [aggregatable](./kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md) + +## IndexPatternField.aggregatable property + +Signature: + +```typescript +aggregatable?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md new file mode 100644 index 0000000000000..ca2552aeb1b42 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) + +## IndexPatternField.conflictDescriptions property + +Signature: + +```typescript +conflictDescriptions?: Record; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md new file mode 100644 index 0000000000000..8e848276f21c4 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [count](./kibana-plugin-plugins-data-public.indexpatternfield.count.md) + +## IndexPatternField.count property + +Signature: + +```typescript +count?: number; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.displayname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.displayname.md new file mode 100644 index 0000000000000..ed9630f92fc97 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.displayname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [displayName](./kibana-plugin-plugins-data-public.indexpatternfield.displayname.md) + +## IndexPatternField.displayName property + +Signature: + +```typescript +displayName?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.estypes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.estypes.md new file mode 100644 index 0000000000000..dec74df099d43 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.estypes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) + +## IndexPatternField.esTypes property + +Signature: + +```typescript +esTypes?: string[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.filterable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.filterable.md new file mode 100644 index 0000000000000..4290c4a2f86b3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.filterable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) + +## IndexPatternField.filterable property + +Signature: + +```typescript +filterable?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.format.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.format.md new file mode 100644 index 0000000000000..d5df8ed628cb0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.format.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [format](./kibana-plugin-plugins-data-public.indexpatternfield.format.md) + +## IndexPatternField.format property + +Signature: + +```typescript +format: any; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md new file mode 100644 index 0000000000000..d1a1ee0905c6e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) + +## IndexPatternField.indexPattern property + +Signature: + +```typescript +indexPattern?: IndexPattern; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md new file mode 100644 index 0000000000000..f731be8f613cf --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) + +## IndexPatternField.lang property + +Signature: + +```typescript +lang?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md new file mode 100644 index 0000000000000..a62cee7b654fe --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md @@ -0,0 +1,41 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) + +## IndexPatternField class + +Signature: + +```typescript +export declare class Field implements IFieldType +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(indexPattern, spec, shortDotsEnable, { fieldFormats, toastNotifications })](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) | | Constructs a new instance of the Field class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [$$spec](./kibana-plugin-plugins-data-public.indexpatternfield.__spec.md) | | FieldSpec | | +| [aggregatable](./kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md) | | boolean | | +| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | Record<string, string[]> | | +| [count](./kibana-plugin-plugins-data-public.indexpatternfield.count.md) | | number | | +| [displayName](./kibana-plugin-plugins-data-public.indexpatternfield.displayname.md) | | string | | +| [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[] | | +| [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | boolean | | +| [format](./kibana-plugin-plugins-data-public.indexpatternfield.format.md) | | any | | +| [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) | | IndexPattern | | +| [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string | | +| [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) | | string | | +| [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | string | | +| [scripted](./kibana-plugin-plugins-data-public.indexpatternfield.scripted.md) | | boolean | | +| [searchable](./kibana-plugin-plugins-data-public.indexpatternfield.searchable.md) | | boolean | | +| [sortable](./kibana-plugin-plugins-data-public.indexpatternfield.sortable.md) | | boolean | | +| [subType](./kibana-plugin-plugins-data-public.indexpatternfield.subtype.md) | | IFieldSubType | | +| [type](./kibana-plugin-plugins-data-public.indexpatternfield.type.md) | | string | | +| [visualizable](./kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md) | | boolean | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.name.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.name.md new file mode 100644 index 0000000000000..cb24621e73209 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) + +## IndexPatternField.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md new file mode 100644 index 0000000000000..132ba25a47637 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) + +## IndexPatternField.script property + +Signature: + +```typescript +script?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.scripted.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.scripted.md new file mode 100644 index 0000000000000..1dd6bc865a75d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.scripted.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [scripted](./kibana-plugin-plugins-data-public.indexpatternfield.scripted.md) + +## IndexPatternField.scripted property + +Signature: + +```typescript +scripted?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.searchable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.searchable.md new file mode 100644 index 0000000000000..42f984d851435 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.searchable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [searchable](./kibana-plugin-plugins-data-public.indexpatternfield.searchable.md) + +## IndexPatternField.searchable property + +Signature: + +```typescript +searchable?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.sortable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.sortable.md new file mode 100644 index 0000000000000..72d225185140b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.sortable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [sortable](./kibana-plugin-plugins-data-public.indexpatternfield.sortable.md) + +## IndexPatternField.sortable property + +Signature: + +```typescript +sortable?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md new file mode 100644 index 0000000000000..2d807f8a5739c --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [subType](./kibana-plugin-plugins-data-public.indexpatternfield.subtype.md) + +## IndexPatternField.subType property + +Signature: + +```typescript +subType?: IFieldSubType; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.type.md new file mode 100644 index 0000000000000..c8483c9b83c9a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [type](./kibana-plugin-plugins-data-public.indexpatternfield.type.md) + +## IndexPatternField.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md new file mode 100644 index 0000000000000..dd661ae779c11 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [visualizable](./kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md) + +## IndexPatternField.visualizable property + +Signature: + +```typescript +visualizable?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist._constructor_.md deleted file mode 100644 index 2207107db8b2b..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist._constructor_.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) > [(constructor)](./kibana-plugin-plugins-data-public.indexpatternfieldlist._constructor_.md) - -## IndexPatternFieldList.(constructor) - -Constructs a new instance of the `FieldList` class - -Signature: - -```typescript -constructor(indexPattern: IndexPattern, specs?: FieldSpec[], shortDotsEnable?: boolean); -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| indexPattern | IndexPattern | | -| specs | FieldSpec[] | | -| shortDotsEnable | boolean | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.add.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.add.md deleted file mode 100644 index dce2f38bbcf10..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.add.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) > [add](./kibana-plugin-plugins-data-public.indexpatternfieldlist.add.md) - -## IndexPatternFieldList.add property - -Signature: - -```typescript -add: (field: Record) => void; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.getbyname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.getbyname.md deleted file mode 100644 index bf6bc51b60301..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.getbyname.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) > [getByName](./kibana-plugin-plugins-data-public.indexpatternfieldlist.getbyname.md) - -## IndexPatternFieldList.getByName property - -Signature: - -```typescript -getByName: (name: string) => Field | undefined; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.getbytype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.getbytype.md deleted file mode 100644 index 86c5ae32940d4..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.getbytype.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) > [getByType](./kibana-plugin-plugins-data-public.indexpatternfieldlist.getbytype.md) - -## IndexPatternFieldList.getByType property - -Signature: - -```typescript -getByType: (type: string) => any[]; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.md deleted file mode 100644 index 478b73f5f8581..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.md +++ /dev/null @@ -1,28 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) - -## IndexPatternFieldList class - -Signature: - -```typescript -export declare class FieldList extends Array implements IFieldList -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(indexPattern, specs, shortDotsEnable)](./kibana-plugin-plugins-data-public.indexpatternfieldlist._constructor_.md) | | Constructs a new instance of the FieldList class | - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [add](./kibana-plugin-plugins-data-public.indexpatternfieldlist.add.md) | | (field: Record<string, any>) => void | | -| [getByName](./kibana-plugin-plugins-data-public.indexpatternfieldlist.getbyname.md) | | (name: string) => Field | undefined | | -| [getByType](./kibana-plugin-plugins-data-public.indexpatternfieldlist.getbytype.md) | | (type: string) => any[] | | -| [remove](./kibana-plugin-plugins-data-public.indexpatternfieldlist.remove.md) | | (field: IFieldType) => void | | -| [update](./kibana-plugin-plugins-data-public.indexpatternfieldlist.update.md) | | (field: Record<string, any>) => void | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.remove.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.remove.md deleted file mode 100644 index 1f2e0883d272e..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.remove.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) > [remove](./kibana-plugin-plugins-data-public.indexpatternfieldlist.remove.md) - -## IndexPatternFieldList.remove property - -Signature: - -```typescript -remove: (field: IFieldType) => void; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.update.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.update.md deleted file mode 100644 index d5156ed41e493..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfieldlist.update.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) > [update](./kibana-plugin-plugins-data-public.indexpatternfieldlist.update.md) - -## IndexPatternFieldList.update property - -Signature: - -```typescript -update: (field: Record) => void; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 13e38ba5e6e5d..8b58957b9044a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -9,11 +9,10 @@ | Class | Description | | --- | --- | | [AggParamType](./kibana-plugin-plugins-data-public.aggparamtype.md) | | -| [Field](./kibana-plugin-plugins-data-public.field.md) | | | [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) | | | [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) | | | [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) | | -| [IndexPatternFieldList](./kibana-plugin-plugins-data-public.indexpatternfieldlist.md) | | +| [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) | | | [IndexPatternSelect](./kibana-plugin-plugins-data-public.indexpatternselect.md) | | | [OptionedParamType](./kibana-plugin-plugins-data-public.optionedparamtype.md) | | | [Plugin](./kibana-plugin-plugins-data-public.plugin.md) | | @@ -61,6 +60,7 @@ | [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) | | | [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) | | +| [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) | | | [IKibanaSearchRequest](./kibana-plugin-plugins-data-public.ikibanasearchrequest.md) | | | [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) | | | [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) | Use data plugin interface instead | @@ -103,6 +103,7 @@ | [esQuery](./kibana-plugin-plugins-data-public.esquery.md) | | | [fieldFormats](./kibana-plugin-plugins-data-public.fieldformats.md) | | | [FilterBar](./kibana-plugin-plugins-data-public.filterbar.md) | | +| [getIndexPatternFieldListCreator](./kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md) | | | [getKbnTypeNames](./kibana-plugin-plugins-data-public.getkbntypenames.md) | Get the esTypes known by all kbnFieldTypes {Array} | | [indexPatterns](./kibana-plugin-plugins-data-public.indexpatterns.md) | | | [QueryStringInput](./kibana-plugin-plugins-data-public.querystringinput.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md index d0d4cc491e142..58690300b3bd6 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md @@ -7,5 +7,5 @@ Signature: ```typescript -QueryStringInput: React.FC> +QueryStringInput: React.FC> ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md index a0b879673e553..0d5e0e42af27f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md @@ -7,7 +7,7 @@ Signature: ```typescript -SearchBar: React.ComponentClass, "query" | "isLoading" | "filters" | "indexPatterns" | "refreshInterval" | "screenTitle" | "dataTestSubj" | "customSubmitButton" | "showQueryBar" | "showQueryInput" | "showFilterBar" | "showDatePicker" | "showAutoRefreshOnly" | "isRefreshPaused" | "dateRangeFrom" | "dateRangeTo" | "showSaveQuery" | "savedQuery" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated" | "onClearSavedQuery" | "onRefresh" | "timeHistory" | "onFiltersUpdated" | "onRefreshChange">, any> & { - WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; +SearchBar: React.ComponentClass, "query" | "isLoading" | "filters" | "indexPatterns" | "refreshInterval" | "customSubmitButton" | "screenTitle" | "dataTestSubj" | "showQueryBar" | "showQueryInput" | "showFilterBar" | "showDatePicker" | "showAutoRefreshOnly" | "isRefreshPaused" | "dateRangeFrom" | "dateRangeTo" | "showSaveQuery" | "savedQuery" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated" | "onClearSavedQuery" | "onRefresh" | "timeHistory" | "onFiltersUpdated" | "onRefreshChange">, any> & { + WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; } ``` diff --git a/docs/discover/images/autorefresh-interval.png b/docs/discover/images/autorefresh-interval.png new file mode 100644 index 0000000000000..a9f72a1cb73cd Binary files /dev/null and b/docs/discover/images/autorefresh-interval.png differ diff --git a/docs/discover/search.asciidoc b/docs/discover/search.asciidoc index 21ae4560fba94..9fe35f0302760 100644 --- a/docs/discover/search.asciidoc +++ b/docs/discover/search.asciidoc @@ -1,25 +1,53 @@ [[search]] == Searching your data -You can search the indices that match the current <> by entering -your search criteria in the Query bar. By default you can use Kibana's <> -which features autocomplete and a simple, easy to use syntax. Kibana's legacy query -language (based on Lucene https://lucene.apache.org/core/2_9_4/queryparsersyntax.html[query syntax]) -is still available for the time being under the options menu in the Query Bar. When this -legacy query language is selected, the full JSON-based {ref}/query-dsl.html[Elasticsearch Query DSL] -can also be used. - -When you submit a search request, the histogram, Documents table, and Fields -list are updated to reflect the search results. The total number of hits -(matching documents) is shown in the toolbar. The Documents table shows the -first five hundred hits. By default, the hits are listed in reverse -chronological order, with the newest documents shown first. You can reverse -the sort order by clicking the Time column header. You can also sort the table -by the values in any indexed field. For more information, see <>. - -To search your data, enter your search criteria in the Query bar and -press *Enter* or click *Search* image:images/search-button.jpg[] to submit -the request to Elasticsearch. +Many Kibana apps embed a query bar for real-time search, including +*Discover*, *Visualize*, and *Dashboard*. + +[float] +=== Search your data + +To search the indices that match the current <>, +enter your search criteria in the query bar. By default, you'll use +{kib}'s <> (KQL), which +features autocomplete and a simple, easy-to-use syntax. If you prefer to use +{kib}'s legacy query +language, based on the +Lucene https://lucene.apache.org/core/2_9_4/queryparsersyntax.html[query syntax], +you can switch to it from the KQL popup in the query bar. When you enable the +legacy query language, you can use the full +JSON-based {ref}/query-dsl.html[Elasticsearch Query DSL]. + + +[float] +[[autorefresh]] +=== Refresh search results +As more documents are added to the indices you're searching, the search results +shown in *Discover*, and used to display visualizations, get stale. Using the +time filter, you can +configure a refresh interval to periodically resubmit your searches to +retrieve the latest results. + +[role="screenshot"] +image::images/autorefresh-interval.png[] + +You can also manually refresh the search results by +clicking the *Refresh* button. + +[float] +=== Searching large amounts of data + +Sometimes you want to search through large amounts of data no matter how long +the search takes. While this might not happen often, there are times +that long-running queries are required. Consider a threat hunting scenario +where you need to search through years of data. + +If you run a query, and the run time gets close to the +timeout, you're presented the option to ignore the timeout. This enables you to +run queries with large amounts of data to completion. + +By default, a query times out after 30 seconds. +The timeout is in place to avoid unintentional load on the cluster. + include::kuery.asciidoc[] @@ -160,31 +188,3 @@ To completely delete a query: image::discover/images/saved-query-management-component-delete-query-button.png["Example of the saved query management popover when a query is hovered over and we are about to delete a query",width="80%"] You can import, export, and delete saved queries from <>. - -[[select-pattern]] -=== Change the indices you're searching -When you submit a search request, the indices that match the currently-selected -index pattern are searched. -To change the indices you are searching, click the index pattern and select a -different <>. - -[[autorefresh]] -=== Refresh the search results -As more documents are added to the indices you're searching, the search results -shown in Discover and used to display visualizations get stale. You can -configure a refresh interval to periodically resubmit your searches to -retrieve the latest results. - -. Click image:images/time-filter-calendar.png[]. - -. In the *Refresh every* field, enter the refresh rate, then select the interval - from the dropdown. - -. Click *Start*. -+ -image::images/autorefresh-intervals.png[] - -To disable auto refresh, click *Stop*. - -If auto refresh is not enabled, click *Refresh* to manually refresh the search -results. diff --git a/docs/images/autorefresh-intervals.png b/docs/images/autorefresh-intervals.png index a79ae2f1f6c46..49be46fefd4aa 100644 Binary files a/docs/images/autorefresh-intervals.png and b/docs/images/autorefresh-intervals.png differ diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 51910169e8673..cafd50d92376f 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -68,6 +68,9 @@ into the document when displaying it. `metrics:max_buckets`:: The maximum numbers of buckets that a single data source can return. This might arise when the user selects a short interval (for example, 1s) for a long time period (1 year). +`pageNavigation`:: The style of navigation menu for Kibana. +Choices are Legacy, the legacy style where every plugin is represented in the nav, +and Modern, a new format that bundles related plugins together in flyaway nested navigation. `query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character in a query clause. Only applies when experimental query features are enabled in the query bar. To disallow leading wildcards in Lucene queries, diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index a5503969a3ec1..85d580de9475f 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -70,3 +70,13 @@ This page has moved. Please see <>. == Maps This page has moved. Please see <>. + +[role="exclude",id="development-embedding-visualizations"] +== Embedding Visualizations + +This page was deleted. See <>. + +[role="exclude",id="development-create-visualization"] +== Developing Visualizations + +This page was deleted. See <>. diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index d4dbe9407b7a9..547b4fdedcec6 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -5,7 +5,7 @@ Alerting and action settings ++++ -Alerts and actions are enabled by default in {kib}, but require you configure the following in order to use them: +Alerts and actions are enabled by default in {kib}, but require you configure the following in order to use them: . <>. . <>. @@ -18,27 +18,36 @@ You can configure the following settings in the `kibana.yml` file. [[general-alert-action-settings]] ==== General settings -`xpack.encryptedSavedObjects.encryptionKey`:: +[cols="2*<"] +|=== -A string of 32 or more characters used to encrypt sensitive properties on alerts and actions before they're stored in {es}. Third party credentials — such as the username and password used to connect to an SMTP service — are an example of encrypted properties. -+ -If not set, {kib} will generate a random key on startup, but all alert and action functions will be blocked. Generated keys are not allowed for alerts and actions because when a new key is generated on restart, existing encrypted data becomes inaccessible. For the same reason, alerts and actions in high-availability deployments of {kib} will behave unexpectedly if the key isn't the same on all instances of {kib}. -+ -Although the key can be specified in clear text in `kibana.yml`, it's recommended to store this key securely in the <>. +| `xpack.encryptedSavedObjects.encryptionKey` + | A string of 32 or more characters used to encrypt sensitive properties on alerts and actions before they're stored in {es}. Third party credentials — such as the username and password used to connect to an SMTP service — are an example of encrypted properties. + + + + If not set, {kib} will generate a random key on startup, but all alert and action functions will be blocked. Generated keys are not allowed for alerts and actions because when a new key is generated on restart, existing encrypted data becomes inaccessible. For the same reason, alerts and actions in high-availability deployments of {kib} will behave unexpectedly if the key isn't the same on all instances of {kib}. + + + + Although the key can be specified in clear text in `kibana.yml`, it's recommended to store this key securely in the <>. + +|=== [float] [[action-settings]] ==== Action settings -`xpack.actions.whitelistedHosts`:: -A list of hostnames that {kib} is allowed to connect to when built-in actions are triggered. It defaults to `[*]`, allowing any host, but keep in mind the potential for SSRF attacks when hosts are not explicitly whitelisted. An empty list `[]` can be used to block built-in actions from making any external connections. -+ -Note that hosts associated with built-in actions, such as Slack and PagerDuty, are not automatically whitelisted. If you are not using the default `[*]` setting, you must ensure that the corresponding endpoints are whitelisted as well. +[cols="2*<"] +|=== + +| `xpack.actions.whitelistedHosts` + | A list of hostnames that {kib} is allowed to connect to when built-in actions are triggered. It defaults to `[*]`, allowing any host, but keep in mind the potential for SSRF attacks when hosts are not explicitly whitelisted. An empty list `[]` can be used to block built-in actions from making any external connections. + + + + Note that hosts associated with built-in actions, such as Slack and PagerDuty, are not automatically whitelisted. If you are not using the default `[*]` setting, you must ensure that the corresponding endpoints are whitelisted as well. + +| `xpack.actions.enabledActionTypes` + | A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, and `.webhook`. An empty list `[]` will disable all action types. + + + + Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. -`xpack.actions.enabledActionTypes`:: -A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, and `.webhook`. An empty list `[]` will disable all action types. -+ -Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. +|=== [float] [[alert-settings]] diff --git a/docs/settings/apm-settings.asciidoc b/docs/settings/apm-settings.asciidoc index fd53c3aeb3605..8844fcd03ae9a 100644 --- a/docs/settings/apm-settings.asciidoc +++ b/docs/settings/apm-settings.asciidoc @@ -38,27 +38,42 @@ If you'd like to change any of the default values, copy and paste the relevant settings into your `kibana.yml` configuration file. Changing these settings may disable features of the APM App. -xpack.apm.enabled:: Set to `false` to disable the APM app. Defaults to `true`. +[cols="2*<"] +|=== +| `xpack.apm.enabled` + | Set to `false` to disable the APM app. Defaults to `true`. -xpack.apm.ui.enabled:: Set to `false` to hide the APM app from the menu. Defaults to `true`. +| `xpack.apm.ui.enabled` + | Set to `false` to hide the APM app from the menu. Defaults to `true`. -xpack.apm.ui.transactionGroupBucketSize:: Number of top transaction groups displayed in the APM app. Defaults to `100`. +| `xpack.apm.ui.transactionGroupBucketSize` + | Number of top transaction groups displayed in the APM app. Defaults to `100`. -xpack.apm.ui.maxTraceItems:: Maximum number of child items displayed when viewing trace details. Defaults to `1000`. +| `xpack.apm.ui.maxTraceItems` + | Maximum number of child items displayed when viewing trace details. Defaults to `1000`. -apm_oss.indexPattern:: The index pattern used for integrations with Machine Learning and Query Bar. -It must match all apm indices. Defaults to `apm-*`. +| `apm_oss.indexPattern` + | The index pattern used for integrations with Machine Learning and Query Bar. + It must match all apm indices. Defaults to `apm-*`. -apm_oss.errorIndices:: Matcher for all {apm-server-ref}/error-indices.html[error indices]. Defaults to `apm-*`. +| `apm_oss.errorIndices` + | Matcher for all {apm-server-ref}/error-indices.html[error indices]. Defaults to `apm-*`. -apm_oss.onboardingIndices:: Matcher for all onboarding indices. Defaults to `apm-*`. +| `apm_oss.onboardingIndices` + | Matcher for all onboarding indices. Defaults to `apm-*`. -apm_oss.spanIndices:: Matcher for all {apm-server-ref}/span-indices.html[span indices]. Defaults to `apm-*`. +| `apm_oss.spanIndices` + | Matcher for all {apm-server-ref}/span-indices.html[span indices]. Defaults to `apm-*`. -apm_oss.transactionIndices:: Matcher for all {apm-server-ref}/transaction-indices.html[transaction indices]. Defaults to `apm-*`. +| `apm_oss.transactionIndices` + | Matcher for all {apm-server-ref}/transaction-indices.html[transaction indices]. Defaults to `apm-*`. -apm_oss.metricsIndices:: Matcher for all {apm-server-ref}/metricset-indices.html[metrics indices]. Defaults to `apm-*`. +| `apm_oss.metricsIndices` + | Matcher for all {apm-server-ref}/metricset-indices.html[metrics indices]. Defaults to `apm-*`. -apm_oss.sourcemapIndices:: Matcher for all {apm-server-ref}/sourcemap-indices.html[source map indices]. Defaults to `apm-*`. +| `apm_oss.sourcemapIndices` + | Matcher for all {apm-server-ref}/sourcemap-indices.html[source map indices]. Defaults to `apm-*`. + +|=== // end::general-apm-settings[] diff --git a/docs/settings/dev-settings.asciidoc b/docs/settings/dev-settings.asciidoc index 436f169b82ca3..c43b96a8668e0 100644 --- a/docs/settings/dev-settings.asciidoc +++ b/docs/settings/dev-settings.asciidoc @@ -12,12 +12,20 @@ They are enabled by default. [[grok-settings]] ==== Grok Debugger settings -`xpack.grokdebugger.enabled`:: -Set to `true` (default) to enable the <>. +[cols="2*<"] +|=== +| `xpack.grokdebugger.enabled` + | Set to `true` to enable the <>. Defaults to `true`. + +|=== [float] [[profiler-settings]] ==== {searchprofiler} Settings -`xpack.searchprofiler.enabled`:: -Set to `true` (default) to enable the <>. +[cols="2*<"] +|=== +| `xpack.searchprofiler.enabled` + | Set to `true` to enable the <>. Defaults to `true`. + +|=== diff --git a/docs/settings/general-infra-logs-ui-settings.asciidoc b/docs/settings/general-infra-logs-ui-settings.asciidoc index 7b32372a1f59a..2a9d4df1ff43c 100644 --- a/docs/settings/general-infra-logs-ui-settings.asciidoc +++ b/docs/settings/general-infra-logs-ui-settings.asciidoc @@ -1,17 +1,30 @@ -`xpack.infra.enabled`:: Set to `false` to disable the Logs and Metrics app plugin {kib}. Defaults to `true`. +[cols="2*<"] +|=== +| `xpack.infra.enabled` + | Set to `false` to disable the Logs and Metrics app plugin {kib}. Defaults to `true`. -`xpack.infra.sources.default.logAlias`:: Index pattern for matching indices that contain log data. Defaults to `filebeat-*,kibana_sample_data_logs*`. To match multiple wildcard patterns, use a comma to separate the names, with no space after the comma. For example, `logstash-app1-*,default-logs-*`. +| `xpack.infra.sources.default.logAlias` + | Index pattern for matching indices that contain log data. Defaults to `filebeat-*,kibana_sample_data_logs*`. To match multiple wildcard patterns, use a comma to separate the names, with no space after the comma. For example, `logstash-app1-*,default-logs-*`. -`xpack.infra.sources.default.metricAlias`:: Index pattern for matching indices that contain Metricbeat data. Defaults to `metricbeat-*`. To match multiple wildcard patterns, use a comma to separate the names, with no space after the comma. For example, `logstash-app1-*,default-logs-*`. +| `xpack.infra.sources.default.metricAlias` + | Index pattern for matching indices that contain Metricbeat data. Defaults to `metricbeat-*`. To match multiple wildcard patterns, use a comma to separate the names, with no space after the comma. For example, `logstash-app1-*,default-logs-*`. -`xpack.infra.sources.default.fields.timestamp`:: Timestamp used to sort log entries. Defaults to `@timestamp`. +| `xpack.infra.sources.default.fields.timestamp` + | Timestamp used to sort log entries. Defaults to `@timestamp`. -`xpack.infra.sources.default.fields.message`:: Fields used to display messages in the Logs app. Defaults to `['message', '@message']`. +| `xpack.infra.sources.default.fields.message` + | Fields used to display messages in the Logs app. Defaults to `['message', '@message']`. -`xpack.infra.sources.default.fields.tiebreaker`:: Field used to break ties between two entries with the same timestamp. Defaults to `_doc`. +| `xpack.infra.sources.default.fields.tiebreaker` + | Field used to break ties between two entries with the same timestamp. Defaults to `_doc`. -`xpack.infra.sources.default.fields.host`:: Field used to identify hosts. Defaults to `host.name`. +| `xpack.infra.sources.default.fields.host` + | Field used to identify hosts. Defaults to `host.name`. -`xpack.infra.sources.default.fields.container`:: Field used to identify Docker containers. Defaults to `container.id`. +| `xpack.infra.sources.default.fields.container` + | Field used to identify Docker containers. Defaults to `container.id`. -`xpack.infra.sources.default.fields.pod`:: Field used to identify Kubernetes pods. Defaults to `kubernetes.pod.uid`. +| `xpack.infra.sources.default.fields.pod` + | Field used to identify Kubernetes pods. Defaults to `kubernetes.pod.uid`. + +|=== diff --git a/docs/settings/graph-settings.asciidoc b/docs/settings/graph-settings.asciidoc index 7e597362b1cfc..8ccff21a26f74 100644 --- a/docs/settings/graph-settings.asciidoc +++ b/docs/settings/graph-settings.asciidoc @@ -10,5 +10,10 @@ You do not need to configure any settings to use the {graph-features}. [float] [[general-graph-settings]] ==== General graph settings -`xpack.graph.enabled`:: -Set to `false` to disable the {graph-features}. + +[cols="2*<"] +|=== +| `xpack.graph.enabled` + | Set to `false` to disable the {graph-features}. + +|=== diff --git a/docs/settings/i18n-settings.asciidoc b/docs/settings/i18n-settings.asciidoc index 4fe466bcb4580..6d92e74f17cb2 100644 --- a/docs/settings/i18n-settings.asciidoc +++ b/docs/settings/i18n-settings.asciidoc @@ -9,10 +9,7 @@ You do not need to configure any settings to run Kibana in English. ==== General i18n Settings `i18n.locale`:: -Kibana currently supports the following locales: -+ -- English - `en` (default) -- Chinese - `zh-CN` -- Japanese - `ja-JP` - - + {kib} supports the following locales: + * English - `en` (default) + * Chinese - `zh-CN` + * Japanese - `ja-JP` diff --git a/docs/settings/ml-settings.asciidoc b/docs/settings/ml-settings.asciidoc index 36578c909f513..24e38e73bca9b 100644 --- a/docs/settings/ml-settings.asciidoc +++ b/docs/settings/ml-settings.asciidoc @@ -11,12 +11,25 @@ enabled by default. [[general-ml-settings-kb]] ==== General {ml} settings -`xpack.ml.enabled`:: -Set to `true` (default) to enable {kib} {ml-features}. + -+ -If set to `false` in `kibana.yml`, the {ml} icon is hidden in this {kib} -instance. If `xpack.ml.enabled` is set to `true` in `elasticsearch.yml`, however, -you can still use the {ml} APIs. To disable {ml} entirely, see the -{ref}/ml-settings.html[{es} {ml} settings]. +[cols="2*<"] +|=== +| `xpack.ml.enabled` + | Set to `true` (default) to enable {kib} {ml-features}. + + + + If set to `false` in `kibana.yml`, the {ml} icon is hidden in this {kib} + instance. If `xpack.ml.enabled` is set to `true` in `elasticsearch.yml`, however, + you can still use the {ml} APIs. To disable {ml} entirely, see the + {ref}/ml-settings.html[{es} {ml} settings]. +|=== +[[data-visualizer-settings]] +==== {data-viz} settings + +[cols="2*<"] +|=== +| `xpack.ml.file_data_visualizer.max_file_size` + | Sets the file size limit when importing data in the {data-viz}. The default + value is `100MB`. The highest supported value for this setting is `1GB`. + +|=== diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 6645f49029a51..f180f2c3ecc97 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -29,45 +29,49 @@ For more information, see [[monitoring-general-settings]] ==== General monitoring settings -`monitoring.enabled`:: -Set to `true` (default) to enable the {monitor-features} in {kib}. Unlike the -`monitoring.ui.enabled` setting, when this setting is `false`, the -monitoring back-end does not run and {kib} stats are not sent to the monitoring -cluster. - -`monitoring.ui.elasticsearch.hosts`:: -Specifies the location of the {es} cluster where your monitoring data is stored. -By default, this is the same as `elasticsearch.hosts`. This setting enables -you to use a single {kib} instance to search and visualize data in your -production cluster as well as monitor data sent to a dedicated monitoring -cluster. - -`monitoring.ui.elasticsearch.username`:: -Specifies the username used by {kib} monitoring to establish a persistent connection -in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} -monitoring cluster. - -Every other request performed by the Stack Monitoring UI to the monitoring {es} -cluster uses the authenticated user's credentials, which must be the same on -both the {es} monitoring cluster and the {es} production cluster. - -If not set, {kib} uses the value of the `elasticsearch.username` setting. - -`monitoring.ui.elasticsearch.password`:: -Specifies the password used by {kib} monitoring to establish a persistent connection -in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} -monitoring cluster. - -Every other request performed by the Stack Monitoring UI to the monitoring {es} -cluster uses the authenticated user's credentials, which must be the same on -both the {es} monitoring cluster and the {es} production cluster. - -If not set, {kib} uses the value of the `elasticsearch.password` setting. - -`monitoring.ui.elasticsearch.pingTimeout`:: -Specifies the time in milliseconds to wait for {es} to respond to internal -health checks. By default, it matches the `elasticsearch.pingTimeout` setting, -which has a default value of `30000`. +[cols="2*<"] +|=== +| `monitoring.enabled` + | Set to `true` (default) to enable the {monitor-features} in {kib}. Unlike the + `monitoring.ui.enabled` setting, when this setting is `false`, the + monitoring back-end does not run and {kib} stats are not sent to the monitoring + cluster. + +| `monitoring.ui.elasticsearch.hosts` + | Specifies the location of the {es} cluster where your monitoring data is stored. + By default, this is the same as `elasticsearch.hosts`. This setting enables + you to use a single {kib} instance to search and visualize data in your + production cluster as well as monitor data sent to a dedicated monitoring + cluster. + +| `monitoring.ui.elasticsearch.username` + | Specifies the username used by {kib} monitoring to establish a persistent connection + in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} + monitoring cluster. + + + + Every other request performed by the Stack Monitoring UI to the monitoring {es} + cluster uses the authenticated user's credentials, which must be the same on + both the {es} monitoring cluster and the {es} production cluster. + + + + If not set, {kib} uses the value of the `elasticsearch.username` setting. + +| `monitoring.ui.elasticsearch.password` + | Specifies the password used by {kib} monitoring to establish a persistent connection + in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} + monitoring cluster. + + + + Every other request performed by the Stack Monitoring UI to the monitoring {es} + cluster uses the authenticated user's credentials, which must be the same on + both the {es} monitoring cluster and the {es} production cluster. + + + + If not set, {kib} uses the value of the `elasticsearch.password` setting. + +| `monitoring.ui.elasticsearch.pingTimeout` + | Specifies the time in milliseconds to wait for {es} to respond to internal + health checks. By default, it matches the `elasticsearch.pingTimeout` setting, + which has a default value of `30000`. + +|=== [float] [[monitoring-collection-settings]] @@ -75,15 +79,18 @@ which has a default value of `30000`. These settings control how data is collected from {kib}. -`monitoring.kibana.collection.enabled`:: -Set to `true` (default) to enable data collection from the {kib} NodeJS server -for {kib} Dashboards to be featured in the Monitoring. +[cols="2*<"] +|=== +| `monitoring.kibana.collection.enabled` + | Set to `true` (default) to enable data collection from the {kib} NodeJS server + for {kib} Dashboards to be featured in the Monitoring. -`monitoring.kibana.collection.interval`:: -Specifies the number of milliseconds to wait in between data sampling on the -{kib} NodeJS server for the metrics that are displayed in the {kib} dashboards. -Defaults to `10000` (10 seconds). +| `monitoring.kibana.collection.interval` + | Specifies the number of milliseconds to wait in between data sampling on the + {kib} NodeJS server for the metrics that are displayed in the {kib} dashboards. + Defaults to `10000` (10 seconds). +|=== [float] [[monitoring-ui-settings]] @@ -94,27 +101,31 @@ However, the defaults work best in most circumstances. For more information about configuring {kib}, see {kibana-ref}/settings.html[Setting Kibana Server Properties]. -`monitoring.ui.elasticsearch.logFetchCount`:: -Specifies the number of log entries to display in the Monitoring UI. Defaults to -`10`. The maximum value is `50`. +[cols="2*<"] +|=== +| `monitoring.ui.elasticsearch.logFetchCount` + | Specifies the number of log entries to display in the Monitoring UI. Defaults to + `10`. The maximum value is `50`. -`monitoring.ui.max_bucket_size`:: -Specifies the number of term buckets to return out of the overall terms list when -performing terms aggregations to retrieve index and node metrics. For more -information about the `size` parameter, see -{ref}/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-size[Terms Aggregation]. -Defaults to `10000`. +| `monitoring.ui.max_bucket_size` + | Specifies the number of term buckets to return out of the overall terms list when + performing terms aggregations to retrieve index and node metrics. For more + information about the `size` parameter, see + {ref}/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-size[Terms Aggregation]. + Defaults to `10000`. -`monitoring.ui.min_interval_seconds`:: -Specifies the minimum number of seconds that a time bucket in a chart can -represent. Defaults to 10. If you modify the -`monitoring.ui.collection.interval` in `elasticsearch.yml`, use the same -value in this setting. +| `monitoring.ui.min_interval_seconds` + | Specifies the minimum number of seconds that a time bucket in a chart can + represent. Defaults to 10. If you modify the + `monitoring.ui.collection.interval` in `elasticsearch.yml`, use the same + value in this setting. -`monitoring.ui.enabled`:: -Set to `false` to hide the Monitoring UI in {kib}. The monitoring back-end -continues to run as an agent for sending {kib} stats to the monitoring -cluster. Defaults to `true`. +| `monitoring.ui.enabled` + | Set to `false` to hide the Monitoring UI in {kib}. The monitoring back-end + continues to run as an agent for sending {kib} stats to the monitoring + cluster. Defaults to `true`. + +|=== [float] [[monitoring-ui-cgroup-settings]] @@ -125,18 +136,20 @@ better decisions about your container performance, rather than guessing based on the overall machine performance. If you are not running your applications in a container, then Cgroup statistics are not useful. -`monitoring.ui.container.elasticsearch.enabled`:: - -For {es} clusters that are running in containers, this setting changes the -*Node Listing* to display the CPU utilization based on the reported Cgroup -statistics. It also adds the calculated Cgroup CPU utilization to the -*Node Overview* page instead of the overall operating system's CPU -utilization. Defaults to `false`. - -`monitoring.ui.container.logstash.enabled`:: - -For {ls} nodes that are running in containers, this setting -changes the {ls} *Node Listing* to display the CPU utilization -based on the reported Cgroup statistics. It also adds the -calculated Cgroup CPU utilization to the {ls} node detail -pages instead of the overall operating system’s CPU utilization. Defaults to `false`. +[cols="2*<"] +|=== +| `monitoring.ui.container.elasticsearch.enabled` + | For {es} clusters that are running in containers, this setting changes the + *Node Listing* to display the CPU utilization based on the reported Cgroup + statistics. It also adds the calculated Cgroup CPU utilization to the + *Node Overview* page instead of the overall operating system's CPU + utilization. Defaults to `false`. + +| `monitoring.ui.container.logstash.enabled` + | For {ls} nodes that are running in containers, this setting + changes the {ls} *Node Listing* to display the CPU utilization + based on the reported Cgroup statistics. It also adds the + calculated Cgroup CPU utilization to the {ls} node detail + pages instead of the overall operating system’s CPU utilization. Defaults to `false`. + +|=== diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 9a45fb9ab1d0c..7c50dbf542d0d 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -14,45 +14,54 @@ You can configure `xpack.reporting` settings in your `kibana.yml` to: [float] [[general-reporting-settings]] ==== General reporting settings -[[xpack-enable-reporting]]`xpack.reporting.enabled`:: -Set to `false` to disable the {report-features}. -`xpack.reporting.encryptionKey`:: -Set to any text string. By default, Kibana will generate a random key when it -starts, which will cause pending reports to fail after restart. Configure this -setting to preserve the same key across multiple restarts and multiple instances of Kibana. +[cols="2*<"] +|=== +| [[xpack-enable-reporting]]`xpack.reporting.enabled` + | Set to `false` to disable the {report-features}. + +| `xpack.reporting.encryptionKey` + | Set to any text string. By default, {kib} will generate a random key when it + starts, which will cause pending reports to fail after restart. Configure this + setting to preserve the same key across multiple restarts and multiple instances of {kib}. + +|=== [float] [[reporting-kibana-server-settings]] -==== Kibana server settings +==== {kib} server settings -Reporting opens the {kib} web interface in a server process to generate -screenshots of {kib} visualizations. In most cases, the default settings -will work and you don't need to configure Reporting to communicate with {kib}. +Reporting opens the {kib} web interface in a server process to generate +screenshots of {kib} visualizations. In most cases, the default settings +will work and you don't need to configure Reporting to communicate with {kib}. However, if your client connections must go through a reverse-proxy -to access {kib}, Reporting configuration must have the proxy port, protocol, +to access {kib}, Reporting configuration must have the proxy port, protocol, and hostname set in the `xpack.reporting.kibanaServer.*` settings. -[NOTE] +[NOTE] ==== -If a reverse-proxy carries encrypted traffic from end-user -clients back to a {kib} server, the proxy port, protocol, and hostname -in Reporting settings must be valid for the encryption that the Reporting -browser will receive. Encrypted communications will fail if there are +If a reverse-proxy carries encrypted traffic from end-user +clients back to a {kib} server, the proxy port, protocol, and hostname +in Reporting settings must be valid for the encryption that the Reporting +browser will receive. Encrypted communications will fail if there are mismatches in the host information between the request and the certificate on the server. Configuring the `xpack.reporting.kibanaServer` settings to point to a -proxy host requires that the Kibana server has network access to the proxy. +proxy host requires that the {kib} server has network access to the proxy. ==== -`xpack.reporting.kibanaServer.port`:: -The port for accessing Kibana, if different from the `server.port` value. +[cols="2*<"] +|=== +| `xpack.reporting.kibanaServer.port` + | The port for accessing {kib}, if different from the `server.port` value. + +| `xpack.reporting.kibanaServer.protocol` + | The protocol for accessing {kib}, typically `http` or `https`. -`xpack.reporting.kibanaServer.protocol`:: -The protocol for accessing Kibana, typically `http` or `https`. +| `xpack.reporting.kibanaServer.hostname` + | The hostname for accessing {kib}, if different from the `server.host` value. -`xpack.reporting.kibanaServer.hostname`:: -The hostname for accessing {kib}, if different from the `server.host` value. +|=== [NOTE] ============ @@ -68,55 +77,67 @@ because, in the Reporting browser, it becomes an automatic redirect to `"0.0.0.0 ==== Background job settings Reporting generates reports in the background and jobs are coordinated using documents -in Elasticsearch. Depending on how often you generate reports and the overall number of +in {es}. Depending on how often you generate reports and the overall number of reports, you might need to change the following settings. -`xpack.reporting.queue.indexInterval`:: -How often the index that stores reporting jobs rolls over to a new index. -Valid values are `year`, `month`, `week`, `day`, and `hour`. Defaults to `week`. +[cols="2*<"] +|=== +| `xpack.reporting.queue.indexInterval` + | How often the index that stores reporting jobs rolls over to a new index. + Valid values are `year`, `month`, `week`, `day`, and `hour`. Defaults to `week`. -`xpack.reporting.queue.pollEnabled`:: -Set to `true` (default) to enable the Kibana instance to to poll the index for -pending jobs and claim them for execution. Setting this to `false` allows the -Kibana instance to only add new jobs to the reporting queue, list jobs, and -provide the downloads to completed report through the UI. +| `xpack.reporting.queue.pollEnabled` + | Set to `true` (default) to enable the {kib} instance to to poll the index for + pending jobs and claim them for execution. Setting this to `false` allows the + {kib} instance to only add new jobs to the reporting queue, list jobs, and + provide the downloads to completed report through the UI. + +|=== [NOTE] ============ -Running multiple instances of Kibana in a cluster for load balancing of +Running multiple instances of {kib} in a cluster for load balancing of reporting requires identical values for `xpack.reporting.encryptionKey` and, if security is enabled, `xpack.security.encryptionKey`. ============ -`xpack.reporting.queue.pollInterval`:: -Specifies the number of milliseconds that the reporting poller waits between polling the -index for any pending Reporting jobs. Defaults to `3000` (3 seconds). +[cols="2*<"] +|=== +| `xpack.reporting.queue.pollInterval` + | Specifies the number of milliseconds that the reporting poller waits between polling the + index for any pending Reporting jobs. Defaults to `3000` (3 seconds). + +| [[xpack-reporting-q-timeout]] `xpack.reporting.queue.timeout` + | How long each worker has to produce a report. If your machine is slow or under + heavy load, you might need to increase this timeout. Specified in milliseconds. + If a Reporting job execution time goes over this time limit, the job will be + marked as a failure and there will not be a download available. + Defaults to `120000` (two minutes). -[[xpack-reporting-q-timeout]]`xpack.reporting.queue.timeout`:: -How long each worker has to produce a report. If your machine is slow or under -heavy load, you might need to increase this timeout. Specified in milliseconds. -If a Reporting job execution time goes over this time limit, the job will be -marked as a failure and there will not be a download available. -Defaults to `120000` (two minutes). +|=== [float] [[reporting-capture-settings]] ==== Capture settings -Reporting works by capturing screenshots from Kibana. The following settings +Reporting works by capturing screenshots from {kib}. The following settings control the capturing process. -`xpack.reporting.capture.timeouts.openUrl`:: -How long to allow the Reporting browser to wait for the initial data of the -Kibana page to load. Defaults to `30000` (30 seconds). +[cols="2*<"] +|=== +| `xpack.reporting.capture.timeouts.openUrl` + | How long to allow the Reporting browser to wait for the initial data of the + {kib} page to load. Defaults to `30000` (30 seconds). + +| `xpack.reporting.capture.timeouts.waitForElements` + | How long to allow the Reporting browser to wait for the visualization panels to + load on the {kib} page. Defaults to `30000` (30 seconds). -`xpack.reporting.capture.timeouts.waitForElements`:: -How long to allow the Reporting browser to wait for the visualization panels to -load on the Kibana page. Defaults to `30000` (30 seconds). +| `xpack.reporting.capture.timeouts.renderComplete` + | How long to allow the Reporting browser to wait for each visualization to + signal that it is done renderings. Defaults to `30000` (30 seconds). -`xpack.reporting.capture.timeouts.renderComplete`:: -How long to allow the Reporting brwoser to wait for each visualization to -signal that it is done renderings. Defaults to `30000` (30 seconds). +|=== [NOTE] ============ @@ -126,20 +147,24 @@ capturing the page with a screenshot. As a result, a download will be available, but there will likely be errors in the visualizations in the report. ============ -`xpack.reporting.capture.maxAttempts`:: -If capturing a report fails for any reason, Kibana will re-attempt othe reporting -job, as many times as this setting. Defaults to `3`. +[cols="2*<"] +|=== +| `xpack.reporting.capture.maxAttempts` + | If capturing a report fails for any reason, {kib} will re-attempt other reporting + job, as many times as this setting. Defaults to `3`. -`xpack.reporting.capture.loadDelay`:: -When visualizations are not evented, this is the amount of time before -taking a screenshot. All visualizations that ship with Kibana are evented, so this -setting should not have much effect. If you are seeing empty images instead of -visualizations, try increasing this value. -Defaults to `3000` (3 seconds). +| `xpack.reporting.capture.loadDelay` + | When visualizations are not evented, this is the amount of time before + taking a screenshot. All visualizations that ship with {kib} are evented, so this + setting should not have much effect. If you are seeing empty images instead of + visualizations, try increasing this value. + Defaults to `3000` (3 seconds). -[[xpack-reporting-browser]]`xpack.reporting.capture.browser.type`:: -Specifies the browser to use to capture screenshots. This setting exists for -backward compatibility. The only valid option is `chromium`. +| [[xpack-reporting-browser]] `xpack.reporting.capture.browser.type` + | Specifies the browser to use to capture screenshots. This setting exists for + backward compatibility. The only valid option is `chromium`. + +|=== [float] [[reporting-chromium-settings]] @@ -147,47 +172,59 @@ backward compatibility. The only valid option is `chromium`. When `xpack.reporting.capture.browser.type` is set to `chromium` (default) you can also specify the following settings. -`xpack.reporting.capture.browser.chromium.disableSandbox`:: -Elastic recommends that you research the feasibility of enabling unprivileged user namespaces. -See Chromium Sandbox for additional information. Defaults to false for all operating systems except Debian, -Red Hat Linux, and CentOS which use true +[cols="2*<"] +|=== +| `xpack.reporting.capture.browser.chromium.disableSandbox` + | It is recommended that you research the feasibility of enabling unprivileged user namespaces. + See Chromium Sandbox for additional information. Defaults to false for all operating systems except Debian, + Red Hat Linux, and CentOS which use true. -`xpack.reporting.capture.browser.chromium.proxy.enabled`:: -Enables the proxy for Chromium to use. When set to `true`, you must also specify the -`xpack.reporting.capture.browser.chromium.proxy.server` setting. -Defaults to `false` +| `xpack.reporting.capture.browser.chromium.proxy.enabled` + | Enables the proxy for Chromium to use. When set to `true`, you must also specify the + `xpack.reporting.capture.browser.chromium.proxy.server` setting. + Defaults to `false`. -`xpack.reporting.capture.browser.chromium.proxy.server`:: -The uri for the proxy server. Providing the username and password for the proxy server via the uri is not supported. +| `xpack.reporting.capture.browser.chromium.proxy.server` + | The uri for the proxy server. Providing the username and password for the proxy server via the uri is not supported. -`xpack.reporting.capture.browser.chromium.proxy.bypass`:: -An array of hosts that should not go through the proxy server and should use a direct connection instead. -Examples of valid entries are "elastic.co", "*.elastic.co", ".elastic.co", ".elastic.co:5601" +| `xpack.reporting.capture.browser.chromium.proxy.bypass` + | An array of hosts that should not go through the proxy server and should use a direct connection instead. + Examples of valid entries are "elastic.co", "*.elastic.co", ".elastic.co", ".elastic.co:5601". +|=== [float] [[reporting-csv-settings]] ==== CSV settings -[[xpack-reporting-csv]]`xpack.reporting.csv.maxSizeBytes`:: -The maximum size of a CSV file before being truncated. This setting exists to prevent -large exports from causing performance and storage issues. -Defaults to `10485760` (10mB) + +[cols="2*<"] +|=== +| [[xpack-reporting-csv]] `xpack.reporting.csv.maxSizeBytes` + | The maximum size of a CSV file before being truncated. This setting exists to prevent + large exports from causing performance and storage issues. + Defaults to `10485760` (10mB). + +|=== [float] [[reporting-advanced-settings]] ==== Advanced settings -`xpack.reporting.index`:: -Reporting uses a weekly index in Elasticsearch to store the reporting job and -the report content. The index is automatically created if it does not already -exist. Configure this to a unique value, beginning with `.reporting-`, for every -Kibana instance that has a unique `kibana.index` setting. Defaults to `.reporting` +[cols="2*<"] +|=== +| `xpack.reporting.index` + | Reporting uses a weekly index in {es} to store the reporting job and + the report content. The index is automatically created if it does not already + exist. Configure this to a unique value, beginning with `.reporting-`, for every + {kib} instance that has a unique `kibana.index` setting. Defaults to `.reporting`. + +| `xpack.reporting.roles.allow` + | Specifies the roles in addition to superusers that can use reporting. + Defaults to `[ "reporting_user" ]`. + -`xpack.reporting.roles.allow`:: -Specifies the roles in addition to superusers that can use reporting. -Defaults to `[ "reporting_user" ]` -+ --- -NOTE: Each user has access to only their own reports. +|=== --- +[NOTE] +============ +Each user has access to only their own reports. +============ diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index 16d68a7759f77..8f6905d643139 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -12,55 +12,83 @@ You do not need to configure any additional settings to use the [[general-security-settings]] ==== General security settings -`xpack.security.enabled`:: -By default, {kib} automatically detects whether to enable the -{security-features} based on the license and whether {es} {security-features} -are enabled. -+ -Do not set this to `false`; it disables the login form, user and role management -screens, and authorization using <>. To disable -{security-features} entirely, see -{ref}/security-settings.html[{es} security settings]. - -`xpack.security.audit.enabled`:: -Set to `true` to enable audit logging for security events. By default, it is set -to `false`. For more details see <>. +[cols="2*<"] +|=== +| `xpack.security.enabled` + | By default, {kib} automatically detects whether to enable the + {security-features} based on the license and whether {es} {security-features} + are enabled. + + + + Do not set this to `false`; it disables the login form, user and role management + screens, and authorization using <>. To disable + {security-features} entirely, see + {ref}/security-settings.html[{es} security settings]. + +| `xpack.security.audit.enabled` + | Set to `true` to enable audit logging for security events. By default, it is set + to `false`. For more details see <>. + +|=== [float] [[security-ui-settings]] ==== User interface security settings -You can configure the following settings in the `kibana.yml` file: - -`xpack.security.cookieName`:: -Sets the name of the cookie used for the session. The default value is `"sid"`. - -`xpack.security.encryptionKey`:: -An arbitrary string of 32 characters or more that is used to encrypt credentials -in a cookie. It is crucial that this key is not exposed to users of {kib}. By -default, a value is automatically generated in memory. If you use that default -behavior, all sessions are invalidated when {kib} restarts. -In addition, high-availability deployments of {kib} will behave unexpectedly -if this setting isn't the same for all instances of {kib}. - -`xpack.security.secureCookies`:: -Sets the `secure` flag of the session cookie. The default value is `false`. It -is automatically set to `true` if `server.ssl.enabled` is set to `true`. Set -this to `true` if SSL is configured outside of {kib} (for example, you are -routing requests through a load balancer or proxy). - -`xpack.security.session.idleTimeout`:: -Sets the session duration. The format is a string of `[ms|s|m|h|d|w|M|Y]` -(e.g. '70ms', '5s', '3d', '1Y'). By default, sessions stay active until the -browser is closed. When this is set to an explicit idle timeout, closing the -browser still requires the user to log back in to {kib}. - -`xpack.security.session.lifespan`:: -Sets the maximum duration, also known as "absolute timeout". The format is a -string of `[ms|s|m|h|d|w|M|Y]` (e.g. '70ms', '5s', '3d', '1Y'). By default, -a session can be renewed indefinitely. When this value is set, a session will end -once its lifespan is exceeded, even if the user is not idle. NOTE: if `idleTimeout` -is not set, this setting will still cause sessions to expire. - -`xpack.security.loginAssistanceMessage`:: -Adds a message to the login screen. Useful for displaying information about maintenance windows, links to corporate sign up pages etc. +You can configure the following settings in the `kibana.yml` file. + +[cols="2*<"] +|=== +| `xpack.security.cookieName` + | Sets the name of the cookie used for the session. The default value is `"sid"`. + +| `xpack.security.encryptionKey` + | An arbitrary string of 32 characters or more that is used to encrypt credentials + in a cookie. It is crucial that this key is not exposed to users of {kib}. By + default, a value is automatically generated in memory. If you use that default + behavior, all sessions are invalidated when {kib} restarts. + In addition, high-availability deployments of {kib} will behave unexpectedly + if this setting isn't the same for all instances of {kib}. + +| `xpack.security.secureCookies` + | Sets the `secure` flag of the session cookie. The default value is `false`. It + is automatically set to `true` if `server.ssl.enabled` is set to `true`. Set + this to `true` if SSL is configured outside of {kib} (for example, you are + routing requests through a load balancer or proxy). + +| `xpack.security.session.idleTimeout` + | Sets the session duration. By default, sessions stay active until the + browser is closed. When this is set to an explicit idle timeout, closing the + browser still requires the user to log back in to {kib}. + +|=== + +[TIP] +============ +The format is a string of `[ms|s|m|h|d|w|M|Y]` +(e.g. '70ms', '5s', '3d', '1Y'). +============ + +[cols="2*<"] +|=== + +| `xpack.security.session.lifespan` + | Sets the maximum duration, also known as "absolute timeout". By default, + a session can be renewed indefinitely. When this value is set, a session will end + once its lifespan is exceeded, even if the user is not idle. NOTE: if `idleTimeout` + is not set, this setting will still cause sessions to expire. + +|=== + +[TIP] +============ +The format is a +string of `[ms|s|m|h|d|w|M|Y]` (e.g. '70ms', '5s', '3d', '1Y'). +============ + +[cols="2*<"] +|=== + +| `xpack.security.loginAssistanceMessage` + | Adds a message to the login screen. Useful for displaying information about maintenance windows, links to corporate sign up pages etc. + +|=== diff --git a/docs/settings/spaces-settings.asciidoc b/docs/settings/spaces-settings.asciidoc index bb0a15b29a087..bda5f00f762cd 100644 --- a/docs/settings/spaces-settings.asciidoc +++ b/docs/settings/spaces-settings.asciidoc @@ -5,18 +5,22 @@ Spaces settings ++++ -By default, Spaces is enabled in Kibana, and you can secure Spaces using +By default, Spaces is enabled in Kibana, and you can secure Spaces using roles when Security is enabled. [float] [[spaces-settings]] ==== Spaces settings -`xpack.spaces.enabled`:: -Set to `true` (default) to enable Spaces in {kib}. +[cols="2*<"] +|=== +| `xpack.spaces.enabled` + | Set to `true` (default) to enable Spaces in {kib}. -`xpack.spaces.maxSpaces`:: -The maximum amount of Spaces that can be used with this instance of Kibana. Some operations -in Kibana return all spaces using a single `_search` from Elasticsearch, so this must be -set lower than the `index.max_result_window` in Elasticsearch. -Defaults to `1000`. \ No newline at end of file +| `xpack.spaces.maxSpaces` + | The maximum amount of Spaces that can be used with this instance of {kib}. Some operations + in {kib} return all spaces using a single `_search` from {es}, so this must be + set lower than the `index.max_result_window` in {es}. + Defaults to `1000`. + +|=== diff --git a/docs/settings/telemetry-settings.asciidoc b/docs/settings/telemetry-settings.asciidoc index ad5f53ad879f8..33f167b13b310 100644 --- a/docs/settings/telemetry-settings.asciidoc +++ b/docs/settings/telemetry-settings.asciidoc @@ -8,7 +8,7 @@ By default, Usage Collection (also known as Telemetry) is enabled. This helps us learn about the {kib} features that our users are most interested in, so we can focus our efforts on making them even better. -You can control whether this data is sent from the {kib} servers, or if it should be sent +You can control whether this data is sent from the {kib} servers, or if it should be sent from the user's browser, in case a firewall is blocking the connections from the server. Additionally, you can decide to completely disable this feature either in the config file or in {kib} via *Management > Kibana > Advanced Settings > Usage Data*. See our https://www.elastic.co/legal/privacy-statement[Privacy Statement] to learn more. @@ -17,22 +17,30 @@ See our https://www.elastic.co/legal/privacy-statement[Privacy Statement] to lea [[telemetry-general-settings]] ==== General telemetry settings -`telemetry.enabled`:: *Default: true*. -Set to `true` to send cluster statistics to Elastic. Reporting your -cluster statistics helps us improve your user experience. Your data is never -shared with anyone. Set to `false` to disable statistics reporting from any -browser connected to the {kib} instance. - -`telemetry.sendUsageFrom`:: *Default: 'browser'*. -Set to `'server'` to report the cluster statistics from the {kib} server. -If the server fails to connect to our endpoint at https://telemetry.elastic.co/, it assumes -it is behind a firewall and falls back to `'browser'` to send it from users' browsers -when they are navigating through {kib}. - -`telemetry.optIn`:: *Default: true*. -Set to `true` to automatically opt into reporting cluster statistics. You can also opt out through -*Advanced Settings* in {kib}. - -`telemetry.allowChangingOptInStatus`:: *Default: true*. -Set to `true` to allow overwriting the `telemetry.optIn` setting via the {kib} UI. -Note: When `false`, `telemetry.optIn` must be `true`. To disable telemetry and not allow users to change that parameter, use `telemetry.enabled`. +[cols="2*<"] +|=== +| `telemetry.enabled` + | Set to `true` to send cluster statistics to Elastic. Reporting your + cluster statistics helps us improve your user experience. Your data is never + shared with anyone. Set to `false` to disable statistics reporting from any + browser connected to the {kib} instance. Defaults to `true`. + +| `telemetry.sendUsageFrom` + | Set to `'server'` to report the cluster statistics from the {kib} server. + If the server fails to connect to our endpoint at https://telemetry.elastic.co/, it assumes + it is behind a firewall and falls back to `'browser'` to send it from users' browsers + when they are navigating through {kib}. Defaults to 'browser'. + +| `telemetry.optIn` + | Set to `true` to automatically opt into reporting cluster statistics. You can also opt out through + *Advanced Settings* in {kib}. Defaults to `true`. + +| `telemetry.allowChangingOptInStatus` + | Set to `true` to allow overwriting the `telemetry.optIn` setting via the {kib} UI. Defaults to `true`. + + +|=== + +[NOTE] +============ +When `false`, `telemetry.optIn` must be `true`. To disable telemetry and not allow users to change that parameter, use `telemetry.enabled`. +============ diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 41fe8d337c03b..cc662af08b8f1 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -1,7 +1,7 @@ [[settings]] -== Configuring Kibana +== Configuring {kib} -The Kibana server reads properties from the `kibana.yml` file on startup. The +The {kib} server reads properties from the `kibana.yml` file on startup. The location of this file differs depending on how you installed {kib}. For example, if you installed {kib} from an archive distribution (`.tar.gz` or `.zip`), by default it is in `$KIBANA_HOME/config`. By default, with package distributions @@ -11,444 +11,622 @@ The default host and port settings configure {kib} to run on `localhost:5601`. T variety of other options. Finally, environment variables can be injected into configuration using `${MY_ENV_VAR}` syntax. -.Kibana configuration settings +[cols="2*<"] +|=== -`console.enabled:`:: *Default: true* Set to false to disable Console. Toggling -this will cause the server to regenerate assets on the next startup, which may -cause a delay before pages start being served. +| `console.enabled:` + | Toggling this causes the server to regenerate assets on the next startup, +which may cause a delay before pages start being served. +Set to `false` to disable Console. *Default: `true`* -`cpu.cgroup.path.override:`:: Override for cgroup cpu path when mounted in a -manner that is inconsistent with `/proc/self/cgroup` +| `cpu.cgroup.path.override:` + | Override for cgroup cpu path when mounted in a +manner that is inconsistent with `/proc/self/cgroup`. -`cpuacct.cgroup.path.override:`:: Override for cgroup cpuacct path when mounted -in a manner that is inconsistent with `/proc/self/cgroup` +| `cpuacct.cgroup.path.override:` + | Override for cgroup cpuacct path when mounted +in a manner that is inconsistent with `/proc/self/cgroup`. -`csp.rules:`:: A template -https://w3c.github.io/webappsec-csp/[content-security-policy] that disables -certain unnecessary and potentially insecure capabilities in the browser. We -strongly recommend that you keep the default CSP rules that ship with Kibana. +| `csp.rules:` + | A https://w3c.github.io/webappsec-csp/[content-security-policy] template +that disables certain unnecessary and potentially insecure capabilities in +the browser. It is strongly recommended that you keep the default CSP rules +that ship with {kib}. -`csp.strict:`:: *Default: `true`* Blocks access to Kibana to any browser that -does not enforce even rudimentary CSP rules. In practice, this will disable +| `csp.strict:` + | Blocks {kib} access to any browser that +does not enforce even rudimentary CSP rules. In practice, this disables support for older, less safe browsers like Internet Explorer. -See <> for more information. - -`csp.warnLegacyBrowsers:`:: *Default: `true`* Shows a warning message after -loading Kibana to any browser that does not enforce even rudimentary CSP rules, -though Kibana is still accessible. This configuration is effectively ignored -when `csp.strict` is enabled. - -`elasticsearch.customHeaders:`:: *Default: `{}`* Header names and values to send -to Elasticsearch. Any custom headers cannot be overwritten by client-side -headers, regardless of the `elasticsearch.requestHeadersWhitelist` configuration. - -`elasticsearch.hosts:`:: *Default: `[ "http://localhost:9200" ]`* The URLs of the {es} instances to use for all your queries. All nodes -listed here must be on the same cluster. +For more information, refer to <>. +*Default: `true`* + +| `csp.warnLegacyBrowsers:` + | Shows a warning message after loading {kib} to any browser that does not +enforce even rudimentary CSP rules, though {kib} is still accessible. This +configuration is effectively ignored when `csp.strict` is enabled. +*Default: `true`* + +| `elasticsearch.customHeaders:` + | Header names and values to send to {es}. Any custom headers cannot be +overwritten by client-side headers, regardless of the +`elasticsearch.requestHeadersWhitelist` configuration. *Default: `{}`* + +| `elasticsearch.hosts:` + | The URLs of the {es} instances to use for all your queries. All nodes +listed here must be on the same cluster. *Default: `[ "http://localhost:9200" ]`* + -To enable SSL/TLS for outbound connections to {es}, use the `https` protocol in this setting. - -`elasticsearch.logQueries:`:: *Default: `false`* Logs queries sent to -Elasticsearch. Requires `logging.verbose` set to `true`. This is useful for -seeing the query DSL generated by applications that currently do not have an -inspector, for example Timelion and Monitoring. - -`elasticsearch.pingTimeout:`:: -*Default: the value of the `elasticsearch.requestTimeout` setting* Time in -milliseconds to wait for Elasticsearch to respond to pings. - -`elasticsearch.preserveHost:`:: *Default: true* When this setting’s value is -true, Kibana uses the hostname specified in the `server.host` setting. When the -value of this setting is `false`, Kibana uses the hostname of the host that -connects to this Kibana instance. - -`elasticsearch.requestHeadersWhitelist:`:: *Default: `[ 'authorization' ]`* List -of Kibana client-side headers to send to Elasticsearch. To send *no* client-side -headers, set this value to [] (an empty list). -Removing the `authorization` header from being whitelisted means that you cannot -use <> in Kibana. - -`elasticsearch.requestTimeout:`:: *Default: 30000* Time in milliseconds to wait -for responses from the back end or Elasticsearch. This value must be a positive -integer. - -`elasticsearch.shardTimeout:`:: *Default: 30000* Time in milliseconds for -Elasticsearch to wait for responses from shards. Set to 0 to disable. - -`elasticsearch.sniffInterval:`:: *Default: false* Time in milliseconds between -requests to check Elasticsearch for an updated list of nodes. - -`elasticsearch.sniffOnStart:`:: *Default: false* Attempt to find other -Elasticsearch nodes on startup. - -`elasticsearch.sniffOnConnectionFault:`:: *Default: false* Update the list of -Elasticsearch nodes immediately following a connection fault. - -`elasticsearch.ssl.alwaysPresentCertificate:`:: *Default: false* Controls {kib}'s behavior in regard to presenting a client certificate when -requested by {es}. This setting applies to all outbound SSL/TLS connections to {es}, including requests that are proxied for end users. -+ -WARNING: If {es} uses certificates to authenticate end users with a PKI realm and `elasticsearch.ssl.alwaysPresentCertificate` is `true`, -proxied requests may be executed as the identity that is tied to the {kib} server. - -`elasticsearch.ssl.certificate:` and `elasticsearch.ssl.key:`:: Paths to a PEM-encoded X.509 client certificate and its corresponding -private key. These are used by {kib} to authenticate itself when making outbound SSL/TLS connections to {es}. For this setting to take -effect, the `xpack.security.http.ssl.client_authentication` setting in {es} must be also be set to `"required"` or `"optional"` to request a -client certificate from {kib}. +To enable SSL/TLS for outbound connections to {es}, use the `https` protocol +in this setting. + +| `elasticsearch.logQueries:` + | Log queries sent to {es}. Requires `logging.verbose` set to `true`. +This is useful for seeing the query DSL generated by applications that +currently do not have an inspector, for example Timelion and Monitoring. +*Default: `false`* + +| `elasticsearch.pingTimeout:` + | Time in milliseconds to wait for {es} to respond to pings. +*Default: the value of the `elasticsearch.requestTimeout` setting* + +| `elasticsearch.preserveHost:` + | When the value is `true`, {kib} uses the hostname specified in the +`server.host` setting. When the value is `false`, {kib} uses +the hostname of the host that connects to this {kib} instance. *Default: `true`* + +| `elasticsearch.requestHeadersWhitelist:` + | List of {kib} client-side headers to send to {es}. To send *no* client-side +headers, set this value to [] (an empty list). Removing the `authorization` +header from being whitelisted means that you cannot use +<> in {kib}. +*Default: `[ 'authorization' ]`* + +| `elasticsearch.requestTimeout:` + | Time in milliseconds to wait for responses from the back end or {es}. +This value must be a positive integer. *Default: `30000`* + +| `elasticsearch.shardTimeout:` + | Time in milliseconds for {es} to wait for responses from shards. +Set to 0 to disable. *Default: `30000`* + +| `elasticsearch.sniffInterval:` + | Time in milliseconds between requests to check {es} for an updated list of +nodes. *Default: `false`* + +| `elasticsearch.sniffOnStart:` + | Attempt to find other {es} nodes on startup. *Default: `false`* + +| `elasticsearch.sniffOnConnectionFault:` + | Update the list of {es} nodes immediately following a connection fault. +*Default: `false`* + +| `elasticsearch.ssl.alwaysPresentCertificate:` + | Controls {kib} behavior in regard to presenting a client certificate when +requested by {es}. This setting applies to all outbound SSL/TLS connections +to {es}, including requests that are proxied for end users. *Default: `false`* + +|=== + +[WARNING] +============ +When {es} uses certificates to authenticate end users with a PKI realm +and `elasticsearch.ssl.alwaysPresentCertificate` is `true`, +proxied requests may be executed as the identity that is tied to the {kib} +server. +============ + +[cols="2*<"] +|=== + +| `elasticsearch.ssl.certificate:` and `elasticsearch.ssl.key:` + | Paths to a PEM-encoded X.509 client certificate and its corresponding +private key. These are used by {kib} to authenticate itself when making +outbound SSL/TLS connections to {es}. For this setting to take effect, the +`xpack.security.http.ssl.client_authentication` setting in {es} must be also +be set to `"required"` or `"optional"` to request a client certificate from +{kib}. + +|=== + +[NOTE] +============ +These settings cannot be used in conjunction with `elasticsearch.ssl.keystore.path`. +============ + +[cols="2*<"] +|=== + +| `elasticsearch.ssl.certificateAuthorities:` + | Paths to one or more PEM-encoded X.509 certificate authority (CA) +certificates, which make up a trusted certificate chain for {es}. This chain is +used by {kib} to establish trust when making outbound SSL/TLS connections to +{es}. + -NOTE: These settings cannot be used in conjunction with `elasticsearch.ssl.keystore.path`. - -`elasticsearch.ssl.certificateAuthorities:`:: Paths to one or more PEM-encoded X.509 certificate authority (CA) certificates which make up a -trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections to {es}. +In addition to this setting, trusted certificates may be specified via +`elasticsearch.ssl.keystore.path` and/or `elasticsearch.ssl.truststore.path`. + +| `elasticsearch.ssl.keyPassphrase:` + | The password that decrypts the private key that is specified +via `elasticsearch.ssl.key`. This value is optional, as the key may not be +encrypted. + +| `elasticsearch.ssl.keystore.path:` + | Path to a PKCS#12 keystore that contains an X.509 client certificate and it's +corresponding private key. These are used by {kib} to authenticate itself when +making outbound SSL/TLS connections to {es}. For this setting, you must also set +the `xpack.security.http.ssl.client_authentication` setting in {es} to +`"required"` or `"optional"` to request a client certificate from {kib}. + -In addition to this setting, trusted certificates may be specified via `elasticsearch.ssl.keystore.path` and/or +If the keystore contains any additional certificates, they are used as a +trusted certificate chain for {es}. This chain is used by {kib} to establish +trust when making outbound SSL/TLS connections to {es}. In addition to this +setting, trusted certificates may be specified via +`elasticsearch.ssl.certificateAuthorities` and/or `elasticsearch.ssl.truststore.path`. -`elasticsearch.ssl.keyPassphrase:`:: The password that will be used to decrypt the private key that is specified via -`elasticsearch.ssl.key`. This value is optional, as the key may not be encrypted. +|=== -`elasticsearch.ssl.keystore.path:`:: Path to a PKCS#12 keystore that contains an X.509 client certificate and its corresponding private key. -These are used by {kib} to authenticate itself when making outbound SSL/TLS connections to {es}. For this setting to take effect, the -`xpack.security.http.ssl.client_authentication` setting in {es} must also be set to `"required"` or `"optional"` to request a client -certificate from {kib}. -+ --- -If the keystore contains any additional certificates, those will be used as a trusted certificate chain for {es}. This chain is used by -{kib} to establish trust when making outbound SSL/TLS connections to {es}. In addition to this setting, trusted certificates may be -specified via `elasticsearch.ssl.certificateAuthorities` and/or `elasticsearch.ssl.truststore.path`. +[NOTE] +============ +This setting cannot be used in conjunction with +`elasticsearch.ssl.certificate` or `elasticsearch.ssl.key`. +============ -NOTE: This setting cannot be used in conjunction with `elasticsearch.ssl.certificate` or `elasticsearch.ssl.key`. --- +[cols="2*<"] +|=== -`elasticsearch.ssl.keystore.password:`:: The password that will be used to decrypt the keystore that is specified via -`elasticsearch.ssl.keystore.path`. If the keystore has no password, leave this unset. If the keystore has an empty password, set this to +| `elasticsearch.ssl.keystore.password:` + | The password that decrypts the keystore specified via +`elasticsearch.ssl.keystore.path`. If the keystore has no password, leave this +as blank. If the keystore has an empty password, set this to `""`. -`elasticsearch.ssl.truststore.path:`:: Path to a PKCS#12 trust store that contains one or more X.509 certificate authority (CA) certificates -which make up a trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections -to {es}. +| `elasticsearch.ssl.truststore.path:`:: + | Path to a PKCS#12 trust store that contains one or more X.509 certificate +authority (CA) certificates, which make up a trusted certificate chain for +{es}. This chain is used by {kib} to establish trust when making outbound +SSL/TLS connections to {es}. + -In addition to this setting, trusted certificates may be specified via `elasticsearch.ssl.certificateAuthorities` and/or +In addition to this setting, trusted certificates may be specified via +`elasticsearch.ssl.certificateAuthorities` and/or `elasticsearch.ssl.keystore.path`. -`elasticsearch.ssl.truststore.password:`:: The password that will be used to decrypt the trust store specified via -`elasticsearch.ssl.truststore.path`. If the trust store has no password, leave this unset. If the trust store has an empty password, set -this to `""`. - -`elasticsearch.ssl.verificationMode:`:: *Default: `"full"`* Controls the verification of the server certificate that {kib} receives when -making an outbound SSL/TLS connection to {es}. Valid values are `"full"`, `"certificate"`, and `"none"`. Using `"full"` will perform -hostname verification, using `"certificate"` will skip hostname verification, and using `"none"` will skip verification entirely. - -`elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait -for Elasticsearch at Kibana startup before retrying. - -`elasticsearch.username:` and `elasticsearch.password:`:: If your Elasticsearch -is protected with basic authentication, these settings provide the username and -password that the Kibana server uses to perform maintenance on the Kibana index -at startup. Your Kibana users still need to authenticate with Elasticsearch, -which is proxied through the Kibana server. - -`interpreter.enableInVisualize`:: *Default: true* Enables use of interpreter in -Visualize. - -`kibana.defaultAppId:`:: *Default: "home"* The default application to load. - -`kibana.index:`:: *Default: ".kibana"* Kibana uses an index in Elasticsearch to -store saved searches, visualizations and dashboards. Kibana creates a new index -if the index doesn’t already exist. If you configure a custom index, the name must -be lowercase, and conform to {es} {ref}/indices-create-index.html[index name limitations]. - -`kibana.autocompleteTimeout:`:: *Default: "1000"* Time in milliseconds to wait -for autocomplete suggestions from Elasticsearch. This value must be a whole number -greater than zero. - -`kibana.autocompleteTerminateAfter:`:: *Default: "100000"* Maximum number of -documents loaded by each shard to generate autocomplete suggestions. This value -must be a whole number greater than zero. - -`logging.dest:`:: *Default: `stdout`* Enables you specify a file where Kibana -stores log output. - -`logging.json:`:: *Default: false* Logs output as JSON. When set to `true`, the -logs will be formatted as JSON strings that include timestamp, log level, context, message -text and any other metadata that may be associated with the log message itself. -If `logging.dest.stdout` is set and there is no interactive terminal ("TTY"), this setting -will default to `true`. - -`logging.quiet:`:: *Default: false* Set the value of this setting to `true` to -suppress all logging output other than error messages. - -`logging.rotate:`:: [experimental] Specifies the options for the logging rotate feature. +|`elasticsearch.ssl.truststore.password:` + | The password that decrypts the trust store specified via +`elasticsearch.ssl.truststore.path`. If the trust store has no password, +leave this as blank. If the trust store has an empty password, set this to `""`. + +| `elasticsearch.ssl.verificationMode:` + | Controls the verification of the server certificate that {kib} receives when +making an outbound SSL/TLS connection to {es}. Valid values are `"full"`, +`"certificate"`, and `"none"`. Using `"full"` performs hostname verification, +using `"certificate"` skips hostname verification, and using `"none"` skips +verification entirely. *Default: `"full"`* + +| `elasticsearch.startupTimeout:` + | Time in milliseconds to wait for {es} at {kib} startup before retrying. +*Default: `5000`* + +| `elasticsearch.username:` and `elasticsearch.password:` + | If your {es} is protected with basic authentication, these settings provide +the username and password that the {kib} server uses to perform maintenance +on the {kib} index at startup. {kib} users still need to authenticate with +{es}, which is proxied through the {kib} server. + +| `interpreter.enableInVisualize` + | Enables use of interpreter in Visualize. *Default: `true`* + +| `kibana.defaultAppId:` + | The default application to load. *Default: `"home"`* + +| `kibana.index:` + | {kib} uses an index in {es} to store saved searches, visualizations, and +dashboards. {kib} creates a new index if the index doesn’t already exist. +If you configure a custom index, the name must be lowercase, and conform to the +{es} {ref}/indices-create-index.html[index name limitations]. +*Default: `".kibana"`* + +| `kibana.autocompleteTimeout:` + | Time in milliseconds to wait for autocomplete suggestions from {es}. +This value must be a whole number greater than zero. *Default: `"1000"`* + +| `kibana.autocompleteTerminateAfter:` + | Maximum number of documents loaded by each shard to generate autocomplete +suggestions. This value must be a whole number greater than zero. +*Default: `"100000"`* + +| `logging.dest:` + | Enables you to specify a file where {kib} stores log output. +*Default: `stdout`* + +| `logging.json:` + | Logs output as JSON. When set to `true`, the logs are formatted as JSON +strings that include timestamp, log level, context, message text, and any other +metadata that may be associated with the log message. +When `logging.dest.stdout` is set, and there is no interactive terminal ("TTY"), +this setting defaults to `true`. *Default: `false`* + +| `logging.quiet:` + | Set the value of this setting to `true` to suppress all logging output other +than error messages. *Default: `false`* + +| `logging.rotate:` + | experimental[] Specifies the options for the logging rotate feature. When not defined, all the sub options defaults would be applied. The following example shows a valid logging rotate configuration: -+ + +|=== + +[source,text] -- - logging.rotate: - enabled: true - everyBytes: 10485760 - keepFiles: 10 + logging.rotate: + enabled: true + everyBytes: 10485760 + keepFiles: 10 -- -`logging.rotate.enabled:`:: [experimental] *Default: false* Set the value of this setting to `true` to +[cols="2*<"] +|=== + +| `logging.rotate.enabled:` + | experimental[] Set the value of this setting to `true` to enable log rotation. If you do not have a `logging.dest` set that is different from `stdout` -that feature would not take any effect. +that feature would not take any effect. *Default: `false`* -`logging.rotate.everyBytes:`:: [experimental] *Default: 10485760* The maximum size of a log file (that is `not an exact` limit). After the +| `logging.rotate.everyBytes:` + | experimental[] The maximum size of a log file (that is `not an exact` limit). After the limit is reached, a new log file is generated. The default size limit is 10485760 (10 MB) and -this option should be in the range of 1048576 (1 MB) to 1073741824 (1 GB). +this option should be in the range of 1048576 (1 MB) to 1073741824 (1 GB). *Default: `10485760`* -`logging.rotate.keepFiles:`:: [experimental] *Default: 7* The number of most recent rotated log files to keep +| `logging.rotate.keepFiles:` + | experimental[] The number of most recent rotated log files to keep on disk. Older files are deleted during log rotation. The default value is 7. The `logging.rotate.keepFiles` -option has to be in the range of 2 to 1024 files. +option has to be in the range of 2 to 1024 files. *Default: `7`* -`logging.rotate.pollingInterval:`:: [experimental] *Default: 10000* The number of milliseconds for the polling strategy in case -the `logging.rotate.usePolling` is enabled. That option has to be in the range of 5000 to 3600000 milliseconds. +| `logging.rotate.pollingInterval:` + | experimental[] The number of milliseconds for the polling strategy in case +the `logging.rotate.usePolling` is enabled. `logging.rotate.usePolling` must be in the 5000 to 3600000 millisecond range. *Default: `10000`* -`logging.rotate.usePolling:`:: [experimental] *Default: false* By default we try to understand the best way to monitoring +| `logging.rotate.usePolling:` + | experimental[] By default we try to understand the best way to monitoring the log file and warning about it. Please be aware there are some systems where watch api is not accurate. In those cases, in order to get the feature working, -the `polling` method could be used enabling that option. +the `polling` method could be used enabling that option. *Default: `false`* -`logging.silent:`:: *Default: false* Set the value of this setting to `true` to -suppress all logging output. +| `logging.silent:` + | Set the value of this setting to `true` to +suppress all logging output. *Default: `false`* -`logging.timezone`:: *Default: UTC* Set to the canonical timezone id -(for example, `America/Los_Angeles`) to log events using that timezone. A list of timezones can -be referenced at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. +| `logging.timezone` + | Set to the canonical timezone ID +(for example, `America/Los_Angeles`) to log events using that timezone. For a +list of timezones, refer to https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. *Default: `UTC`* -[[logging-verbose]]`logging.verbose:`:: *Default: false* Set the value of this -setting to `true` to log all events, including system usage information and all -requests. Supported on Elastic Cloud Enterprise. +| [[logging-verbose]] `logging.verbose:` + | Set to `true` to log all events, including system usage information and all +requests. Supported on {ece}. *Default: `false`* -`map.includeElasticMapsService:`:: *Default: true* -Set to false to disable connections to Elastic Maps Service. +| `map.includeElasticMapsService:` + | Set to `false` to disable connections to Elastic Maps Service. When `includeElasticMapsService` is turned off, only the vector layers configured by `map.regionmap` -and the tile layer configured by `map.tilemap.url` will be available in <>. +and the tile layer configured by `map.tilemap.url` are available in <>. *Default: `true`* -`map.proxyElasticMapsServiceInMaps:`:: *Default: false* -Set to true to proxy all <> Elastic Maps Service requests through the Kibana server. +| `map.proxyElasticMapsServiceInMaps:` + | Set to `true` to proxy all <> Elastic Maps Service +requests through the {kib} server. *Default: `false`* -[[regionmap-settings]] `map.regionmap:`:: Specifies additional vector layers for +| [[regionmap-settings]] `map.regionmap:` + | Specifies additional vector layers for use in <> visualizations. Supported on {ece}. Each layer object points to an external vector file that contains a geojson FeatureCollection. The file must use the https://en.wikipedia.org/wiki/World_Geodetic_System[WGS84 coordinate reference system (ESPG:4326)] and only include polygons. If the file is hosted on a separate domain from -Kibana, the server needs to be CORS-enabled so Kibana can download the file. -[[region-map-configuration-example]] +{kib}, the server needs to be CORS-enabled so {kib} can download the file. The following example shows a valid region map configuration. -+ + +|=== + +[source,text] -- - map - includeElasticMapsService: false - regionmap: - layers: - - name: "Departments of France" - url: "http://my.cors.enabled.server.org/france_departements.geojson" - attribution: "INRAP" - fields: - - name: "department" - description: "Full department name" - - name: "INSEE" - description: "INSEE numeric identifier" +map.regionmap: + includeElasticMapsService: false + layers: + - name: "Departments of France" + url: "http://my.cors.enabled.server.org/france_departements.geojson" + attribution: "INRAP" + fields: + - name: "department" + description: "Full department name" + - name: "INSEE" + description: "INSEE numeric identifier" -- -[[regionmap-ES-map]]`map.includeElasticMapsService:`:: Turns on or off -whether layers from the Elastic Maps Service should be included in the vector -layer option list. Supported on Elastic Cloud Enterprise. By turning this off, +[cols="2*<"] +|=== + +| [[regionmap-ES-map]] `map.includeElasticMapsService:` + | Turns on or off whether layers from the Elastic Maps Service should be included in the vector +layer option list. Supported on {ece}. By turning this off, only the layers that are configured here will be included. The default is `true`. This also affects whether tile-service from the Elastic Maps Service will be available. -[[regionmap-attribution]]`map.regionmap.layers[].attribution:`:: Optional. -References the originating source of the geojson file. Supported on {ece}. +| [[regionmap-attribution]] `map.regionmap.layers[].attribution:` + | Optional. References the originating source of the geojson file. +Supported on {ece}. -[[regionmap-fields]]`map.regionmap.layers[].fields[]:`:: Mandatory. Each layer +| [[regionmap-fields]] `map.regionmap.layers[].fields[]:` + | Mandatory. Each layer can contain multiple fields to indicate what properties from the geojson -features you wish to expose. This <> shows how to define multiple -properties. Supported on {ece}. +features you wish to expose. Supported on {ece}. The following shows how to define multiple +properties: + +|=== -[[regionmap-field-description]]`map.regionmap.layers[].fields[].description:`:: -Mandatory. The human readable text that is shown under the Options tab when +[source,text] +-- +map.regionmap: + includeElasticMapsService: false + layers: + - name: "Departments of France" + url: "http://my.cors.enabled.server.org/france_departements.geojson" + attribution: "INRAP" + fields: + - name: "department" + description: "Full department name" + - name: "INSEE" + description: "INSEE numeric identifier" +-- + +[cols="2*<"] +|=== + +| [[regionmap-field-description]] `map.regionmap.layers[].fields[].description:` + | Mandatory. The human readable text that is shown under the Options tab when building the Region Map visualization. Supported on {ece}. -[[regionmap-field-name]]`map.regionmap.layers[].fields[].name:`:: Mandatory. +| [[regionmap-field-name]] `map.regionmap.layers[].fields[].name:` + | Mandatory. This value is used to do an inner-join between the document stored in -Elasticsearch and the geojson file. For example, if the field in the geojson is -called `Location` and has city names, there must be a field in Elasticsearch -that holds the same values that Kibana can then use to lookup for the geoshape +{es} and the geojson file. For example, if the field in the geojson is +called `Location` and has city names, there must be a field in {es} +that holds the same values that {kib} can then use to lookup for the geoshape data. Supported on {ece}. -[[regionmap-name]]`map.regionmap.layers[].name:`:: Mandatory. A description of +| [[regionmap-name]] `map.regionmap.layers[].name:` + | Mandatory. A description of the map being provided. Supported on {ece}. -[[regionmap-url]]`map.regionmap.layers[].url:`:: Mandatory. The location of the +| [[regionmap-url]] `map.regionmap.layers[].url:` + | Mandatory. The location of the geojson file as provided by a webserver. Supported on {ece}. -[[tilemap-settings]] `map.tilemap.options.attribution:`:: +| [[tilemap-settings]] `map.tilemap.options.attribution:` + | The map attribution string. Supported on {ece}. *Default: `"© [Elastic Maps Service](https://www.elastic.co/elastic-maps-service)"`* -The map attribution string. Supported on {ece}. -[[tilemap-max-zoom]]`map.tilemap.options.maxZoom:`:: *Default: 10* The maximum -zoom level. Supported on {ece}. +| [[tilemap-max-zoom]] `map.tilemap.options.maxZoom:` + | The maximum zoom level. Supported on {ece}. *Default: `10`* -[[tilemap-min-zoom]]`map.tilemap.options.minZoom:`:: *Default: 1* The minimum -zoom level. Supported on {ece}. +| [[tilemap-min-zoom]] `map.tilemap.options.minZoom:` + | The minimum zoom level. Supported on {ece}. *Default: `1`* -[[tilemap-subdomains]]`map.tilemap.options.subdomains:`:: An array of subdomains +| [[tilemap-subdomains]] `map.tilemap.options.subdomains:` + | An array of subdomains used by the tile service. Specify the position of the subdomain the URL with the token `{s}`. Supported on {ece}. -[[tilemap-url]]`map.tilemap.url:`:: The URL to the tileservice that Kibana uses +| [[tilemap-url]] `map.tilemap.url:` + | The URL to the tileservice that {kib} uses to display map tiles in tilemap visualizations. Supported on {ece}. By default, -Kibana reads this url from an external metadata service, but users can still +{kib} reads this URL from an external metadata service, but users can override this parameter to use their own Tile Map Service. For example: `"https://tiles.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana"` -`newsfeed.enabled:` :: *Default: `true`* Controls whether to enable the newsfeed -system for the Kibana UI notification center. Set to `false` to disable the -newsfeed system. +| `newsfeed.enabled:` + | Controls whether to enable the newsfeed +system for the {kib} UI notification center. Set to `false` to disable the +newsfeed system. *Default: `true`* -`path.data:`:: *Default: `data`* The path where Kibana stores persistent data -not saved in Elasticsearch. +| `path.data:` + | The path where {kib} stores persistent data +not saved in {es}. *Default: `data`* -`pid.file:`:: Specifies the path where Kibana creates the process ID file. +| `pid.file:` + | Specifies the path where {kib} creates the process ID file. -`ops.interval:`:: *Default: 5000* Set the interval in milliseconds to sample -system and process performance metrics. The minimum value is 100. +| `ops.interval:` + | Set the interval in milliseconds to sample +system and process performance metrics. The minimum value is 100. *Default: `5000`* -`server.basePath:`:: Enables you to specify a path to mount Kibana at if you are -running behind a proxy. Use the `server.rewriteBasePath` setting to tell Kibana +| `server.basePath:` + | Enables you to specify a path to mount {kib} at if you are +running behind a proxy. Use the `server.rewriteBasePath` setting to tell {kib} if it should remove the basePath from requests it receives, and to prevent a deprecation warning at startup. This setting cannot end in a slash (`/`). -[[server-compression]]`server.compression.enabled:`:: *Default: `true`* Set to `false` to disable HTTP compression for all responses. +| [[server-compression]] `server.compression.enabled:` + | Set to `false` to disable HTTP compression for all responses. *Default: `true`* -`server.compression.referrerWhitelist:`:: *Default: none* Specifies an array of trusted hostnames, such as the Kibana host, or a reverse -proxy sitting in front of it. This determines whether HTTP compression may be used for responses, based on the request's `Referer` header. -This setting may not be used when `server.compression.enabled` is set to `false`. +| `server.compression.referrerWhitelist:` + | Specifies an array of trusted hostnames, such as the {kib} host, or a reverse +proxy sitting in front of it. This determines whether HTTP compression may be used for responses, based on the request `Referer` header. +This setting may not be used when `server.compression.enabled` is set to `false`. *Default: `none`* -`server.customResponseHeaders:`:: *Default: `{}`* Header names and values to - send on all responses to the client from the Kibana server. +| `server.customResponseHeaders:` + | Header names and values to +send on all responses to the client from the {kib} server. *Default: `{}`* -`server.host:`:: *Default: "localhost"* This setting specifies the host of the -back end server. To allow remote users to connect, set the value to the IP address or DNS name of the {kib} server. +| `server.host:` + | This setting specifies the host of the +back end server. To allow remote users to connect, set the value to the IP address or DNS name of the {kib} server. *Default: `"localhost"`* -`server.keepaliveTimeout:`:: *Default: "120000"* The number of milliseconds to wait for additional data before restarting -the `server.socketTimeout` counter. +| `server.keepaliveTimeout:` + | The number of milliseconds to wait for additional data before restarting +the `server.socketTimeout` counter. *Default: `"120000"`* -`server.maxPayloadBytes:`:: *Default: 1048576* The maximum payload size in bytes -for incoming server requests. +| `server.maxPayloadBytes:` + | The maximum payload size in bytes +for incoming server requests. *Default: `1048576`* -`server.name:`:: *Default: "your-hostname"* A human-readable display name that -identifies this Kibana instance. +| `server.name:` + | A human-readable display name that +identifies this {kib} instance. *Default: `"your-hostname"`* -`server.port:`:: *Default: 5601* Kibana is served by a back end server. This -setting specifies the port to use. +| `server.port:` + | {kib} is served by a back end server. This +setting specifies the port to use. *Default: `5601`* -`server.rewriteBasePath:`:: *Default: deprecated* Specifies whether Kibana should +| `server.rewriteBasePath:` + | Specifies whether {kib} should rewrite requests that are prefixed with `server.basePath` or require that they -are rewritten by your reverse proxy. In Kibana 6.3 and earlier, the default is -`false`. In Kibana 7.x, the setting is deprecated. In Kibana 8.0 and later, the -default is `true`. +are rewritten by your reverse proxy. In {kib} 6.3 and earlier, the default is +`false`. In {kib} 7.x, the setting is deprecated. In {kib} 8.0 and later, the +default is `true`. *Default: `deprecated`* -`server.socketTimeout:`:: *Default: "120000"* The number of milliseconds to wait before closing an -inactive socket. +| `server.socketTimeout:` + | The number of milliseconds to wait before closing an +inactive socket. *Default: `"120000"`* -`server.ssl.certificate:` and `server.ssl.key:`:: Paths to a PEM-encoded X.509 server certificate and its corresponding private key. These -are used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. -+ -NOTE: These settings cannot be used in conjunction with `server.ssl.keystore.path`. +| `server.ssl.certificate:` and `server.ssl.key:` + | Paths to a PEM-encoded X.509 server certificate and its corresponding private key. These +are used by {kib} to establish trust when receiving inbound SSL/TLS connections from users. + +|=== -`server.ssl.certificateAuthorities:`:: Paths to one or more PEM-encoded X.509 certificate authority (CA) certificates which make up a +[NOTE] +============ +These settings cannot be used in conjunction with `server.ssl.keystore.path`. +============ + +[cols="2*<"] +|=== + +| `server.ssl.certificateAuthorities:` + | Paths to one or more PEM-encoded X.509 certificate authority (CA) certificates which make up a trusted certificate chain for {kib}. This chain is used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. If PKI authentication is enabled, this chain is also used by {kib} to verify client certificates from end users. + In addition to this setting, trusted certificates may be specified via `server.ssl.keystore.path` and/or `server.ssl.truststore.path`. -`server.ssl.cipherSuites:`:: *Default: ECDHE-RSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-GCM-SHA384, DHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-SHA256, DHE-RSA-AES128-SHA256, ECDHE-RSA-AES256-SHA384, DHE-RSA-AES256-SHA384, ECDHE-RSA-AES256-SHA256, DHE-RSA-AES256-SHA256, HIGH,!aNULL, !eNULL, !EXPORT, !DES, !RC4, !MD5, !PSK, !SRP, !CAMELLIA*. -Details on the format, and the valid options, are available via the +| `server.ssl.cipherSuites:` + | Details on the format, and the valid options, are available via the https://www.openssl.org/docs/man1.0.2/apps/ciphers.html#CIPHER-LIST-FORMAT[OpenSSL cipher list format documentation]. +*Default: `ECDHE-RSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-GCM-SHA384, DHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-SHA256, DHE-RSA-AES128-SHA256, ECDHE-RSA-AES256-SHA384, DHE-RSA-AES256-SHA384, ECDHE-RSA-AES256-SHA256, DHE-RSA-AES256-SHA256, HIGH,!aNULL, !eNULL, !EXPORT, !DES, !RC4, !MD5, !PSK, !SRP, !CAMELLIA`*. -`server.ssl.clientAuthentication:`:: *Default: `"none"`* Controls {kib}’s behavior in regard to requesting a certificate from client +| `server.ssl.clientAuthentication:` + | Controls the behavior in {kib} for requesting a certificate from client connections. Valid values are `"required"`, `"optional"`, and `"none"`. Using `"required"` will refuse to establish the connection unless a client presents a certificate, using `"optional"` will allow a client to present a certificate if it has one, and using `"none"` will -prevent a client from presenting a certificate. +prevent a client from presenting a certificate. *Default: `"none"`* -`server.ssl.enabled:`:: *Default: `false`* Enables SSL/TLS for inbound connections to {kib}. When set to `true`, a certificate and its +| `server.ssl.enabled:` + | Enables SSL/TLS for inbound connections to {kib}. When set to `true`, a certificate and its corresponding private key must be provided. These can be specified via `server.ssl.keystore.path` or the combination of -`server.ssl.certificate` and `server.ssl.key`. +`server.ssl.certificate` and `server.ssl.key`. *Default: `false`* -`server.ssl.keyPassphrase:`:: The password that will be used to decrypt the private key that is specified via `server.ssl.key`. This value +| `server.ssl.keyPassphrase:` + | The password that decrypts the private key that is specified via `server.ssl.key`. This value is optional, as the key may not be encrypted. -`server.ssl.keystore.path:`:: Path to a PKCS#12 keystore that contains an X.509 server certificate and its corresponding private key. If the +| `server.ssl.keystore.path:` + | Path to a PKCS#12 keystore that contains an X.509 server certificate and its corresponding private key. If the keystore contains any additional certificates, those will be used as a trusted certificate chain for {kib}. All of these are used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. The certificate chain is also used by {kib} to verify client certificates from end users when PKI authentication is enabled. + --- In addition to this setting, trusted certificates may be specified via `server.ssl.certificateAuthorities` and/or `server.ssl.truststore.path`. -NOTE: This setting cannot be used in conjunction with `server.ssl.certificate` or `server.ssl.key`. --- +|=== + +[NOTE] +============ +This setting cannot be used in conjunction with `server.ssl.certificate` or `server.ssl.key` +============ -`server.ssl.keystore.password:`:: The password that will be used to decrypt the keystore specified via `server.ssl.keystore.path`. If the +[cols="2*<"] +|=== + +| `server.ssl.keystore.password:` + | The password that will be used to decrypt the keystore specified via `server.ssl.keystore.path`. If the keystore has no password, leave this unset. If the keystore has an empty password, set this to `""`. -`server.ssl.truststore.path:`:: Path to a PKCS#12 trust store that contains one or more X.509 certificate authority (CA) certificates which +| `server.ssl.truststore.path:` + | Path to a PKCS#12 trust store that contains one or more X.509 certificate authority (CA) certificates which make up a trusted certificate chain for {kib}. This chain is used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. If PKI authentication is enabled, this chain is also used by {kib} to verify client certificates from end users. + In addition to this setting, trusted certificates may be specified via `server.ssl.certificateAuthorities` and/or `server.ssl.keystore.path`. -`server.ssl.truststore.password:`:: The password that will be used to decrypt the trust store specified via `server.ssl.truststore.path`. If +| `server.ssl.truststore.password:` + | The password that will be used to decrypt the trust store specified via `server.ssl.truststore.path`. If the trust store has no password, leave this unset. If the trust store has an empty password, set this to `""`. -`server.ssl.redirectHttpFromPort:`:: Kibana will bind to this port and redirect +| `server.ssl.redirectHttpFromPort:` + | {kib} binds to this port and redirects all http requests to https over the port configured as `server.port`. -`server.ssl.supportedProtocols:`:: *Default: TLSv1.1, TLSv1.2* An array of -supported protocols with versions. Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2` +| `server.ssl.supportedProtocols:` + | An array of supported protocols with versions. +Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2`. *Default: TLSv1.1, TLSv1.2* -`server.xsrf.whitelist:`:: It is not recommended to disable protections for +| `server.xsrf.whitelist:` + | It is not recommended to disable protections for arbitrary API endpoints. Instead, supply the `kbn-xsrf` header. The `server.xsrf.whitelist` setting requires the following format: -[source,text] +|=== +[source,text] ---- *Default: [ ]* An array of API endpoints which should be exempt from Cross-Site Request Forgery ("XSRF") protections. ---- -`status.allowAnonymous:`:: *Default: false* If authentication is enabled, -setting this to `true` enables unauthenticated users to access the Kibana -server status API and status page. +[cols="2*<"] +|=== + +| `status.allowAnonymous:` + | If authentication is enabled, +setting this to `true` enables unauthenticated users to access the {kib} +server status API and status page. *Default: `false`* -`telemetry.allowChangingOptInStatus`:: *Default: true*. If `true`, -users are able to change the telemetry setting at a later time in -<>. If `false`, +| `telemetry.allowChangingOptInStatus` + | When `true`, users are able to change the telemetry setting at a later time in +<>. When `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. +cannot be `false` at the same time. *Default: `true`*. -`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`. +| `telemetry.optIn` + | When `true`, telemetry data is sent to Elastic. +When `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`. +*Default: `true`* -`telemetry.enabled`:: *Default: true* Reporting your cluster statistics helps +| `telemetry.enabled` + | Reporting your cluster statistics helps us improve your user experience. Your data is never shared with anyone. Set to `false` to disable telemetry capabilities entirely. You can alternatively opt -out through the *Advanced Settings* in {kib}. +out through *Advanced Settings*. *Default: `true`* + +| `vis_type_vega.enableExternalUrls:` + | Set this value to true to allow Vega to use any URL to access external data +sources and images. When false, Vega can only get data from {es}. *Default: `false`* -`vis_type_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` + | Set this value to false to +disable the License Management UI. *Default: `true`* -`xpack.license_management.enabled`:: *Default: true* Set this value to false to -disable the License Management user interface. +| `xpack.rollup.enabled:` + | Set this value to false to disable the +Rollup UI. *Default: true* -`xpack.rollup.enabled:`:: *Default: true* Set this value to false to disable the -Rollup user interface. +| `i18n.locale` + | Set this value to change the {kib} interface language. +Valid locales are: `en`, `zh-CN`, `ja-JP`. *Default: `en`* -`i18n.locale`:: *Default: en* Set this value to change the Kibana interface language. Valid locales are: `en`, `zh-CN`, `ja-JP`. +|=== include::{docdir}/settings/alert-action-settings.asciidoc[] include::{docdir}/settings/apm-settings.asciidoc[] diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc index abdcc7d1ba524..673b4f6263e18 100644 --- a/docs/user/alerting/action-types/pagerduty.asciidoc +++ b/docs/user/alerting/action-types/pagerduty.asciidoc @@ -92,7 +92,7 @@ section of the alert configuration and selecting *Add new*. * Alternatively, create a connector by navigating to *Management* from the {kib} navbar and selecting *Alerts and Actions*. Then, select the *Connectors* tab, click the *Create connector* button, and select the PagerDuty option. -. Configure the connector by giving it a name and optionally entering the API URL and Routing Key, or using the defaults. +. Configure the connector by giving it a name and entering the Integration Key, optionally entering a custom API URL. + See <> for how to obtain the endpoint and key information from PagerDuty and <> for more details. @@ -133,7 +133,7 @@ PagerDuty connectors have the following configuration properties: Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <> setting, make sure the hostname is whitelisted. -Routing Key:: A 32 character PagerDuty Integration Key for an integration on a service or on a global ruleset. +Integration Key:: A 32 character PagerDuty Integration Key for an integration on a service, also referred to as the routing key. [float] [[pagerduty-action-configuration]] diff --git a/docs/user/discover.asciidoc b/docs/user/discover.asciidoc index 2547b38a22616..7bac80237a26e 100644 --- a/docs/user/discover.asciidoc +++ b/docs/user/discover.asciidoc @@ -21,6 +21,7 @@ image::images/Discover-Start.png[Discover] [float] +[[select-pattern]] === Set up your index pattern The first thing to do in *Discover* is to select an <>, which diff --git a/docs/user/reporting/development/pdf-integration.asciidoc b/docs/user/reporting/development/pdf-integration.asciidoc index af5ba5be1636e..e9f32de41baab 100644 --- a/docs/user/reporting/development/pdf-integration.asciidoc +++ b/docs/user/reporting/development/pdf-integration.asciidoc @@ -63,3 +63,5 @@ If there are multiple visualizations, the `data-shared-items-count` attribute sh many Visualizations to look for. Reporting will look at every element with the `data-shared-item` attribute and use the corresponding `data-render-complete` attribute and `renderComplete` events to listen for rendering to complete. When rendering is complete for a visualization the `data-render-complete` attribute should be set to "true" and it should dispatch a custom DOM `renderComplete` event. + +If the reporting job uses multiple URLs, before looking for any of the `data-shared-item` or `data-shared-items-count` attributes, it waits for a `data-shared-page` attribute that specifies which page is being loaded. diff --git a/docs/user/security/securing-kibana.asciidoc b/docs/user/security/securing-kibana.asciidoc index 24aacd6a47626..f4178bacb111e 100644 --- a/docs/user/security/securing-kibana.asciidoc +++ b/docs/user/security/securing-kibana.asciidoc @@ -31,14 +31,14 @@ file: [source,yaml] ----------------------------------------------- -elasticsearch.username: "kibana" +elasticsearch.username: "kibana_system" elasticsearch.password: "kibanapassword" ----------------------------------------------- The {kib} server submits requests as this user to access the cluster monitoring APIs and the `.kibana` index. The server does _not_ need access to user indices. -The password for the built-in `kibana` user is typically set as part of the +The password for the built-in `kibana_system` user is typically set as part of the {security} configuration process on {es}. For more information, see {ref}/built-in-users.html[Built-in users]. -- diff --git a/docs/visualize/timelion.asciidoc b/docs/visualize/timelion.asciidoc index a7520227977bc..852c3e1ecdeca 100644 --- a/docs/visualize/timelion.asciidoc +++ b/docs/visualize/timelion.asciidoc @@ -50,10 +50,10 @@ To compare the two data sets, add another series with data from the previous hou .es(index=metricbeat-*, timefield='@timestamp', metric='avg:system.cpu.user.pct'), - .es(offset=-1h, <1> - index=metricbeat-*, - timefield='@timestamp', - metric='avg:system.cpu.user.pct') +.es(offset=-1h, <1> + index=metricbeat-*, + timefield='@timestamp', + metric='avg:system.cpu.user.pct') ---------------------------------- <1> `offset` offsets the data retrieval by a date expression. In this example, `-1h` offsets the data back by one hour. @@ -119,11 +119,11 @@ To differentiate between the current hour data and the last hour data, change th metric='avg:system.cpu.user.pct') .label('last hour') .lines(fill=1,width=0.5), <1> - .es(index=metricbeat-*, - timefield='@timestamp', - metric='avg:system.cpu.user.pct') - .label('current hour') - .title('CPU usage over time') +.es(index=metricbeat-*, + timefield='@timestamp', + metric='avg:system.cpu.user.pct') + .label('current hour') + .title('CPU usage over time') ---------------------------------- <1> `.lines()` changes the appearance of the chart lines. In this example, `.lines(fill=1,width=0.5)` sets the fill level to `1`, and the border width to `0.5`. @@ -169,7 +169,20 @@ Change the position and style of the legend: [source,text] ---------------------------------- -.es(offset=-1h,index=metricbeat-*, timefield='@timestamp', metric='avg:system.cpu.user.pct').label('last hour').lines(fill=1,width=0.5).color(gray), .es(index=metricbeat-*, timefield='@timestamp', metric='avg:system.cpu.user.pct').label('current hour').title('CPU usage over time').color(#1E90FF).legend(columns=2, position=nw) <1> +.es(offset=-1h, + index=metricbeat-*, + timefield='@timestamp', + metric='avg:system.cpu.user.pct') + .label('last hour') + .lines(fill=1,width=0.5) + .color(gray), +.es(index=metricbeat-*, + timefield='@timestamp', + metric='avg:system.cpu.user.pct') + .label('current hour') + .title('CPU usage over time') + .color(#1E90FF) + .legend(columns=2, position=nw) <1> ---------------------------------- <1> `.legend()` sets the position and style of the legend. In this example, `.legend(columns=2, position=nw)` places the legend in the north west position of the visualization with two columns. @@ -192,7 +205,9 @@ To start tracking the inbound and outbound network traffic, enter the following [source,text] ---------------------------------- -.es(index=metricbeat*, timefield=@timestamp, metric=max:system.network.in.bytes) +.es(index=metricbeat*, + timefield=@timestamp, + metric=max:system.network.in.bytes) ---------------------------------- [role="screenshot"] @@ -207,7 +222,10 @@ Change how the data is displayed so that you can easily monitor the inbound traf [source,text] ---------------------------------- -.es(index=metricbeat*, timefield=@timestamp, metric=max:system.network.in.bytes).derivative() <1> +.es(index=metricbeat*, + timefield=@timestamp, + metric=max:system.network.in.bytes) + .derivative() <1> ---------------------------------- <1> `.derivative` plots the change in values over time. @@ -220,7 +238,15 @@ Add a similar calculation for outbound traffic: [source,text] ---------------------------------- -.es(index=metricbeat*, timefield=@timestamp, metric=max:system.network.in.bytes).derivative(), .es(index=metricbeat*, timefield=@timestamp, metric=max:system.network.out.bytes).derivative().multiply(-1) <1> +.es(index=metricbeat*, + timefield=@timestamp, + metric=max:system.network.in.bytes) + .derivative(), +.es(index=metricbeat*, + timefield=@timestamp, + metric=max:system.network.out.bytes) + .derivative() + .multiply(-1) <1> ---------------------------------- <1> `.multiply()` multiplies the data series by a number, the result of a data series, or a list of data series. For this example, `.multiply(-1)` converts the outbound network traffic to a negative value since the outbound network traffic is leaving your machine. @@ -237,7 +263,17 @@ To make the visualization easier to analyze, change the data metric from bytes t [source,text] ---------------------------------- -.es(index=metricbeat*, timefield=@timestamp, metric=max:system.network.in.bytes).derivative().divide(1048576), .es(index=metricbeat*, timefield=@timestamp, metric=max:system.network.out.bytes).derivative().multiply(-1).divide(1048576) <1> +.es(index=metricbeat*, + timefield=@timestamp, + metric=max:system.network.in.bytes) + .derivative() + .divide(1048576), +.es(index=metricbeat*, + timefield=@timestamp, + metric=max:system.network.out.bytes) + .derivative() + .multiply(-1) + .divide(1048576) <1> ---------------------------------- <1> `.divide()` accepts the same input as `.multiply()`, then divides the data series by the defined divisor. @@ -271,8 +307,8 @@ Customize and format the visualization using functions: .divide(1048576) .lines(fill=2, width=1) <3> .color(blue) <4> - .label("Outbound traffic") - .legend(columns=2, position=nw) <5> + .label("Outbound traffic") + .legend(columns=2, position=nw) <5> ---------------------------------- <1> `.label()` adds custom labels to the visualization. @@ -309,7 +345,9 @@ To chart the maximum value of `system.memory.actual.used.bytes`, enter the follo [source,text] ---------------------------------- -.es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes') +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes') ---------------------------------- [role="screenshot"] @@ -338,17 +376,17 @@ To track the amount of memory used, create two thresholds: null) .label('warning') .color('#FFCC11'), - .es(index=metricbeat-*, - timefield='@timestamp', - metric='max:system.memory.actual.used.bytes') - .if(gt, - 11375000000, - .es(index=metricbeat-*, - timefield='@timestamp', - metric='max:system.memory.actual.used.bytes'), - null) - .label('severe') - .color('red') +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes') + .if(gt, + 11375000000, + .es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes'), + null) + .label('severe') + .color('red') ---------------------------------- <1> Timelion conditional logic for the _greater than_ operator. In this example, the warning threshold is 11.3GB (`11300000000`), and the severe threshold is 11.375GB (`11375000000`). If the threshold values are too high or low for your machine, adjust the values accordingly. @@ -366,7 +404,33 @@ To determine the trend, create a new data series: [source,text] ---------------------------------- -.es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes'), .es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes').if(gt,11300000000,.es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes'),null).label('warning').color('#FFCC11'), .es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes').if(gt,11375000000,.es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes'),null).label('severe').color('red'), .es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes').mvavg(10) <1> +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes'), +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes') + .if(gt,11300000000, + .es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes'), + null) + .label('warning') + .color('#FFCC11'), +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes') + .if(gt,11375000000, + .es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes'), + null). + label('severe') + .color('red'), +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes') + .mvavg(10) <1> ---------------------------------- <1> `mvavg()` calculates the moving average over a specified period of time. In this example, `.mvavg(10)` creates a moving average with a window of 10 data points. @@ -396,30 +460,30 @@ Customize and format the visualization using functions: .es(index=metricbeat-*, timefield='@timestamp', metric='max:system.memory.actual.used.bytes'), - null) - .label('warning') - .color('#FFCC11') <3> - .lines(width=5), <4> - .es(index=metricbeat-*, - timefield='@timestamp', - metric='max:system.memory.actual.used.bytes') - .if(gt, - 11375000000, - .es(index=metricbeat-*, - timefield='@timestamp', - metric='max:system.memory.actual.used.bytes'), - null) - .label('severe') - .color('red') - .lines(width=5), + null) + .label('warning') + .color('#FFCC11') <3> + .lines(width=5), <4> +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes') + .if(gt, + 11375000000, .es(index=metricbeat-*, timefield='@timestamp', - metric='max:system.memory.actual.used.bytes') - .mvavg(10) - .label('mvavg') - .lines(width=2) - .color(#5E5E5E) - .legend(columns=4, position=nw) <5> + metric='max:system.memory.actual.used.bytes'), + null) + .label('severe') + .color('red') + .lines(width=5), +.es(index=metricbeat-*, + timefield='@timestamp', + metric='max:system.memory.actual.used.bytes') + .mvavg(10) + .label('mvavg') + .lines(width=2) + .color(#5E5E5E) + .legend(columns=4, position=nw) <5> ---------------------------------- <1> `.label()` adds custom labels to the visualization. diff --git a/examples/alerting_example/public/application.tsx b/examples/alerting_example/public/application.tsx index 6ff5a7d0880b8..23e9d19441002 100644 --- a/examples/alerting_example/public/application.tsx +++ b/examples/alerting_example/public/application.tsx @@ -27,6 +27,7 @@ import { IUiSettingsClient, DocLinksStart, ToastsSetup, + ApplicationStart, } from '../../../src/core/public'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; import { ChartsPluginStart } from '../../../src/plugins/charts/public'; @@ -48,6 +49,7 @@ export interface AlertingExampleComponentParams { uiSettings: IUiSettingsClient; docLinks: DocLinksStart; toastNotifications: ToastsSetup; + capabilities: ApplicationStart['capabilities']; } const AlertingExampleApp = (deps: AlertingExampleComponentParams) => { @@ -102,6 +104,7 @@ export const renderApp = ( http={http} uiSettings={uiSettings} docLinks={docLinks} + capabilities={application.capabilities} {...deps} />, element diff --git a/examples/alerting_example/public/components/create_alert.tsx b/examples/alerting_example/public/components/create_alert.tsx index 0541e0b18a2e1..a8e1f06cb3914 100644 --- a/examples/alerting_example/public/components/create_alert.tsx +++ b/examples/alerting_example/public/components/create_alert.tsx @@ -36,6 +36,7 @@ export const CreateAlert = ({ docLinks, data, toastNotifications, + capabilities, }: AlertingExampleComponentParams) => { const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); @@ -60,6 +61,7 @@ export const CreateAlert = ({ docLinks, charts, dataFieldsFormats: data.fieldFormats, + capabilities, }} > = () => new UiActionExamplesPlugin(); +export const plugin = () => new UiActionExamplesPlugin(); export { HELLO_WORLD_TRIGGER_ID } from './hello_world_trigger'; export { ACTION_HELLO_WORLD } from './hello_world_action'; diff --git a/examples/ui_action_examples/public/plugin.ts b/examples/ui_action_examples/public/plugin.ts index c47746d4b3fd6..3a9f673261e33 100644 --- a/examples/ui_action_examples/public/plugin.ts +++ b/examples/ui_action_examples/public/plugin.ts @@ -17,15 +17,19 @@ * under the License. */ -import { Plugin, CoreSetup } from '../../../src/core/public'; -import { UiActionsSetup } from '../../../src/plugins/ui_actions/public'; +import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public'; +import { UiActionsSetup, UiActionsStart } from '../../../src/plugins/ui_actions/public'; import { createHelloWorldAction, ACTION_HELLO_WORLD } from './hello_world_action'; import { helloWorldTrigger, HELLO_WORLD_TRIGGER_ID } from './hello_world_trigger'; -interface UiActionExamplesSetupDependencies { +export interface UiActionExamplesSetupDependencies { uiActions: UiActionsSetup; } +export interface UiActionExamplesStartDependencies { + uiActions: UiActionsStart; +} + declare module '../../../src/plugins/ui_actions/public' { export interface TriggerContextMapping { [HELLO_WORLD_TRIGGER_ID]: {}; @@ -37,8 +41,12 @@ declare module '../../../src/plugins/ui_actions/public' { } export class UiActionExamplesPlugin - implements Plugin { - public setup(core: CoreSetup, { uiActions }: UiActionExamplesSetupDependencies) { + implements + Plugin { + public setup( + core: CoreSetup, + { uiActions }: UiActionExamplesSetupDependencies + ) { uiActions.registerTrigger(helloWorldTrigger); const helloWorldAction = createHelloWorldAction(async () => ({ @@ -46,9 +54,10 @@ export class UiActionExamplesPlugin })); uiActions.registerAction(helloWorldAction); - uiActions.attachAction(helloWorldTrigger.id, helloWorldAction); + uiActions.addTriggerAction(helloWorldTrigger.id, helloWorldAction); } - public start() {} + public start(core: CoreStart, plugins: UiActionExamplesStartDependencies) {} + public stop() {} } diff --git a/examples/ui_actions_explorer/public/app.tsx b/examples/ui_actions_explorer/public/app.tsx index 462f5c3bf88ba..f08b8bb29bdd3 100644 --- a/examples/ui_actions_explorer/public/app.tsx +++ b/examples/ui_actions_explorer/public/app.tsx @@ -95,8 +95,7 @@ const ActionsExplorer = ({ uiActionsApi, openModal }: Props) => { ); }, }); - uiActionsApi.registerAction(dynamicAction); - uiActionsApi.attachAction(HELLO_WORLD_TRIGGER_ID, dynamicAction); + uiActionsApi.addTriggerAction(HELLO_WORLD_TRIGGER_ID, dynamicAction); setConfirmationText( `You've successfully added a new action: ${dynamicAction.getDisplayName( {} diff --git a/examples/ui_actions_explorer/public/plugin.tsx b/examples/ui_actions_explorer/public/plugin.tsx index f1895905a45e1..de86b51aee3a8 100644 --- a/examples/ui_actions_explorer/public/plugin.tsx +++ b/examples/ui_actions_explorer/public/plugin.tsx @@ -79,21 +79,21 @@ export class UiActionsExplorerPlugin implements Plugin (await startServices)[1].uiActions) ); - deps.uiActions.attachAction( + deps.uiActions.addTriggerAction( USER_TRIGGER, createEditUserAction(async () => (await startServices)[0].overlays.openModal) ); - deps.uiActions.attachAction(COUNTRY_TRIGGER, viewInMapsAction); - deps.uiActions.attachAction(COUNTRY_TRIGGER, lookUpWeatherAction); - deps.uiActions.attachAction(COUNTRY_TRIGGER, showcasePluggability); - deps.uiActions.attachAction(PHONE_TRIGGER, makePhoneCallAction); - deps.uiActions.attachAction(PHONE_TRIGGER, showcasePluggability); - deps.uiActions.attachAction(USER_TRIGGER, showcasePluggability); + deps.uiActions.addTriggerAction(COUNTRY_TRIGGER, viewInMapsAction); + deps.uiActions.addTriggerAction(COUNTRY_TRIGGER, lookUpWeatherAction); + deps.uiActions.addTriggerAction(COUNTRY_TRIGGER, showcasePluggability); + deps.uiActions.addTriggerAction(PHONE_TRIGGER, makePhoneCallAction); + deps.uiActions.addTriggerAction(PHONE_TRIGGER, showcasePluggability); + deps.uiActions.addTriggerAction(USER_TRIGGER, showcasePluggability); core.application.register({ id: 'uiActionsExplorer', diff --git a/package.json b/package.json index 1e3ddc976aa67..8a92b46489308 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "test:ftr:server": "node scripts/functional_tests_server", "test:ftr:runner": "node scripts/functional_test_runner", "test:coverage": "grunt test:coverage", - "typespec": "typings-tester --config x-pack/legacy/plugins/canvas/public/lib/aeroelastic/tsconfig.json x-pack/legacy/plugins/canvas/public/lib/aeroelastic/__fixtures__/typescript/typespec_tests.ts", + "typespec": "typings-tester --config x-pack/plugins/canvas/public/lib/aeroelastic/tsconfig.json x-pack/plugins/canvas/public/lib/aeroelastic/__fixtures__/typescript/typespec_tests.ts", "checkLicenses": "node scripts/check_licenses --dev", "build": "node scripts/build --all-platforms", "start": "node scripts/kibana --dev", @@ -122,10 +122,10 @@ "@babel/core": "^7.9.0", "@babel/register": "^7.9.0", "@elastic/apm-rum": "^5.1.1", - "@elastic/charts": "18.4.2", + "@elastic/charts": "19.2.0", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.8.0", - "@elastic/eui": "22.3.0", + "@elastic/eui": "22.3.1", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.4.0", @@ -146,6 +146,7 @@ "@types/tar": "^4.0.3", "JSONStream": "1.3.5", "abortcontroller-polyfill": "^1.4.0", + "accept": "3.0.2", "angular": "^1.7.9", "angular-aria": "^1.7.9", "angular-elastic": "^2.5.1", @@ -199,7 +200,7 @@ "inert": "^5.1.0", "inline-style": "^2.0.0", "joi": "^13.5.2", - "jquery": "^3.4.1", + "jquery": "^3.5.0", "js-yaml": "3.13.1", "json-stable-stringify": "^1.0.1", "json-stringify-pretty-compact": "1.2.0", @@ -296,6 +297,7 @@ "@elastic/eslint-plugin-eui": "0.0.2", "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^5.0.1", + "@elastic/static-fs": "1.0.1", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/eslint-import-resolver-kibana": "2.0.0", @@ -310,6 +312,7 @@ "@percy/agent": "^0.26.0", "@testing-library/react": "^9.3.2", "@testing-library/react-hooks": "^3.2.1", + "@types/accept": "3.1.1", "@types/angular": "^1.6.56", "@types/angular-mocks": "^1.7.0", "@types/babel__core": "^7.1.2", diff --git a/packages/kbn-dev-utils/src/precommit_hook/cli.ts b/packages/kbn-dev-utils/src/precommit_hook/cli.ts new file mode 100644 index 0000000000000..a83e8c2b193d9 --- /dev/null +++ b/packages/kbn-dev-utils/src/precommit_hook/cli.ts @@ -0,0 +1,50 @@ +/* + * 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 Path from 'path'; +import { chmod, writeFile } from 'fs'; +import { promisify } from 'util'; + +import { run } from '../run'; +import { REPO_ROOT } from '../repo_root'; +import { SCRIPT_SOURCE } from './script_source'; +import { getGitDir } from './get_git_dir'; + +const chmodAsync = promisify(chmod); +const writeFileAsync = promisify(writeFile); + +run( + async ({ log }) => { + try { + const gitDir = await getGitDir(); + const installPath = Path.resolve(REPO_ROOT, gitDir, 'hooks/pre-commit'); + + log.info(`Registering Kibana pre-commit git hook...`); + await writeFileAsync(installPath, SCRIPT_SOURCE); + await chmodAsync(installPath, 0o755); + log.success(`Kibana pre-commit git hook was installed successfully.`); + } catch (e) { + log.error(`Kibana pre-commit git hook was not installed as an error occur.`); + throw e; + } + }, + { + description: 'Register git hooks in the local repo', + } +); diff --git a/src/plugins/discover/public/services.ts b/packages/kbn-dev-utils/src/precommit_hook/get_git_dir.ts similarity index 71% rename from src/plugins/discover/public/services.ts rename to packages/kbn-dev-utils/src/precommit_hook/get_git_dir.ts index 37e2144800ea1..5ca7d67d0d4ea 100644 --- a/src/plugins/discover/public/services.ts +++ b/packages/kbn-dev-utils/src/precommit_hook/get_git_dir.ts @@ -17,9 +17,16 @@ * under the License. */ -import { createGetterSetter } from '../../kibana_utils/public'; -import { DocViewsRegistry } from './doc_views/doc_views_registry'; +import execa from 'execa'; -export const [getDocViewsRegistry, setDocViewsRegistry] = createGetterSetter( - 'DocViewsRegistry' -); +import { REPO_ROOT } from '../repo_root'; + +// Retrieves the correct location for the .git dir for +// every git setup (including git worktree) +export async function getGitDir() { + return ( + await execa('git', ['rev-parse', '--git-common-dir'], { + cwd: REPO_ROOT, + }) + ).stdout.trim(); +} diff --git a/packages/kbn-dev-utils/src/precommit_hook/script_source.ts b/packages/kbn-dev-utils/src/precommit_hook/script_source.ts new file mode 100644 index 0000000000000..61b4552f6eaef --- /dev/null +++ b/packages/kbn-dev-utils/src/precommit_hook/script_source.ts @@ -0,0 +1,117 @@ +/* + * 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 os from 'os'; + +import normalizePath from 'normalize-path'; + +const HOME_DIR = normalizePath(os.homedir()); + +export const SCRIPT_SOURCE = `#!/usr/bin/env bash +# +# ** THIS IS AN AUTO-GENERATED FILE ** +# ** PLEASE DO NOT CHANGE IT MANUALLY ** +# +# GENERATED BY \`node scripts/register_git_hook\` +# IF YOU WANNA CHANGE SOMETHING IN THIS SCRIPT +# PLEASE RE-RUN 'yarn kbn bootstrap' or 'node scripts/register_git_hook' + +# pre-commit script takes zero arguments: https://git-scm.com/docs/githooks#_pre_commit + +set -euo pipefail + +# Make it possible to terminate pre commit hook +# using ctrl-c so nothing else would happen or be +# sent to the output. +# +# The correct exit code on that situation +# according the linux documentation project is 130 +# https://www.tldp.org/LDP/abs/html/exitcodes.html +trap "exit 130" INT + +has_node() { + command -v node >/dev/null 2>&1 +} + +has_nvm() { + command -v nvm >/dev/null 2>&1 +} + +try_load_node_from_nvm_paths () { + # If nvm is not loaded, load it + has_node || { + NVM_SH="${HOME_DIR}/.nvm/nvm.sh" + + if [ "${process.platform}" == "darwin" ] && [ -s "$(brew --prefix nvm)/nvm.sh" ]; then + NVM_SH="$(brew --prefix nvm)/nvm.sh" + fi + + export NVM_DIR="${HOME_DIR}/.nvm" + + [ -s "$NVM_SH" ] && \. "$NVM_SH" + + # If nvm has been loaded correctly, use project .nvmrc + has_nvm && nvm use + } +} + +extend_user_path() { + if [ "${process.platform}" == "win32" ]; then + export PATH="$PATH:/c/Program Files/nodejs" + else + export PATH="$PATH:/usr/local/bin:/usr/local" + try_load_node_from_nvm_paths + fi +} + +# Extend path with common path locations for node +# in order to make the hook working on git GUI apps +extend_user_path + +# Check if we have node js bin in path +has_node || { + echo "Can't found node bin in the PATH. Please update the PATH to proceed." + echo "If your PATH already has the node bin, maybe you are using some git GUI app." + echo "Can't found node bin in the PATH. Please update the PATH to proceed." + echo "If your PATH already has the node bin, maybe you are using some git GUI app not launched from the shell." + echo "In order to proceed, you need to config the PATH used by the application that are launching your git GUI app." + echo "If you are running macOS, you can do that using:" + echo "'sudo launchctl config user path /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'" + + exit 1 +} + +execute_precommit_hook() { + node scripts/precommit_hook || return 1 + + PRECOMMIT_FILE="./.git/hooks/pre-commit.local" + if [ -x "\${PRECOMMIT_FILE}" ]; then + echo "Executing local precommit hook found in \${PRECOMMIT_FILE}" + "$PRECOMMIT_FILE" || return 1 + fi +} + +execute_precommit_hook || { + echo "Pre-commit hook failed (add --no-verify to bypass)"; + echo ' For eslint failures you can try running \`node scripts/precommit_hook --fix\`'; + exit 1; +} + +exit 0 +`; diff --git a/packages/kbn-es/src/utils/native_realm.test.js b/packages/kbn-es/src/utils/native_realm.test.js index 99c7ed1623014..54732f7136fcc 100644 --- a/packages/kbn-es/src/utils/native_realm.test.js +++ b/packages/kbn-es/src/utils/native_realm.test.js @@ -109,7 +109,7 @@ describe('setPasswords', () => { mockClient.security.getUser.mockImplementation(() => ({ body: { - kibana: { + kibana_system: { metadata: { _reserved: true, }, @@ -138,7 +138,7 @@ describe('setPasswords', () => { })); await nativeRealm.setPasswords({ - 'password.kibana': 'bar', + 'password.kibana_system': 'bar', }); expect(mockClient.security.changePassword.mock.calls).toMatchInlineSnapshot(` @@ -149,7 +149,7 @@ Array [ "password": "bar", }, "refresh": "wait_for", - "username": "kibana", + "username": "kibana_system", }, ], Array [ @@ -188,7 +188,7 @@ describe('getReservedUsers', () => { it('returns array of reserved usernames', async () => { mockClient.security.getUser.mockImplementation(() => ({ body: { - kibana: { + kibana_system: { metadata: { _reserved: true, }, @@ -206,17 +206,17 @@ describe('getReservedUsers', () => { }, })); - expect(await nativeRealm.getReservedUsers()).toEqual(['kibana', 'logstash_system']); + expect(await nativeRealm.getReservedUsers()).toEqual(['kibana_system', 'logstash_system']); }); }); describe('setPassword', () => { it('sets password for provided user', async () => { - await nativeRealm.setPassword('kibana', 'foo'); + await nativeRealm.setPassword('kibana_system', 'foo'); expect(mockClient.security.changePassword).toHaveBeenCalledWith({ body: { password: 'foo' }, refresh: 'wait_for', - username: 'kibana', + username: 'kibana_system', }); }); @@ -226,7 +226,7 @@ describe('setPassword', () => { }); await expect( - nativeRealm.setPassword('kibana', 'foo') + nativeRealm.setPassword('kibana_system', 'foo') ).rejects.toThrowErrorMatchingInlineSnapshot(`"SomeError"`); }); }); diff --git a/packages/kbn-interpreter/tasks/build/server_code_transformer.js b/packages/kbn-interpreter/tasks/build/server_code_transformer.js deleted file mode 100644 index 4bd9220993c62..0000000000000 --- a/packages/kbn-interpreter/tasks/build/server_code_transformer.js +++ /dev/null @@ -1,43 +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. - */ - -const { extname } = require('path'); - -const { transform } = require('@babel/core'); - -exports.createServerCodeTransformer = sourceMaps => { - return (content, path) => { - switch (extname(path)) { - case '.js': - const { code = '' } = transform(content.toString('utf8'), { - filename: path, - ast: false, - code: true, - sourceMaps: sourceMaps ? 'inline' : false, - babelrc: false, - presets: [require.resolve('@kbn/babel-preset/webpack_preset')], - }); - - return code; - - default: - return content.toString('utf8'); - } - }; -}; diff --git a/packages/kbn-interpreter/tasks/build/server_code_transformer.test.js b/packages/kbn-interpreter/tasks/build/server_code_transformer.test.js deleted file mode 100644 index 519e529c20bf5..0000000000000 --- a/packages/kbn-interpreter/tasks/build/server_code_transformer.test.js +++ /dev/null @@ -1,48 +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 { readFileSync } from 'fs'; -import { resolve } from 'path'; -import { createServerCodeTransformer } from './server_code_transformer'; - -const JS_FIXTURE_PATH = resolve(__dirname, '__fixtures__/sample.js'); -const JS_FIXTURE = readFileSync(JS_FIXTURE_PATH); - -describe('js support', () => { - it('transpiles js file', () => { - const transformer = createServerCodeTransformer(); - expect(transformer(JS_FIXTURE, JS_FIXTURE_PATH)).toMatchInlineSnapshot(` -"\\"use strict\\"; - -var _util = _interopRequireDefault(require(\\"util\\")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/* eslint-disable */ -console.log(_util.default.format('hello world'));" -`); - }); - - it('throws errors for js syntax errors', () => { - const transformer = createServerCodeTransformer(); - expect(() => transformer(Buffer.from(`export default 'foo`), JS_FIXTURE_PATH)).toThrowError( - /Unterminated string constant/ - ); - }); -}); diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index b3e5a8c518682..b7c9a63897bf9 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -14,6 +14,7 @@ "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", + "@types/compression-webpack-plugin": "^2.0.1", "@types/estree": "^0.0.44", "@types/loader-utils": "^1.1.3", "@types/watchpack": "^1.1.5", @@ -23,6 +24,7 @@ "autoprefixer": "^9.7.4", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", + "compression-webpack-plugin": "^3.1.0", "cpy": "^8.0.0", "css-loader": "^3.4.2", "del": "^5.1.0", diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts index ad743933e1171..248b0b7cf4c97 100644 --- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts @@ -19,6 +19,7 @@ import Path from 'path'; import Fs from 'fs'; +import Zlib from 'zlib'; import { inspect } from 'util'; import cpy from 'cpy'; @@ -124,17 +125,12 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { ); assert('produce zero unexpected states', otherStates.length === 0, otherStates); - expect( - Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, 'plugins/foo/target/public/foo.plugin.js'), 'utf8') - ).toMatchSnapshot('foo bundle'); - - expect( - Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, 'plugins/foo/target/public/1.plugin.js'), 'utf8') - ).toMatchSnapshot('1 async bundle'); - - expect( - Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, 'plugins/bar/target/public/bar.plugin.js'), 'utf8') - ).toMatchSnapshot('bar bundle'); + expectFileMatchesSnapshotWithCompression('plugins/foo/target/public/foo.plugin.js', 'foo bundle'); + expectFileMatchesSnapshotWithCompression( + 'plugins/foo/target/public/1.plugin.js', + '1 async bundle' + ); + expectFileMatchesSnapshotWithCompression('plugins/bar/target/public/bar.plugin.js', 'bar bundle'); const foo = config.bundles.find(b => b.id === 'foo')!; expect(foo).toBeTruthy(); @@ -203,3 +199,24 @@ it('uses cache on second run and exist cleanly', async () => { ] `); }); + +/** + * Verifies that the file matches the expected output and has matching compressed variants. + */ +const expectFileMatchesSnapshotWithCompression = (filePath: string, snapshotLabel: string) => { + const raw = Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, filePath), 'utf8'); + expect(raw).toMatchSnapshot(snapshotLabel); + + // Verify the brotli variant matches + expect( + // @ts-ignore @types/node is missing the brotli functions + Zlib.brotliDecompressSync( + Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, `${filePath}.br`)) + ).toString() + ).toEqual(raw); + + // Verify the gzip variant matches + expect( + Zlib.gunzipSync(Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, `${filePath}.gz`))).toString() + ).toEqual(raw); +}; diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index cc3fa8c2720de..95e826e7620aa 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -28,6 +28,7 @@ import TerserPlugin from 'terser-webpack-plugin'; import webpackMerge from 'webpack-merge'; // @ts-ignore import { CleanWebpackPlugin } from 'clean-webpack-plugin'; +import CompressionPlugin from 'compression-webpack-plugin'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; import { Bundle, WorkerConfig, parseDirPath, DisallowedSyntaxPlugin } from '../common'; @@ -319,6 +320,16 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { IS_KIBANA_DISTRIBUTABLE: `"true"`, }, }), + new CompressionPlugin({ + algorithm: 'brotliCompress', + filename: '[path].br', + test: /\.(js|css)$/, + }), + new CompressionPlugin({ + algorithm: 'gzip', + filename: '[path].gz', + test: /\.(js|css)$/, + }), ], optimization: { diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index 5ea031595d1d4..47ed69bc95697 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -50,7 +50,7 @@ "html": "1.0.0", "html-loader": "^0.5.5", "imports-loader": "^0.8.0", - "jquery": "^3.4.1", + "jquery": "^3.5.0", "keymirror": "0.1.1", "moment": "^2.24.0", "node-sass": "^4.13.1", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index a60e2b0449d95..8259f251a9be3 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,15 +9,16 @@ "kbn:watch": "node scripts/build --watch" }, "dependencies": { - "@elastic/charts": "18.4.2", - "@elastic/eui": "22.3.0", + "@elastic/charts": "19.2.0", + "@elastic/eui": "22.3.1", "@kbn/i18n": "1.0.0", "abortcontroller-polyfill": "^1.4.0", "angular": "^1.7.9", + "compression-webpack-plugin": "^3.1.0", "core-js": "^3.6.4", "custom-event-polyfill": "^0.3.0", "elasticsearch-browser": "^16.7.0", - "jquery": "^3.4.1", + "jquery": "^3.5.0", "moment": "^2.24.0", "moment-timezone": "^0.5.27", "monaco-editor": "~0.17.0", diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index bf63c57765859..ca913d0f16417 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -20,6 +20,7 @@ const Path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const CompressionPlugin = require('compression-webpack-plugin'); const { REPO_ROOT } = require('@kbn/dev-utils'); const webpack = require('webpack'); @@ -117,5 +118,19 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ new webpack.DefinePlugin({ 'process.env.NODE_ENV': dev ? '"development"' : '"production"', }), + ...(dev + ? [] + : [ + new CompressionPlugin({ + algorithm: 'brotliCompress', + filename: '[path].br', + test: /\.(js|css)$/, + }), + new CompressionPlugin({ + algorithm: 'gzip', + filename: '[path].gz', + test: /\.(js|css)$/, + }), + ]), ], }); diff --git a/renovate.json5 b/renovate.json5 index c0ddcaf4f23c8..c4efa86366bf4 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -398,6 +398,8 @@ '@types/good-squeeze', 'inert', '@types/inert', + 'accept', + '@types/accept', ], }, { @@ -769,6 +771,14 @@ '@types/podium', ], }, + { + groupSlug: 'pretty-ms', + groupName: 'pretty-ms related packages', + packageNames: [ + 'pretty-ms', + '@types/pretty-ms', + ], + }, { groupSlug: 'proper-lockfile', groupName: 'proper-lockfile related packages', @@ -862,6 +872,14 @@ '@types/sinon', ], }, + { + groupSlug: 'stats-lite', + groupName: 'stats-lite related packages', + packageNames: [ + 'stats-lite', + '@types/stats-lite', + ], + }, { groupSlug: 'storybook', groupName: 'storybook related packages', diff --git a/scripts/kibana.js b/scripts/kibana.js index f5a63e6c07dd6..4da739469ffb1 100644 --- a/scripts/kibana.js +++ b/scripts/kibana.js @@ -17,6 +17,6 @@ * under the License. */ -require('../src/apm')(process.env.ELASTIC_APM_PROXY_SERVICE_NAME || 'kibana-proxy'); require('../src/setup_node_env'); +require('../src/apm')(process.env.ELASTIC_APM_PROXY_SERVICE_NAME || 'kibana-proxy'); require('../src/cli/cli'); diff --git a/scripts/register_git_hook.js b/scripts/register_git_hook.js index 8e03f17967f3f..af3f54619bcec 100644 --- a/scripts/register_git_hook.js +++ b/scripts/register_git_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env'); -require('../src/dev/run_register_git_hook'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('@kbn/dev-utils/target/precommit_hook/cli'); diff --git a/src/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts index 97dec3eead303..3b3e4d78320d2 100644 --- a/src/cli/cluster/cluster_manager.ts +++ b/src/cli/cluster/cluster_manager.ts @@ -268,7 +268,7 @@ export class ClusterManager { fromRoot('x-pack/plugins/siem/cypress'), fromRoot('x-pack/plugins/apm/e2e'), fromRoot('x-pack/plugins/apm/scripts'), - fromRoot('x-pack/legacy/plugins/canvas/canvas_plugin_src'), // prevents server from restarting twice for Canvas plugin changes, + fromRoot('x-pack/plugins/canvas/canvas_plugin_src'), // prevents server from restarting twice for Canvas plugin changes, 'plugins/java_languageserver', ]; diff --git a/src/cli/index.js b/src/cli/index.js index 45f88eaf82a5b..6dbdd800268a9 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -17,6 +17,6 @@ * under the License. */ -require('../apm')(); require('../setup_node_env'); +require('../apm')(); require('./cli'); diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 29d0fe16ee126..471939121143a 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -79,7 +79,7 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { set('optimize.watch', true); if (!has('elasticsearch.username')) { - set('elasticsearch.username', 'kibana'); + set('elasticsearch.username', 'kibana_system'); } if (!has('elasticsearch.password')) { diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 80f12dd78214d..02d46b1583b59 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -990,6 +990,9 @@ ls -lh plugins/my_plugin/target/public/ you might see at least one js bundle - `my_plugin.plugin.js`. This is the only artifact loaded by the platform during bootstrap in the browser. The rule of thumb is to keep its size as small as possible. Other lazily loaded parts of your plugin present in the same folder as separate chunks under `{number}.plugin.js` names. If you want to investigate what your plugin bundle consists of you need to run `@kbn/optimizer` with `--profile` flag to get generated [webpack stats file](https://webpack.js.org/api/stats/). +```bash +node scripts/build_kibana_platform_plugins.js --dist --no-examples --profile +``` Many OSS tools are allowing you to analyze generated stats file - [an official tool](http://webpack.github.io/analyse/#modules) from webpack authors - [webpack-visualizer](https://chrisbateman.github.io/webpack-visualizer/) diff --git a/src/core/public/application/__snapshots__/application_service.test.ts.snap b/src/core/public/application/__snapshots__/application_service.test.ts.snap index 376b320b64ea9..c085fb028cd5a 100644 --- a/src/core/public/application/__snapshots__/application_service.test.ts.snap +++ b/src/core/public/application/__snapshots__/application_service.test.ts.snap @@ -80,5 +80,6 @@ exports[`#start() getComponent returns renderable JSX tree 1`] = ` } mounters={Map {}} setAppLeaveHandler={[Function]} + setIsMounting={[Function]} /> `; diff --git a/src/core/public/application/application_service.test.ts b/src/core/public/application/application_service.test.ts index e29837aecb125..04ff844ffc150 100644 --- a/src/core/public/application/application_service.test.ts +++ b/src/core/public/application/application_service.test.ts @@ -20,7 +20,7 @@ import { createElement } from 'react'; import { BehaviorSubject, Subject } from 'rxjs'; import { bufferCount, take, takeUntil } from 'rxjs/operators'; -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock'; import { contextServiceMock } from '../context/context_service.mock'; @@ -30,6 +30,7 @@ import { MockCapabilitiesService, MockHistory } from './application_service.test import { MockLifecycle } from './test_types'; import { ApplicationService } from './application_service'; import { App, AppNavLinkStatus, AppStatus, AppUpdater, LegacyApp } from './types'; +import { act } from 'react-dom/test-utils'; const createApp = (props: Partial): App => { return { @@ -452,9 +453,9 @@ describe('#setup()', () => { const container = setupDeps.context.createContextContainer.mock.results[0].value; const pluginId = Symbol(); - const mount = () => () => undefined; - registerMountContext(pluginId, 'test' as any, mount); - expect(container.registerContext).toHaveBeenCalledWith(pluginId, 'test', mount); + const appMount = () => () => undefined; + registerMountContext(pluginId, 'test' as any, appMount); + expect(container.registerContext).toHaveBeenCalledWith(pluginId, 'test', appMount); }); }); @@ -809,6 +810,74 @@ describe('#start()', () => { `); }); + it('updates httpLoadingCount$ while mounting', async () => { + // Use a memory history so that mounting the component will work + const { createMemoryHistory } = jest.requireActual('history'); + const history = createMemoryHistory(); + setupDeps.history = history; + + const flushPromises = () => new Promise(resolve => setImmediate(resolve)); + // Create an app and a promise that allows us to control when the app completes mounting + const createWaitingApp = (props: Partial): [App, () => void] => { + let finishMount: () => void; + const mountPromise = new Promise(resolve => (finishMount = resolve)); + const app = { + id: 'some-id', + title: 'some-title', + mount: async () => { + await mountPromise; + return () => undefined; + }, + ...props, + }; + + return [app, finishMount!]; + }; + + // Create some dummy applications + const { register } = service.setup(setupDeps); + const [alphaApp, finishAlphaMount] = createWaitingApp({ id: 'alpha' }); + const [betaApp, finishBetaMount] = createWaitingApp({ id: 'beta' }); + register(Symbol(), alphaApp); + register(Symbol(), betaApp); + + const { navigateToApp, getComponent } = await service.start(startDeps); + const httpLoadingCount$ = startDeps.http.addLoadingCountSource.mock.calls[0][0]; + const stop$ = new Subject(); + const currentLoadingCount$ = new BehaviorSubject(0); + httpLoadingCount$.pipe(takeUntil(stop$)).subscribe(currentLoadingCount$); + const loadingPromise = httpLoadingCount$.pipe(bufferCount(5), takeUntil(stop$)).toPromise(); + mount(getComponent()!); + + await act(() => navigateToApp('alpha')); + expect(currentLoadingCount$.value).toEqual(1); + await act(async () => { + finishAlphaMount(); + await flushPromises(); + }); + expect(currentLoadingCount$.value).toEqual(0); + + await act(() => navigateToApp('beta')); + expect(currentLoadingCount$.value).toEqual(1); + await act(async () => { + finishBetaMount(); + await flushPromises(); + }); + expect(currentLoadingCount$.value).toEqual(0); + + stop$.next(); + const loadingCounts = await loadingPromise; + expect(loadingCounts).toMatchInlineSnapshot(` + Array [ + 0, + 1, + 0, + 1, + 0, + ] + `); + }); + it('sets window.location.href when navigating to legacy apps', async () => { setupDeps.http = httpServiceMock.createSetupContract({ basePath: '/test' }); setupDeps.injectedMetadata.getLegacyMode.mockReturnValue(true); diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index bafa1932e5e92..8442f1ecc6411 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -26,6 +26,7 @@ import { InjectedMetadataSetup } from '../injected_metadata'; import { HttpSetup, HttpStart } from '../http'; import { OverlayStart } from '../overlays'; import { ContextSetup, IContextContainer } from '../context'; +import { PluginOpaqueId } from '../plugins'; import { AppRouter } from './ui'; import { Capabilities, CapabilitiesService } from './capabilities'; import { @@ -34,7 +35,6 @@ import { AppLeaveHandler, AppMount, AppMountDeprecated, - AppMounter, AppNavLinkStatus, AppStatus, AppUpdatableFields, @@ -145,6 +145,25 @@ export class ApplicationService { this.subscriptions.push(subscription); }; + const wrapMount = (plugin: PluginOpaqueId, app: App): AppMount => { + let handler: AppMount; + if (isAppMountDeprecated(app.mount)) { + handler = this.mountContext!.createHandler(plugin, app.mount); + if (process.env.NODE_ENV === 'development') { + // eslint-disable-next-line no-console + console.warn( + `App [${app.id}] is using deprecated mount context. Use core.getStartServices() instead.` + ); + } + } else { + handler = app.mount; + } + return async params => { + this.currentAppId$.next(app.id); + return handler(params); + }; + }; + return { registerMountContext: this.mountContext!.registerContext, register: (plugin, app: App) => { @@ -162,24 +181,6 @@ export class ApplicationService { throw new Error('Cannot register an application route that includes HTTP base path'); } - let handler: AppMount; - - if (isAppMountDeprecated(app.mount)) { - handler = this.mountContext!.createHandler(plugin, app.mount); - // eslint-disable-next-line no-console - console.warn( - `App [${app.id}] is using deprecated mount context. Use core.getStartServices() instead.` - ); - } else { - handler = app.mount; - } - - const mount: AppMounter = async params => { - const unmount = await handler(params); - this.currentAppId$.next(app.id); - return unmount; - }; - const { updater$, ...appProps } = app; this.apps.set(app.id, { ...appProps, @@ -193,7 +194,7 @@ export class ApplicationService { this.mounters.set(app.id, { appRoute: app.appRoute!, appBasePath: basePath.prepend(app.appRoute!), - mount, + mount: wrapMount(plugin, app), unmountBeforeMounting: false, }); }, @@ -238,6 +239,9 @@ export class ApplicationService { throw new Error('ApplicationService#setup() must be invoked before start.'); } + const httpLoadingCount$ = new BehaviorSubject(0); + http.addLoadingCountSource(httpLoadingCount$); + this.registrationClosed = true; window.addEventListener('beforeunload', this.onBeforeUnload); @@ -303,6 +307,7 @@ export class ApplicationService { mounters={availableMounters} appStatuses$={applicationStatuses$} setAppLeaveHandler={this.setAppLeaveHandler} + setIsMounting={isMounting => httpLoadingCount$.next(isMounting ? 1 : 0)} /> ); }, diff --git a/src/core/public/application/integration_tests/application_service.test.tsx b/src/core/public/application/integration_tests/application_service.test.tsx index edf3583f384b8..60c36d3e330e0 100644 --- a/src/core/public/application/integration_tests/application_service.test.tsx +++ b/src/core/public/application/integration_tests/application_service.test.tsx @@ -17,6 +17,7 @@ * under the License. */ +import { take } from 'rxjs/operators'; import { createRenderer } from './utils'; import { createMemoryHistory, MemoryHistory } from 'history'; import { ApplicationService } from '../application_service'; @@ -56,6 +57,69 @@ describe('ApplicationService', () => { service = new ApplicationService(); }); + describe('navigating to apps', () => { + describe('using history.push', () => { + it('emits currentAppId$ before mounting the app', async () => { + const { register } = service.setup(setupDeps); + + let resolveMount: () => void; + const promise = new Promise(resolve => { + resolveMount = resolve; + }); + + register(Symbol(), { + id: 'app1', + title: 'App1', + mount: async ({}: AppMountParameters) => { + await promise; + return () => undefined; + }, + }); + + const { currentAppId$, getComponent } = await service.start(startDeps); + update = createRenderer(getComponent()); + + await navigate('/app/app1'); + + expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1'); + + resolveMount!(); + + expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1'); + }); + }); + + describe('using navigateToApp', () => { + it('emits currentAppId$ before mounting the app', async () => { + const { register } = service.setup(setupDeps); + + let resolveMount: () => void; + const promise = new Promise(resolve => { + resolveMount = resolve; + }); + + register(Symbol(), { + id: 'app1', + title: 'App1', + mount: async ({}: AppMountParameters) => { + await promise; + return () => undefined; + }, + }); + + const { navigateToApp, currentAppId$ } = await service.start(startDeps); + + await navigateToApp('app1'); + + expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1'); + + resolveMount!(); + + expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1'); + }); + }); + }); + describe('leaving an application that registered an app leave handler', () => { it('navigates to the new app if action is default', async () => { startDeps.overlays.openConfirm.mockResolvedValue(true); diff --git a/src/core/public/application/integration_tests/router.test.tsx b/src/core/public/application/integration_tests/router.test.tsx index 2f26bc1409104..915c58b28ad6d 100644 --- a/src/core/public/application/integration_tests/router.test.tsx +++ b/src/core/public/application/integration_tests/router.test.tsx @@ -40,7 +40,7 @@ describe('AppContainer', () => { }; const mockMountersToMounters = () => new Map([...mounters].map(([appId, { mounter }]) => [appId, mounter])); - const setAppLeaveHandlerMock = () => undefined; + const noop = () => undefined; const mountersToAppStatus$ = () => { return new BehaviorSubject( @@ -86,7 +86,8 @@ describe('AppContainer', () => { history={globalHistory} mounters={mockMountersToMounters()} appStatuses$={appStatuses$} - setAppLeaveHandler={setAppLeaveHandlerMock} + setAppLeaveHandler={noop} + setIsMounting={noop} /> ); }); @@ -98,7 +99,7 @@ describe('AppContainer', () => { expect(app1.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -110,7 +111,7 @@ describe('AppContainer', () => { expect(app1Unmount).toHaveBeenCalled(); expect(app2.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app2 html:
App 2
" @@ -124,7 +125,7 @@ describe('AppContainer', () => { expect(standardApp.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -136,7 +137,7 @@ describe('AppContainer', () => { expect(standardAppUnmount).toHaveBeenCalled(); expect(chromelessApp.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" @@ -148,7 +149,7 @@ describe('AppContainer', () => { expect(chromelessAppUnmount).toHaveBeenCalled(); expect(standardApp.mounter.mount).toHaveBeenCalledTimes(2); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -162,7 +163,7 @@ describe('AppContainer', () => { expect(chromelessAppA.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" @@ -174,7 +175,7 @@ describe('AppContainer', () => { expect(chromelessAppAUnmount).toHaveBeenCalled(); expect(chromelessAppB.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-b/path html:
Chromeless B
" @@ -186,7 +187,7 @@ describe('AppContainer', () => { expect(chromelessAppBUnmount).toHaveBeenCalled(); expect(chromelessAppA.mounter.mount).toHaveBeenCalledTimes(2); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" @@ -214,7 +215,8 @@ describe('AppContainer', () => { history={globalHistory} mounters={mockMountersToMounters()} appStatuses$={mountersToAppStatus$()} - setAppLeaveHandler={setAppLeaveHandlerMock} + setAppLeaveHandler={noop} + setIsMounting={noop} /> ); @@ -245,7 +247,8 @@ describe('AppContainer', () => { history={globalHistory} mounters={mockMountersToMounters()} appStatuses$={mountersToAppStatus$()} - setAppLeaveHandler={setAppLeaveHandlerMock} + setAppLeaveHandler={noop} + setIsMounting={noop} /> ); @@ -286,7 +289,8 @@ describe('AppContainer', () => { history={globalHistory} mounters={mockMountersToMounters()} appStatuses$={mountersToAppStatus$()} - setAppLeaveHandler={setAppLeaveHandlerMock} + setAppLeaveHandler={noop} + setIsMounting={noop} /> ); diff --git a/src/core/public/application/integration_tests/utils.tsx b/src/core/public/application/integration_tests/utils.tsx index 9092177da5ad4..fa04b56f83ba1 100644 --- a/src/core/public/application/integration_tests/utils.tsx +++ b/src/core/public/application/integration_tests/utils.tsx @@ -18,6 +18,7 @@ */ import React, { ReactElement } from 'react'; +import { act } from 'react-dom/test-utils'; import { mount } from 'enzyme'; import { I18nProvider } from '@kbn/i18n/react'; @@ -34,7 +35,9 @@ export const createRenderer = (element: ReactElement | null): Renderer => { return () => new Promise(async resolve => { if (dom) { - dom.update(); + await act(async () => { + dom.update(); + }); } setImmediate(() => resolve(dom)); // flushes any pending promises }); diff --git a/src/core/public/application/ui/app_container.scss b/src/core/public/application/ui/app_container.scss new file mode 100644 index 0000000000000..4f8fec10a97e1 --- /dev/null +++ b/src/core/public/application/ui/app_container.scss @@ -0,0 +1,25 @@ +.appContainer__loading { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: $euiZLevel1; + animation-name: appContainerFadeIn; + animation-iteration-count: 1; + animation-timing-function: ease-in; + animation-duration: 2s; +} + +@keyframes appContainerFadeIn { + 0% { + opacity: 0; + } + + 50% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} diff --git a/src/core/public/application/ui/app_container.test.tsx b/src/core/public/application/ui/app_container.test.tsx index c538227e8f098..2ee71a5bde7dc 100644 --- a/src/core/public/application/ui/app_container.test.tsx +++ b/src/core/public/application/ui/app_container.test.tsx @@ -18,6 +18,7 @@ */ import React from 'react'; +import { act } from 'react-dom/test-utils'; import { mount } from 'enzyme'; import { AppContainer } from './app_container'; @@ -28,6 +29,12 @@ import { ScopedHistory } from '../scoped_history'; describe('AppContainer', () => { const appId = 'someApp'; const setAppLeaveHandler = jest.fn(); + const setIsMounting = jest.fn(); + + beforeEach(() => { + setAppLeaveHandler.mockClear(); + setIsMounting.mockClear(); + }); const flushPromises = async () => { await new Promise(async resolve => { @@ -67,6 +74,7 @@ describe('AppContainer', () => { appStatus={AppStatus.inaccessible} mounter={mounter} setAppLeaveHandler={setAppLeaveHandler} + setIsMounting={setIsMounting} createScopedHistory={(appPath: string) => // Create a history using the appPath as the current location new ScopedHistory(createMemoryHistory({ initialEntries: [appPath] }), appPath) @@ -86,10 +94,86 @@ describe('AppContainer', () => { expect(wrapper.text()).toEqual(''); - resolvePromise(); - await flushPromises(); - wrapper.update(); + await act(async () => { + resolvePromise(); + await flushPromises(); + wrapper.update(); + }); expect(wrapper.text()).toContain('some-content'); }); + + it('should call setIsMounting while mounting', async () => { + const [waitPromise, resolvePromise] = createResolver(); + const mounter = createMounter(waitPromise); + + const wrapper = mount( + + // Create a history using the appPath as the current location + new ScopedHistory(createMemoryHistory({ initialEntries: [appPath] }), appPath) + } + /> + ); + + expect(setIsMounting).toHaveBeenCalledTimes(1); + expect(setIsMounting).toHaveBeenLastCalledWith(true); + + await act(async () => { + resolvePromise(); + await flushPromises(); + wrapper.update(); + }); + + expect(setIsMounting).toHaveBeenCalledTimes(2); + expect(setIsMounting).toHaveBeenLastCalledWith(false); + }); + + it('should call setIsMounting(false) if mounting throws', async () => { + const [waitPromise, resolvePromise] = createResolver(); + const mounter = { + appBasePath: '/base-path', + appRoute: '/some-route', + unmountBeforeMounting: false, + mount: async ({ element }: AppMountParameters) => { + await waitPromise; + throw new Error(`Mounting failed!`); + }, + }; + + const wrapper = mount( + + // Create a history using the appPath as the current location + new ScopedHistory(createMemoryHistory({ initialEntries: [appPath] }), appPath) + } + /> + ); + + expect(setIsMounting).toHaveBeenCalledTimes(1); + expect(setIsMounting).toHaveBeenLastCalledWith(true); + + // await expect( + await act(async () => { + resolvePromise(); + await flushPromises(); + wrapper.update(); + }); + // ).rejects.toThrow(); + + expect(setIsMounting).toHaveBeenCalledTimes(2); + expect(setIsMounting).toHaveBeenLastCalledWith(false); + }); }); diff --git a/src/core/public/application/ui/app_container.tsx b/src/core/public/application/ui/app_container.tsx index e12a0f2cf2fcd..aad7e6dcf270a 100644 --- a/src/core/public/application/ui/app_container.tsx +++ b/src/core/public/application/ui/app_container.tsx @@ -26,9 +26,11 @@ import React, { MutableRefObject, } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { AppLeaveHandler, AppStatus, AppUnmount, Mounter } from '../types'; import { AppNotFound } from './app_not_found_screen'; import { ScopedHistory } from '../scoped_history'; +import './app_container.scss'; interface Props { /** Path application is mounted on without the global basePath */ @@ -38,6 +40,7 @@ interface Props { appStatus: AppStatus; setAppLeaveHandler: (appId: string, handler: AppLeaveHandler) => void; createScopedHistory: (appUrl: string) => ScopedHistory; + setIsMounting: (isMounting: boolean) => void; } export const AppContainer: FunctionComponent = ({ @@ -47,7 +50,9 @@ export const AppContainer: FunctionComponent = ({ setAppLeaveHandler, createScopedHistory, appStatus, + setIsMounting, }: Props) => { + const [showSpinner, setShowSpinner] = useState(true); const [appNotFound, setAppNotFound] = useState(false); const elementRef = useRef(null); const unmountRef: MutableRefObject = useRef(null); @@ -65,28 +70,42 @@ export const AppContainer: FunctionComponent = ({ } setAppNotFound(false); + setIsMounting(true); if (mounter.unmountBeforeMounting) { unmount(); } const mount = async () => { - unmountRef.current = - (await mounter.mount({ - appBasePath: mounter.appBasePath, - history: createScopedHistory(appPath), - element: elementRef.current!, - onAppLeave: handler => setAppLeaveHandler(appId, handler), - })) || null; + setShowSpinner(true); + try { + unmountRef.current = + (await mounter.mount({ + appBasePath: mounter.appBasePath, + history: createScopedHistory(appPath), + element: elementRef.current!, + onAppLeave: handler => setAppLeaveHandler(appId, handler), + })) || null; + } catch (e) { + // TODO: add error UI + } finally { + setShowSpinner(false); + setIsMounting(false); + } }; mount(); return unmount; - }, [appId, appStatus, mounter, createScopedHistory, setAppLeaveHandler, appPath]); + }, [appId, appStatus, mounter, createScopedHistory, setAppLeaveHandler, appPath, setIsMounting]); return ( {appNotFound && } + {showSpinner && ( +
+ +
+ )}
); diff --git a/src/core/public/application/ui/app_router.tsx b/src/core/public/application/ui/app_router.tsx index 4c135c5769067..ea7c5c9308fe2 100644 --- a/src/core/public/application/ui/app_router.tsx +++ b/src/core/public/application/ui/app_router.tsx @@ -32,6 +32,7 @@ interface Props { history: History; appStatuses$: Observable>; setAppLeaveHandler: (appId: string, handler: AppLeaveHandler) => void; + setIsMounting: (isMounting: boolean) => void; } interface Params { @@ -43,6 +44,7 @@ export const AppRouter: FunctionComponent = ({ mounters, setAppLeaveHandler, appStatuses$, + setIsMounting, }) => { const appStatuses = useObservable(appStatuses$, new Map()); const createScopedHistory = useMemo( @@ -67,7 +69,7 @@ export const AppRouter: FunctionComponent = ({ appPath={url} appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible} createScopedHistory={createScopedHistory} - {...{ appId, mounter, setAppLeaveHandler }} + {...{ appId, mounter, setAppLeaveHandler, setIsMounting }} /> )} />, @@ -92,7 +94,7 @@ export const AppRouter: FunctionComponent = ({ appId={id} appStatus={appStatuses.get(id) ?? AppStatus.inaccessible} createScopedHistory={createScopedHistory} - {...{ mounter, setAppLeaveHandler }} + {...{ mounter, setAppLeaveHandler, setIsMounting }} /> ); }} diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index 89007461b63e6..4a79dd8869c1c 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -23,7 +23,8 @@ import { ChromeBreadcrumb, ChromeService, InternalChromeStart, -} from './chrome_service'; + NavType, +} from './'; const createStartContractMock = () => { const startContract: DeeplyMockedKeys = { @@ -72,6 +73,7 @@ const createStartContractMock = () => { setHelpExtension: jest.fn(), setHelpSupportUrl: jest.fn(), getIsNavDrawerLocked$: jest.fn(), + getNavType$: jest.fn(), }; startContract.navLinks.getAll.mockReturnValue([]); startContract.getBrand$.mockReturnValue(new BehaviorSubject({} as ChromeBrand)); @@ -81,6 +83,7 @@ const createStartContractMock = () => { startContract.getBreadcrumbs$.mockReturnValue(new BehaviorSubject([{} as ChromeBreadcrumb])); startContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined)); startContract.getIsNavDrawerLocked$.mockReturnValue(new BehaviorSubject(false)); + startContract.getNavType$.mockReturnValue(new BehaviorSubject('modern' as NavType)); return startContract; }; diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index bf531aaa00fac..327be61cc63e3 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -17,19 +17,18 @@ * under the License. */ -import * as Rx from 'rxjs'; -import { take, toArray } from 'rxjs/operators'; import { shallow } from 'enzyme'; import React from 'react'; - +import * as Rx from 'rxjs'; +import { take, toArray } from 'rxjs/operators'; +import { App } from '../application'; import { applicationServiceMock } from '../application/application_service.mock'; +import { docLinksServiceMock } from '../doc_links/doc_links_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock'; import { notificationServiceMock } from '../notifications/notifications_service.mock'; -import { docLinksServiceMock } from '../doc_links/doc_links_service.mock'; -import { ChromeService } from './chrome_service'; -import { App } from '../application'; import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock'; +import { ChromeService } from './chrome_service'; class FakeApp implements App { public title = `${this.id} App`; @@ -163,7 +162,7 @@ describe('start', () => { }); describe('visibility', () => { - it('updates/emits the visibility', async () => { + it('emits false when no application is mounted', async () => { const { chrome, service } = await start(); const promise = chrome .getIsVisible$() @@ -177,33 +176,37 @@ describe('start', () => { await expect(promise).resolves.toMatchInlineSnapshot(` Array [ - true, - true, false, - true, + false, + false, + false, ] `); }); - it('always emits false if embed query string is preset when set up', async () => { + it('emits false until manually overridden when in embed mode', async () => { window.history.pushState(undefined, '', '#/home?a=b&embed=true'); + const startDeps = defaultStartDeps([new FakeApp('alpha')]); + const { navigateToApp } = startDeps.application; + const { chrome, service } = await start({ startDeps }); - const { chrome, service } = await start(); const promise = chrome .getIsVisible$() .pipe(toArray()) .toPromise(); + await navigateToApp('alpha'); + chrome.setIsVisible(true); chrome.setIsVisible(false); - chrome.setIsVisible(true); + service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` Array [ false, false, - false, + true, false, ] `); @@ -228,7 +231,7 @@ describe('start', () => { await expect(promise).resolves.toMatchInlineSnapshot(` Array [ - true, + false, true, false, true, @@ -245,13 +248,13 @@ describe('start', () => { .pipe(toArray()) .toPromise(); - navigateToApp('alpha'); + await navigateToApp('alpha'); chrome.setIsVisible(true); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` Array [ - true, + false, false, false, ] diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 7c9b644b8b984..3fc22caaefb04 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -17,28 +17,26 @@ * under the License. */ +import { Breadcrumb as EuiBreadcrumb, IconType } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import React from 'react'; -import { BehaviorSubject, Observable, ReplaySubject, combineLatest, of, merge } from 'rxjs'; +import { BehaviorSubject, combineLatest, merge, Observable, of, ReplaySubject } from 'rxjs'; import { flatMap, map, takeUntil } from 'rxjs/operators'; import { parse } from 'url'; - -import { i18n } from '@kbn/i18n'; -import { IconType, Breadcrumb as EuiBreadcrumb } from '@elastic/eui'; - -import { InjectedMetadataStart } from '../injected_metadata'; -import { NotificationsStart } from '../notifications'; import { InternalApplicationStart } from '../application'; +import { DocLinksStart } from '../doc_links'; import { HttpStart } from '../http'; - +import { InjectedMetadataStart } from '../injected_metadata'; +import { NotificationsStart } from '../notifications'; +import { IUiSettingsClient } from '../ui_settings'; +import { KIBANA_ASK_ELASTIC_LINK } from './constants'; +import { ChromeDocTitle, DocTitleService } from './doc_title'; +import { ChromeNavControls, NavControlsService } from './nav_controls'; import { ChromeNavLinks, NavLinksService } from './nav_links'; import { ChromeRecentlyAccessed, RecentlyAccessedService } from './recently_accessed'; -import { NavControlsService, ChromeNavControls } from './nav_controls'; -import { DocTitleService, ChromeDocTitle } from './doc_title'; -import { LoadingIndicator, Header } from './ui'; -import { DocLinksStart } from '../doc_links'; +import { Header, LoadingIndicator } from './ui'; +import { NavType } from './ui/header'; import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu'; -import { KIBANA_ASK_ELASTIC_LINK } from './constants'; -import { IUiSettingsClient } from '../ui_settings'; export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle }; const IS_LOCKED_KEY = 'core.chrome.isLocked'; @@ -91,8 +89,7 @@ interface StartDeps { /** @internal */ export class ChromeService { private isVisible$!: Observable; - private appHidden$!: Observable; - private toggleHidden$!: BehaviorSubject; + private isForceHidden$!: BehaviorSubject; private readonly stop$ = new ReplaySubject(1); private readonly navControls = new NavControlsService(); private readonly navLinks = new NavLinksService(); @@ -111,13 +108,12 @@ export class ChromeService { private initVisibility(application: StartDeps['application']) { // Start off the chrome service hidden if "embed" is in the hash query string. const isEmbedded = 'embed' in parse(location.hash.slice(1), true).query; + this.isForceHidden$ = new BehaviorSubject(isEmbedded); - this.toggleHidden$ = new BehaviorSubject(isEmbedded); - this.appHidden$ = merge( - // Default the app being hidden to the same value initial value as the chrome visibility - // in case the application service has not emitted an app ID yet, since we want to trigger - // combineLatest below regardless of having an application value yet. - of(isEmbedded), + const appHidden$ = merge( + // For the isVisible$ logic, having no mounted app is equivalent to having a hidden app + // in the sense that the chrome UI should not be displayed until a non-chromeless app is mounting or mounted + of(true), application.currentAppId$.pipe( flatMap(appId => application.applications$.pipe( @@ -128,8 +124,8 @@ export class ChromeService { ) ) ); - this.isVisible$ = combineLatest([this.appHidden$, this.toggleHidden$]).pipe( - map(([appHidden, toggleHidden]) => !(appHidden || toggleHidden)), + this.isVisible$ = combineLatest([appHidden$, this.isForceHidden$]).pipe( + map(([appHidden, forceHidden]) => !appHidden && !forceHidden), takeUntil(this.stop$) ); } @@ -165,6 +161,10 @@ export class ChromeService { const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$)); + // TODO #64541 + // Can delete + const getNavType$ = uiSettings.get$('pageNavigation').pipe(takeUntil(this.stop$)); + if (!this.params.browserSupportsCsp && injectedMetadata.getCspConfig().warnLegacyBrowsers) { notifications.toasts.addWarning( i18n.translate('core.chrome.legacyBrowserWarning', { @@ -202,6 +202,7 @@ export class ChromeService { navControlsRight$={navControls.getRight$()} onIsLockedUpdate={setIsNavDrawerLocked} isLocked$={getIsNavDrawerLocked$} + navType$={getNavType$} /> ), @@ -221,7 +222,7 @@ export class ChromeService { getIsVisible$: () => this.isVisible$, - setIsVisible: (isVisible: boolean) => this.toggleHidden$.next(!isVisible), + setIsVisible: (isVisible: boolean) => this.isForceHidden$.next(!isVisible), getApplicationClasses$: () => applicationClasses$.pipe( @@ -262,6 +263,8 @@ export class ChromeService { setHelpSupportUrl: (url: string) => helpSupportUrl$.next(url), getIsNavDrawerLocked$: () => getIsNavDrawerLocked$, + + getNavType$: () => getNavType$, }; } @@ -408,6 +411,13 @@ export interface ChromeStart { * Get an observable of the current locked state of the nav drawer. */ getIsNavDrawerLocked$(): Observable; + + /** + * Get the navigation type + * TODO #64541 + * Can delete + */ + getNavType$(): Observable; } /** @internal */ diff --git a/src/core/public/chrome/index.ts b/src/core/public/chrome/index.ts index 4a500836990a7..cc1e0851f5944 100644 --- a/src/core/public/chrome/index.ts +++ b/src/core/public/chrome/index.ts @@ -33,6 +33,7 @@ export { ChromeHelpExtensionMenuDocumentationLink, ChromeHelpExtensionMenuGitHubLink, } from './ui/header/header_help_menu'; +export { NavType } from './ui'; export { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links'; export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem } from './recently_accessed'; export { ChromeNavControl, ChromeNavControls } from './nav_controls'; diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap new file mode 100644 index 0000000000000..14d5b2e8fdcbb --- /dev/null +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -0,0 +1,4506 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CollapsibleNav renders links grouped by category 1`] = ` + + + + + + } + /> + + + + +
+ +
+
+
+ + +
+ } + onActivation={[Function]} + onDeactivation={[Function]} + persistentFocus={false} + > + + +
+ } + onActivation={[Function]} + onDeactivation={[Function]} + persistentFocus={false} + /> + + +
+
+ +
+ + + + + +`; + +exports[`CollapsibleNav renders the default nav 1`] = ` + + + +`; + +exports[`CollapsibleNav renders the default nav 2`] = ` + + + + + + } + /> + + + + +
+ +
+
+
+ + + + +
+
+ +
+ + + + + +`; + +exports[`CollapsibleNav renders the default nav 3`] = ` + + + + + + +
+ +
+
+
+ + +
+ } + onActivation={[Function]} + onDeactivation={[Function]} + persistentFocus={false} + > + + +
+ } + onActivation={[Function]} + onDeactivation={[Function]} + persistentFocus={false} + /> + + +
+
+ +
+ + + + + +`; diff --git a/src/core/public/chrome/ui/header/_index.scss b/src/core/public/chrome/ui/header/_index.scss index f19728a52dd70..5c5e7f18b60a4 100644 --- a/src/core/public/chrome/ui/header/_index.scss +++ b/src/core/public/chrome/ui/header/_index.scss @@ -1,25 +1,12 @@ -@import '@elastic/eui/src/components/header/variables'; -@import '@elastic/eui/src/components/nav_drawer/variables'; - -.chrHeaderWrapper { +// TODO #64541 +// Delete this block +.chrHeaderWrapper:not(.headerWrapper) { width: 100%; position: fixed; top: 0; z-index: 10; } -.chrHeaderWrapper ~ .app-wrapper:not(.hidden-chrome) { - top: $euiHeaderChildSize; - left: $euiHeaderChildSize; - - // HOTFIX: Temporary fix for flyouts not inside portals - // SASSTODO: Find an actual solution - .euiFlyout { - top: $euiHeaderChildSize; - height: calc(100% - #{$euiHeaderChildSize}); - } -} - .chrHeaderHelpMenu__version { text-transform: none; } @@ -29,19 +16,8 @@ margin-right: $euiSize; } -// Mobile header is smaller -@include euiBreakpoint('xs', 's') { - .chrHeaderWrapper ~ .app-wrapper:not(.hidden-chrome) { - left: 0; - } -} - -@include euiBreakpoint('xl') { - .chrHeaderWrapper--navIsLocked { - ~ .app-wrapper:not(.hidden-chrome) { - // Shrink the content from the left so it's no longer overlapped by the nav drawer (ALWAYS) - left: $euiNavDrawerWidthExpanded !important; // sass-lint:disable-line no-important - transition: left $euiAnimSpeedFast $euiAnimSlightResistance; - } +.header__toggleNavButtonSection { + .euiBody--collapsibleNavIsDocked & { + display: none; } } diff --git a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx new file mode 100644 index 0000000000000..4a9d3071b93be --- /dev/null +++ b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx @@ -0,0 +1,136 @@ +/* + * 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 { mount, ReactWrapper } from 'enzyme'; +import React from 'react'; +import sinon from 'sinon'; +import { CollapsibleNav } from './collapsible_nav'; +import { AppCategory } from '../../../../types'; +import { DEFAULT_APP_CATEGORIES } from '../../..'; +import { StubBrowserStorage } from 'test_utils/stub_browser_storage'; + +jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ + htmlIdGenerator: () => () => 'mockId', +})); + +const { kibana, observability, security, management } = DEFAULT_APP_CATEGORIES; + +function mockLink(label: string, category?: AppCategory) { + return { + key: label, + label, + href: label, + isActive: true, + onClick: () => {}, + category, + 'data-test-subj': label, + }; +} + +function mockRecentNavLink(label: string) { + return { + href: label, + label, + title: label, + 'aria-label': label, + }; +} + +function mockProps() { + return { + id: 'collapsible-nav', + homeHref: '/', + isLocked: false, + isOpen: false, + navLinks: [], + recentNavLinks: [], + storage: new StubBrowserStorage(), + onIsOpenUpdate: () => {}, + onIsLockedUpdate: () => {}, + }; +} + +describe('CollapsibleNav', () => { + // this test is mostly an "EUI works as expected" sanity check + it('renders the default nav', () => { + const onLock = sinon.spy(); + const component = mount(); + expect(component).toMatchSnapshot(); + + component.setProps({ isOpen: true }); + expect(component).toMatchSnapshot(); + + component.setProps({ isLocked: true }); + expect(component).toMatchSnapshot(); + + // limit the find to buttons because jest also renders data-test-subj on a JSX wrapper element + component.find('button[data-test-subj="collapsible-nav-lock"]').simulate('click'); + expect(onLock.callCount).toEqual(1); + }); + + it('renders links grouped by category', () => { + // just a test of category functionality, categories are not accurate + const navLinks = [ + mockLink('discover', kibana), + mockLink('siem', security), + mockLink('metrics', observability), + mockLink('monitoring', management), + mockLink('visualize', kibana), + mockLink('dashboard', kibana), + mockLink('canvas'), // links should be able to be rendered top level as well + mockLink('logs', observability), + ]; + const recentNavLinks = [mockRecentNavLink('recent 1'), mockRecentNavLink('recent 2')]; + const component = mount( + + ); + expect(component).toMatchSnapshot(); + }); + + it('remembers collapsible section state', () => { + function expectNavLinksCount(component: ReactWrapper, count: number) { + expect( + component.find('.euiAccordion-isOpen a[data-test-subj="collapsibleNavAppLink"]').length + ).toEqual(count); + } + + const navLinks = [ + mockLink('discover', kibana), + mockLink('siem', security), + mockLink('metrics', observability), + mockLink('monitoring', management), + mockLink('visualize', kibana), + mockLink('dashboard', kibana), + mockLink('logs', observability), + ]; + const component = mount(); + expectNavLinksCount(component, 7); + component.find('[data-test-subj="collapsibleNavGroup-kibana"] button').simulate('click'); + expectNavLinksCount(component, 4); + component.setProps({ isOpen: false }); + expectNavLinksCount(component, 0); // double check the nav closed + component.setProps({ isOpen: true }); + expectNavLinksCount(component, 4); + }); +}); diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx new file mode 100644 index 0000000000000..274195f1917a5 --- /dev/null +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -0,0 +1,281 @@ +/* + * 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 { + EuiCollapsibleNav, + EuiCollapsibleNavGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiListGroup, + EuiListGroupItem, + EuiShowFor, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { groupBy, sortBy } from 'lodash'; +import React, { useRef } from 'react'; +import { AppCategory } from '../../../../types'; +import { OnIsLockedUpdate } from './'; +import { NavLink, RecentNavLink } from './nav_link'; + +function getAllCategories(allCategorizedLinks: Record) { + const allCategories = {} as Record; + + for (const [key, value] of Object.entries(allCategorizedLinks)) { + allCategories[key] = value[0].category; + } + + return allCategories; +} + +function getOrderedCategories( + mainCategories: Record, + categoryDictionary: ReturnType +) { + return sortBy( + Object.keys(mainCategories), + categoryName => categoryDictionary[categoryName]?.order + ); +} + +function getCategoryLocalStorageKey(id: string) { + return `core.navGroup.${id}`; +} + +function getIsCategoryOpen(id: string, storage: Storage) { + const value = storage.getItem(getCategoryLocalStorageKey(id)) ?? 'true'; + + return value === 'true'; +} + +function setIsCategoryOpen(id: string, isOpen: boolean, storage: Storage) { + storage.setItem(getCategoryLocalStorageKey(id), `${isOpen}`); +} + +interface Props { + isLocked: boolean; + isOpen: boolean; + navLinks: NavLink[]; + recentNavLinks: RecentNavLink[]; + homeHref: string; + id: string; + storage?: Storage; + onIsLockedUpdate: OnIsLockedUpdate; + onIsOpenUpdate: (isOpen?: boolean) => void; +} + +export function CollapsibleNav({ + isLocked, + isOpen, + navLinks, + recentNavLinks, + onIsLockedUpdate, + onIsOpenUpdate, + homeHref, + id, + storage = window.localStorage, +}: Props) { + const lockRef = useRef(null); + const groupedNavLinks = groupBy(navLinks, link => link?.category?.id); + const { undefined: unknowns = [], ...allCategorizedLinks } = groupedNavLinks; + const categoryDictionary = getAllCategories(allCategorizedLinks); + const orderedCategories = getOrderedCategories(allCategorizedLinks, categoryDictionary); + + return ( + + {/* Pinned items */} + + + onIsOpenUpdate(false), + }, + ]} + maxWidth="none" + color="text" + gutterSize="none" + size="s" + /> + + + + + + + {/* Recently viewed */} + setIsCategoryOpen('recentlyViewed', isCategoryOpen, storage)} + > + {recentNavLinks.length > 0 ? ( + { + // TODO #64541 + // Can remove icon from recent links completely + const { iconType, ...linkWithoutIcon } = link; + return linkWithoutIcon; + })} + maxWidth="none" + color="subdued" + gutterSize="none" + size="s" + /> + ) : ( + +

+ {i18n.translate('core.ui.EmptyRecentlyViewed', { + defaultMessage: 'No recently viewed items', + })} +

+
+ )} +
+ + {/* Kibana, Observability, Security, and Management sections */} + {orderedCategories.map((categoryName, i) => { + const category = categoryDictionary[categoryName]!; + const links = allCategorizedLinks[categoryName].map( + ({ label, href, isActive, isDisabled, onClick }: NavLink) => ({ + label, + href, + isActive, + isDisabled, + 'data-test-subj': 'collapsibleNavAppLink', + onClick: (e: React.MouseEvent) => { + onIsOpenUpdate(false); + onClick(e); + }, + }) + ); + + return ( + setIsCategoryOpen(category.id, isCategoryOpen, storage)} + data-test-subj={`collapsibleNavGroup-${category.id}`} + > + + + ); + })} + + {/* Things with no category (largely for custom plugins) */} + {unknowns.map(({ label, href, icon, isActive, isDisabled, onClick }, i) => ( + + + ) => { + onIsOpenUpdate(false); + onClick(e); + }} + /> + + + ))} + + {/* Docking button only for larger screens that can support it*/} + + + + { + onIsLockedUpdate(!isLocked); + if (lockRef.current) { + lockRef.current.focus(); + } + }} + iconType={isLocked ? 'lock' : 'lockOpen'} + /> + + + +
+
+ ); +} diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index 66b34c3db7bad..fb94ef46cdc2c 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -25,8 +25,8 @@ import { EuiIcon, // @ts-ignore EuiNavDrawer, - // @ts-ignore EuiShowFor, + htmlIdGenerator, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Component, createRef } from 'react'; @@ -43,13 +43,14 @@ import { InternalApplicationStart } from '../../../application/types'; import { HttpStart } from '../../../http'; import { ChromeHelpExtension } from '../../chrome_service'; import { HeaderBadge } from './header_badge'; -import { OnIsLockedUpdate } from './'; +import { NavType, OnIsLockedUpdate } from './'; import { HeaderBreadcrumbs } from './header_breadcrumbs'; import { HeaderHelpMenu } from './header_help_menu'; import { HeaderNavControls } from './header_nav_controls'; -import { euiNavLink } from './nav_link'; +import { createNavLink, createRecentNavLink } from './nav_link'; import { HeaderLogo } from './header_logo'; import { NavDrawer } from './nav_drawer'; +import { CollapsibleNav } from './collapsible_nav'; export interface HeaderProps { kibanaVersion: string; @@ -70,6 +71,7 @@ export interface HeaderProps { navControlsRight$: Rx.Observable; basePath: HttpStart['basePath']; isLocked$: Rx.Observable; + navType$: Rx.Observable; onIsLockedUpdate: OnIsLockedUpdate; } @@ -83,11 +85,14 @@ interface State { navControlsRight: readonly ChromeNavControl[]; currentAppId: string | undefined; isLocked: boolean; + navType: NavType; + isOpen: boolean; } export class Header extends Component { private subscription?: Rx.Subscription; private navDrawerRef = createRef(); + private toggleCollapsibleNavRef = createRef(); constructor(props: HeaderProps) { super(props); @@ -105,6 +110,8 @@ export class Header extends Component { navControlsRight: [], currentAppId: '', isLocked, + navType: 'modern', + isOpen: false, }; } @@ -120,7 +127,8 @@ export class Header extends Component { this.props.navControlsLeft$, this.props.navControlsRight$, this.props.application.currentAppId$, - this.props.isLocked$ + this.props.isLocked$, + this.props.navType$ ) ).subscribe({ next: ([ @@ -129,7 +137,7 @@ export class Header extends Component { forceNavigation, navLinks, recentlyAccessed, - [navControlsLeft, navControlsRight, currentAppId, isLocked], + [navControlsLeft, navControlsRight, currentAppId, isLocked, navType], ]) => { this.setState({ appTitle, @@ -141,6 +149,7 @@ export class Header extends Component { navControlsRight, currentAppId, isLocked, + navType, }); }, }); @@ -176,7 +185,7 @@ export class Header extends Component { kibanaVersion, } = this.props; const navLinks = this.state.navLinks.map(link => - euiNavLink( + createNavLink( link, this.props.legacyMode, this.state.currentAppId, @@ -184,26 +193,54 @@ export class Header extends Component { this.props.application.navigateToApp ) ); + const recentNavLinks = this.state.recentlyAccessed.map(link => + createRecentNavLink(link, this.state.navLinks, this.props.basePath) + ); if (!isVisible) { return null; } const className = classnames( - 'chrHeaderWrapper', + 'chrHeaderWrapper', // TODO #64541 - delete this + 'hide-for-sharing', { 'chrHeaderWrapper--navIsLocked': this.state.isLocked, - }, - 'hide-for-sharing' + headerWrapper: this.state.navType === 'modern', + } ); - + const navId = htmlIdGenerator()(); return (
- + - - {this.renderMenuTrigger()} - + {this.state.navType === 'modern' ? ( + + { + this.setState({ isOpen: !this.state.isOpen }); + }} + aria-expanded={this.state.isOpen} + aria-pressed={this.state.isOpen} + aria-controls={navId} + ref={this.toggleCollapsibleNavRef} + > + + + + ) : ( + // TODO #64541 + // Delete this block + + + {this.renderMenuTrigger()} + + + )} { - + {this.state.navType === 'modern' ? ( + { + this.setState({ isOpen }); + if (this.toggleCollapsibleNavRef.current) { + this.toggleCollapsibleNavRef.current.focus(); + } + }} + /> + ) : ( + // TODO #64541 + // Delete this block + + )}
); } diff --git a/src/core/public/chrome/ui/header/header_logo.tsx b/src/core/public/chrome/ui/header/header_logo.tsx index 793b8646dabf7..960ec637178e1 100644 --- a/src/core/public/chrome/ui/header/header_logo.tsx +++ b/src/core/public/chrome/ui/header/header_logo.tsx @@ -93,7 +93,7 @@ export function HeaderLogo({ href, forceNavigation, navLinks }: Props) { return ( onClick(e, forceNavigation, navLinks)} href={href} aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel', { diff --git a/src/core/public/chrome/ui/header/index.ts b/src/core/public/chrome/ui/header/index.ts index 49e002a66d939..a492273a65ba8 100644 --- a/src/core/public/chrome/ui/header/index.ts +++ b/src/core/public/chrome/ui/header/index.ts @@ -18,6 +18,7 @@ */ export { Header, HeaderProps } from './header'; +export { OnIsLockedUpdate, NavType } from './types'; export { ChromeHelpExtensionMenuLink, ChromeHelpExtensionMenuCustomLink, @@ -25,4 +26,3 @@ export { ChromeHelpExtensionMenuDocumentationLink, ChromeHelpExtensionMenuGitHubLink, } from './header_help_menu'; -export type OnIsLockedUpdate = (isLocked: boolean) => void; diff --git a/src/core/public/chrome/ui/header/nav_drawer.tsx b/src/core/public/chrome/ui/header/nav_drawer.tsx index c57faec1e428d..7faee8edea43b 100644 --- a/src/core/public/chrome/ui/header/nav_drawer.tsx +++ b/src/core/public/chrome/ui/header/nav_drawer.tsx @@ -22,22 +22,18 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore import { EuiNavDrawer, EuiHorizontalRule, EuiNavDrawerGroup } from '@elastic/eui'; import { OnIsLockedUpdate } from './'; -import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../../..'; -import { HttpStart } from '../../../http'; -import { NavLink } from './nav_link'; +import { NavLink, RecentNavLink } from './nav_link'; import { RecentLinks } from './recent_links'; export interface Props { isLocked?: boolean; onIsLockedUpdate?: OnIsLockedUpdate; navLinks: NavLink[]; - chromeNavLinks: ChromeNavLink[]; - recentlyAccessedItems: ChromeRecentlyAccessedHistoryItem[]; - basePath: HttpStart['basePath']; + recentNavLinks: RecentNavLink[]; } function navDrawerRenderer( - { isLocked, onIsLockedUpdate, navLinks, chromeNavLinks, recentlyAccessedItems, basePath }: Props, + { isLocked, onIsLockedUpdate, navLinks, recentNavLinks }: Props, ref: React.Ref ) { return ( @@ -50,11 +46,7 @@ function navDrawerRenderer( defaultMessage: 'Primary', })} > - {RecentLinks({ - recentlyAccessedItems, - navLinks: chromeNavLinks, - basePath, - })} + {RecentLinks({ recentNavLinks })} ) { return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); } @@ -30,15 +32,36 @@ function LinkIcon({ url }: { url: string }) { return ; } -export type NavLink = ReturnType; +export interface NavLink { + key: string; + label: string; + href: string; + isActive: boolean; + onClick(event: React.MouseEvent): void; + category?: AppCategory; + isDisabled?: boolean; + iconType?: string; + icon?: JSX.Element; + order?: number; + 'data-test-subj': string; +} -export function euiNavLink( +/** + * Create a link that's actually ready to be passed into EUI + * + * @param navLink + * @param legacyMode + * @param currentAppId + * @param basePath + * @param navigateToApp + */ +export function createNavLink( navLink: ChromeNavLink, legacyMode: boolean, currentAppId: string | undefined, basePath: HttpStart['basePath'], navigateToApp: CoreStart['application']['navigateToApp'] -) { +): NavLink { const { legacy, url, @@ -64,7 +87,7 @@ export function euiNavLink( key: id, label: tooltip ?? title, href, // Use href and onClick to support "open in new tab" and SPA navigation in the same link - onClick(event: MouseEvent) { + onClick(event) { if ( !legacyMode && // ignore when in legacy mode !legacy && // ignore links to legacy apps @@ -85,3 +108,76 @@ export function euiNavLink( 'data-test-subj': 'navDrawerAppsMenuLink', }; } + +// Providing a buffer between the limit and the cut off index +// protects from truncating just the last couple (6) characters +const TRUNCATE_LIMIT: number = 64; +const TRUNCATE_AT: number = 58; + +function truncateRecentItemLabel(label: string): string { + if (label.length > TRUNCATE_LIMIT) { + label = `${label.substring(0, TRUNCATE_AT)}…`; + } + + return label; +} + +/** + * @param {string} url - a relative or root relative url. If a relative path is given then the + * absolute url returned will depend on the current page where this function is called from. For example + * if you are on page "http://www.mysite.com/shopping/kids" and you pass this function "adults", you would get + * back "http://www.mysite.com/shopping/adults". If you passed this function a root relative path, or one that + * starts with a "/", for example "/account/cart", you would get back "http://www.mysite.com/account/cart". + * @return {string} the relative url transformed into an absolute url + */ +function relativeToAbsolute(url: string) { + const a = document.createElement('a'); + a.setAttribute('href', url); + return a.href; +} + +export interface RecentNavLink { + href: string; + label: string; + title: string; + 'aria-label': string; + iconType?: string; +} + +/** + * Add saved object type info to recently links + * + * Recent nav links are similar to normal nav links but are missing some Kibana Platform magic and + * because of legacy reasons have slightly different properties. + * @param recentLink + * @param navLinks + * @param basePath + */ +export function createRecentNavLink( + recentLink: ChromeRecentlyAccessedHistoryItem, + navLinks: ChromeNavLink[], + basePath: HttpStart['basePath'] +) { + const { link, label } = recentLink; + const href = relativeToAbsolute(basePath.prepend(link)); + const navLink = navLinks.find(nl => href.startsWith(nl.baseUrl ?? nl.subUrlBase)); + let titleAndAriaLabel = label; + + if (navLink) { + titleAndAriaLabel = i18n.translate('core.ui.recentLinks.linkItem.screenReaderLabel', { + defaultMessage: '{recentlyAccessedItemLinklabel}, type: {pageType}', + values: { + recentlyAccessedItemLinklabel: label, + pageType: navLink.title, + }, + }); + } + + return { + href, + label: truncateRecentItemLabel(label), + title: titleAndAriaLabel, + 'aria-label': titleAndAriaLabel, + iconType: navLink?.euiIconType, + }; +} diff --git a/src/core/public/chrome/ui/header/recent_links.tsx b/src/core/public/chrome/ui/header/recent_links.tsx index 57cb1d9541bcd..019cdce0b43c6 100644 --- a/src/core/public/chrome/ui/header/recent_links.tsx +++ b/src/core/public/chrome/ui/header/recent_links.tsx @@ -21,73 +21,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // @ts-ignore import { EuiNavDrawerGroup } from '@elastic/eui'; -import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../../..'; -import { HttpStart } from '../../../http'; - -// Providing a buffer between the limit and the cut off index -// protects from truncating just the last couple (6) characters -const TRUNCATE_LIMIT: number = 64; -const TRUNCATE_AT: number = 58; - -export function truncateRecentItemLabel(label: string): string { - if (label.length > TRUNCATE_LIMIT) { - label = `${label.substring(0, TRUNCATE_AT)}…`; - } - - return label; -} - -/** - * @param {string} url - a relative or root relative url. If a relative path is given then the - * absolute url returned will depend on the current page where this function is called from. For example - * if you are on page "http://www.mysite.com/shopping/kids" and you pass this function "adults", you would get - * back "http://www.mysite.com/shopping/adults". If you passed this function a root relative path, or one that - * starts with a "/", for example "/account/cart", you would get back "http://www.mysite.com/account/cart". - * @return {string} the relative url transformed into an absolute url - */ -function relativeToAbsolute(url: string) { - const a = document.createElement('a'); - a.setAttribute('href', url); - return a.href; -} - -function prepareForEUI( - recentlyAccessed: ChromeRecentlyAccessedHistoryItem[], - navLinks: ChromeNavLink[], - basePath: HttpStart['basePath'] -) { - return recentlyAccessed.map(({ link, label }) => { - const href = relativeToAbsolute(basePath.prepend(link)); - const navLink = navLinks.find(nl => href.startsWith(nl.baseUrl ?? nl.subUrlBase)); - let titleAndAriaLabel = label; - - if (navLink) { - titleAndAriaLabel = i18n.translate('core.ui.recentLinks.linkItem.screenReaderLabel', { - defaultMessage: '{recentlyAccessedItemLinklabel}, type: {pageType}', - values: { - recentlyAccessedItemLinklabel: label, - pageType: navLink.title, - }, - }); - } - - return { - href, - label: truncateRecentItemLabel(label), - title: titleAndAriaLabel, - 'aria-label': titleAndAriaLabel, - iconType: navLink?.euiIconType, - }; - }); -} +import { RecentNavLink } from './nav_link'; interface Props { - recentlyAccessedItems: ChromeRecentlyAccessedHistoryItem[]; - navLinks: ChromeNavLink[]; - basePath: HttpStart['basePath']; + recentNavLinks: RecentNavLink[]; } -export function RecentLinks({ recentlyAccessedItems, navLinks, basePath }: Props) { +export function RecentLinks({ recentNavLinks }: Props) { return ( input.replace(DOT_PREFIX_RE, '$1.'); +export type OnIsLockedUpdate = (isLocked: boolean) => void; +export type NavType = 'modern' | 'legacy'; diff --git a/src/core/public/chrome/ui/index.ts b/src/core/public/chrome/ui/index.ts index 460e19b7d9780..4f6ad90cb96a3 100644 --- a/src/core/public/chrome/ui/index.ts +++ b/src/core/public/chrome/ui/index.ts @@ -25,4 +25,5 @@ export { ChromeHelpExtensionMenuDiscussLink, ChromeHelpExtensionMenuDocumentationLink, ChromeHelpExtensionMenuGitHubLink, + NavType, } from './header'; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index b4f64125a03ef..3b2d9ed3c0b02 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -54,6 +54,7 @@ import { ChromeStart, ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, + NavType, } from './chrome'; import { FatalErrorsSetup, FatalErrorsStart, FatalErrorInfo } from './fatal_errors'; import { HttpSetup, HttpStart } from './http'; @@ -77,7 +78,17 @@ import { } from './context'; export { CoreContext, CoreSystem } from './core_system'; -export { RecursiveReadonly, DEFAULT_APP_CATEGORIES } from '../utils'; +export { + RecursiveReadonly, + DEFAULT_APP_CATEGORIES, + getFlattenedObject, + URLMeaningfulParts, + modifyUrl, + isRelativeUrl, + Freezable, + deepFreeze, + assertNever, +} from '../utils'; export { AppCategory, UiSettingsParams, @@ -344,4 +355,5 @@ export { PluginOpaqueId, IUiSettingsClient, UiSettingsState, + NavType, }; diff --git a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap index 6b4b22b8541bc..fa83b34e06b81 100644 --- a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap +++ b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap @@ -13,12 +13,7 @@ Array [ Array [
Flyout content
"`; +exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"
Flyout content
"`; exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 1`] = ` Array [ Array [
Flyout content 2
"`; +exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `"
Flyout content 2
"`; diff --git a/src/core/public/overlays/flyout/flyout_service.tsx b/src/core/public/overlays/flyout/flyout_service.tsx index b609b2ce1d741..444430175d4f2 100644 --- a/src/core/public/overlays/flyout/flyout_service.tsx +++ b/src/core/public/overlays/flyout/flyout_service.tsx @@ -91,6 +91,7 @@ export interface OverlayFlyoutStart { export interface OverlayFlyoutOpenOptions { className?: string; closeButtonAriaLabel?: string; + ownFocus?: boolean; 'data-test-subj'?: string; } diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index af06b207889c2..225ef611c0298 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -16,6 +16,7 @@ import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; import { MaybePromise } from '@kbn/utility-types'; import { Observable } from 'rxjs'; +import { ParsedQuery } from 'query-string'; import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types'; import React from 'react'; import * as Rx from 'rxjs'; @@ -54,6 +55,7 @@ export interface AppBase { export interface AppCategory { ariaLabel?: string; euiIconType?: string; + id: string; label: string; order?: number; } @@ -174,6 +176,9 @@ export type AppUpdatableFields = Pick Partial | undefined; +// @public +export function assertNever(x: never): never; + // @public export interface Capabilities { [key: string]: Record>; @@ -339,6 +344,7 @@ export interface ChromeStart { getHelpExtension$(): Observable; getIsNavDrawerLocked$(): Observable; getIsVisible$(): Observable; + getNavType$(): Observable; navControls: ChromeNavControls; navLinks: ChromeNavLinks; recentlyAccessed: ChromeRecentlyAccessed; @@ -434,25 +440,33 @@ export class CoreSystem { stop(): void; } +// @public +export function deepFreeze(object: T): RecursiveReadonly; + // @internal (undocumented) export const DEFAULT_APP_CATEGORIES: Readonly<{ - analyze: { + kibana: { + id: string; label: string; + euiIconType: string; order: number; }; observability: { + id: string; label: string; euiIconType: string; order: number; }; security: { + id: string; label: string; order: number; euiIconType: string; }; management: { + id: string; label: string; - euiIconType: string; + order: number; }; }>; @@ -584,6 +598,16 @@ export interface FatalErrorsSetup { // @public export type FatalErrorsStart = FatalErrorsSetup; +// @public (undocumented) +export type Freezable = { + [k: string]: any; +} | any[]; + +// @public +export function getFlattenedObject(rootValue: Record): { + [key: string]: any; +}; + // @public export type HandlerContextType> = T extends HandlerFunction ? U : never; @@ -795,6 +819,9 @@ export interface ImageValidation { }; } +// @public +export function isRelativeUrl(candidatePath: string): boolean; + // @public export type IToasts = Pick; @@ -857,9 +884,17 @@ export interface LegacyNavLink { url: string; } +// @public +export function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; + // @public export type MountPoint = (element: T) => UnmountCallback; +// Warning: (ae-missing-release-tag) "NavType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type NavType = 'modern' | 'legacy'; + // @public (undocumented) export interface NotificationsSetup { // (undocumented) @@ -1356,6 +1391,26 @@ export type UiSettingsType = 'undefined' | 'json' | 'markdown' | 'number' | 'sel // @public export type UnmountCallback = () => void; +// @public +export interface URLMeaningfulParts { + // (undocumented) + auth?: string | null; + // (undocumented) + hash?: string | null; + // (undocumented) + hostname?: string | null; + // (undocumented) + pathname?: string | null; + // (undocumented) + port?: string | null; + // (undocumented) + protocol?: string | null; + // (undocumented) + query: ParsedQuery; + // (undocumented) + slashes?: boolean | null; +} + // @public export interface UserProvidedValues { // (undocumented) diff --git a/src/core/public/rendering/_base.scss b/src/core/public/rendering/_base.scss index ff28fc75e367d..8032bc458822f 100644 --- a/src/core/public/rendering/_base.scss +++ b/src/core/public/rendering/_base.scss @@ -1,3 +1,6 @@ +@import '@elastic/eui/src/components/header/variables'; +@import '@elastic/eui/src/components/nav_drawer/variables'; + /** * stretch the root element of the Kibana application to set the base-size that * flexed children should keep. Only works when paired with root styles applied @@ -9,7 +12,9 @@ min-height: 100%; } -.app-wrapper { +// TODO #64541 +// Delete this block +.chrHeaderWrapper:not(.headerWrapper) ~ .app-wrapper { display: flex; flex-flow: column nowrap; position: absolute; @@ -20,6 +25,22 @@ z-index: 5; margin: 0 auto; + &:not(.hidden-chrome) { + top: $euiHeaderChildSize; + left: $euiHeaderChildSize; + + // HOTFIX: Temporary fix for flyouts not inside portals + // SASSTODO: Find an actual solution + .euiFlyout { + top: $euiHeaderChildSize; + height: calc(100% - #{$euiHeaderChildSize}); + } + + @include euiBreakpoint('xs', 's') { + left: 0; + } + } + /** * 1. Dirty, but we need to override the .kbnGlobalNav-isOpen state * when we're looking at the log-in screen. @@ -33,6 +54,32 @@ } } +// TODO #64541 +// Delete this block +@include euiBreakpoint('xl') { + .chrHeaderWrapper--navIsLocked:not(.headerWrapper) { + ~ .app-wrapper:not(.hidden-chrome) { + // Shrink the content from the left so it's no longer overlapped by the nav drawer (ALWAYS) + left: $euiNavDrawerWidthExpanded !important; // sass-lint:disable-line no-important + transition: left $euiAnimSpeedFast $euiAnimSlightResistance; + } + } +} + +// TODO #64541 +// Remove .headerWrapper and header conditionals +.headerWrapper ~ .app-wrapper, +:not(header) ~ .app-wrapper { + display: flex; + flex-flow: column nowrap; + margin: 0 auto; + min-height: calc(100vh - #{$euiHeaderHeightCompensation}); + + &.hidden-chrome { + min-height: 100vh; + } +} + .app-wrapper-panel { display: flex; flex-grow: 1; diff --git a/src/core/server/core_app/assets/favicons/android-chrome-192x192.png b/src/core/server/core_app/assets/favicons/android-chrome-192x192.png index 54b274dbc8eb1..18a86e5b95c46 100644 Binary files a/src/core/server/core_app/assets/favicons/android-chrome-192x192.png and b/src/core/server/core_app/assets/favicons/android-chrome-192x192.png differ diff --git a/src/core/server/core_app/assets/favicons/android-chrome-256x256.png b/src/core/server/core_app/assets/favicons/android-chrome-256x256.png index 4fb79e35a8fbd..8238d772ce40b 100644 Binary files a/src/core/server/core_app/assets/favicons/android-chrome-256x256.png and b/src/core/server/core_app/assets/favicons/android-chrome-256x256.png differ diff --git a/src/core/server/core_app/assets/favicons/android-chrome-512x512.png b/src/core/server/core_app/assets/favicons/android-chrome-512x512.png deleted file mode 100644 index 5095b839b77a2..0000000000000 Binary files a/src/core/server/core_app/assets/favicons/android-chrome-512x512.png and /dev/null differ diff --git a/src/core/server/core_app/assets/favicons/apple-touch-icon.png b/src/core/server/core_app/assets/favicons/apple-touch-icon.png index 11a714394b172..1ffeb0852a170 100644 Binary files a/src/core/server/core_app/assets/favicons/apple-touch-icon.png and b/src/core/server/core_app/assets/favicons/apple-touch-icon.png differ diff --git a/src/core/server/core_app/assets/favicons/favicon-16x16.png b/src/core/server/core_app/assets/favicons/favicon-16x16.png index 1ff8f0caa2dfc..631f5b7c7d74b 100644 Binary files a/src/core/server/core_app/assets/favicons/favicon-16x16.png and b/src/core/server/core_app/assets/favicons/favicon-16x16.png differ diff --git a/src/core/server/core_app/assets/favicons/favicon-32x32.png b/src/core/server/core_app/assets/favicons/favicon-32x32.png index 709b651e15eba..bf94dfa995f37 100644 Binary files a/src/core/server/core_app/assets/favicons/favicon-32x32.png and b/src/core/server/core_app/assets/favicons/favicon-32x32.png differ diff --git a/src/core/server/core_app/assets/favicons/favicon.ico b/src/core/server/core_app/assets/favicons/favicon.ico index 9d0ed69fb63e4..db30798a6cf32 100644 Binary files a/src/core/server/core_app/assets/favicons/favicon.ico and b/src/core/server/core_app/assets/favicons/favicon.ico differ diff --git a/src/core/server/core_app/assets/favicons/manifest.json b/src/core/server/core_app/assets/favicons/manifest.json index 17b3c4b2d9e52..de65106f489b7 100644 --- a/src/core/server/core_app/assets/favicons/manifest.json +++ b/src/core/server/core_app/assets/favicons/manifest.json @@ -1,5 +1,6 @@ { "name": "", + "short_name": "", "icons": [ { "src": "/android-chrome-192x192.png", diff --git a/src/core/server/core_app/assets/favicons/mstile-144x144.png b/src/core/server/core_app/assets/favicons/mstile-144x144.png deleted file mode 100644 index be839dad41365..0000000000000 Binary files a/src/core/server/core_app/assets/favicons/mstile-144x144.png and /dev/null differ diff --git a/src/core/server/core_app/assets/favicons/mstile-150x150.png b/src/core/server/core_app/assets/favicons/mstile-150x150.png index 0a2078511231b..82769c1ef242b 100644 Binary files a/src/core/server/core_app/assets/favicons/mstile-150x150.png and b/src/core/server/core_app/assets/favicons/mstile-150x150.png differ diff --git a/src/core/server/core_app/assets/favicons/mstile-310x150.png b/src/core/server/core_app/assets/favicons/mstile-310x150.png deleted file mode 100644 index 8c4d4ec7af840..0000000000000 Binary files a/src/core/server/core_app/assets/favicons/mstile-310x150.png and /dev/null differ diff --git a/src/core/server/core_app/assets/favicons/mstile-310x310.png b/src/core/server/core_app/assets/favicons/mstile-310x310.png deleted file mode 100644 index 82701d9bb35da..0000000000000 Binary files a/src/core/server/core_app/assets/favicons/mstile-310x310.png and /dev/null differ diff --git a/src/core/server/core_app/assets/favicons/mstile-70x70.png b/src/core/server/core_app/assets/favicons/mstile-70x70.png deleted file mode 100644 index 794a22ab1ee6f..0000000000000 Binary files a/src/core/server/core_app/assets/favicons/mstile-70x70.png and /dev/null differ diff --git a/src/core/server/core_app/assets/favicons/safari-pinned-tab.svg b/src/core/server/core_app/assets/favicons/safari-pinned-tab.svg index 839ee14d59444..38a64142be0b7 100644 --- a/src/core/server/core_app/assets/favicons/safari-pinned-tab.svg +++ b/src/core/server/core_app/assets/favicons/safari-pinned-tab.svg @@ -2,34 +2,33 @@ Created by potrace 1.11, written by Peter Selinger 2001-2013 - - - + + + + + + diff --git a/src/core/server/elasticsearch/__snapshots__/elasticsearch_config.test.ts.snap b/src/core/server/elasticsearch/__snapshots__/elasticsearch_config.test.ts.snap index e81336c8863f5..75627f311d9a5 100644 --- a/src/core/server/elasticsearch/__snapshots__/elasticsearch_config.test.ts.snap +++ b/src/core/server/elasticsearch/__snapshots__/elasticsearch_config.test.ts.snap @@ -1,3 +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."`; +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_system\\" user instead."`; diff --git a/src/core/server/elasticsearch/elasticsearch_config.test.ts b/src/core/server/elasticsearch/elasticsearch_config.test.ts index de3f57298f461..cb4501a51e849 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.test.ts @@ -315,12 +315,21 @@ describe('deprecations', () => { const { messages } = applyElasticsearchDeprecations({ username: 'elastic' }); expect(messages).toMatchInlineSnapshot(` Array [ - "Setting [${CONFIG_PATH}.username] to \\"elastic\\" is deprecated. You should use the \\"kibana\\" user instead.", + "Setting [${CONFIG_PATH}.username] to \\"elastic\\" is deprecated. You should use the \\"kibana_system\\" user instead.", ] `); }); - it('does not log a warning if elasticsearch.username is set to something besides "elastic"', () => { + it('logs a warning if elasticsearch.username is set to "kibana"', () => { + const { messages } = applyElasticsearchDeprecations({ username: 'kibana' }); + expect(messages).toMatchInlineSnapshot(` + Array [ + "Setting [${CONFIG_PATH}.username] to \\"kibana\\" is deprecated. You should use the \\"kibana_system\\" user instead.", + ] + `); + }); + + it('does not log a warning if elasticsearch.username is set to something besides "elastic" or "kibana"', () => { const { messages } = applyElasticsearchDeprecations({ username: 'otheruser' }); expect(messages).toHaveLength(0); }); diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts index d3012e361b3ed..c87c94bcd0b6a 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.ts @@ -55,7 +55,7 @@ export const configSchema = schema.object({ 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.' + 'privilege-related issues. You should use the "kibana_system" user instead.' ); } }, @@ -131,7 +131,11 @@ const deprecations: ConfigDeprecationProvider = () => [ } if (es.username === 'elastic') { log( - `Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana" user instead.` + `Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana_system" user instead.` + ); + } else if (es.username === 'kibana') { + log( + `Setting [${fromPath}.username] to "kibana" is deprecated. You should use the "kibana_system" user instead.` ); } if (es.ssl?.key !== undefined && es.ssl?.certificate === undefined) { diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index da8846f6dddbb..a7d78b56ff3fd 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -18,6 +18,7 @@ */ import { BehaviorSubject } from 'rxjs'; +import { Client } from 'elasticsearch'; import { IClusterClient, ICustomClusterClient } from './cluster_client'; import { IScopedClusterClient } from './scoped_cluster_client'; import { ElasticsearchConfig } from './elasticsearch_config'; @@ -130,6 +131,55 @@ const createMock = () => { return mocked; }; +const createElasticsearchClientMock = () => { + const mocked: jest.Mocked = { + cat: {} as any, + cluster: {} as any, + indices: {} as any, + ingest: {} as any, + nodes: {} as any, + snapshot: {} as any, + tasks: {} as any, + bulk: jest.fn(), + clearScroll: jest.fn(), + count: jest.fn(), + create: jest.fn(), + delete: jest.fn(), + deleteByQuery: jest.fn(), + deleteScript: jest.fn(), + deleteTemplate: jest.fn(), + exists: jest.fn(), + explain: jest.fn(), + fieldStats: jest.fn(), + get: jest.fn(), + getScript: jest.fn(), + getSource: jest.fn(), + getTemplate: jest.fn(), + index: jest.fn(), + info: jest.fn(), + mget: jest.fn(), + msearch: jest.fn(), + msearchTemplate: jest.fn(), + mtermvectors: jest.fn(), + ping: jest.fn(), + putScript: jest.fn(), + putTemplate: jest.fn(), + reindex: jest.fn(), + reindexRethrottle: jest.fn(), + renderSearchTemplate: jest.fn(), + scroll: jest.fn(), + search: jest.fn(), + searchShards: jest.fn(), + searchTemplate: jest.fn(), + suggest: jest.fn(), + termvectors: jest.fn(), + update: jest.fn(), + updateByQuery: jest.fn(), + close: jest.fn(), + }; + return mocked; +}; + export const elasticsearchServiceMock = { create: createMock, createInternalSetup: createInternalSetupContractMock, @@ -138,4 +188,5 @@ export const elasticsearchServiceMock = { createClusterClient: createClusterClientMock, createCustomClusterClient: createCustomClusterClientMock, createScopedClusterClient: createScopedClusterClientMock, + createElasticsearchClient: createElasticsearchClientMock, }; diff --git a/src/core/server/http/integration_tests/core_service.test.mocks.ts b/src/core/server/http/integration_tests/core_service.test.mocks.ts index 6fa3357168027..b3adda8dd22d1 100644 --- a/src/core/server/http/integration_tests/core_service.test.mocks.ts +++ b/src/core/server/http/integration_tests/core_service.test.mocks.ts @@ -24,3 +24,14 @@ jest.doMock('../../elasticsearch/scoped_cluster_client', () => ({ return elasticsearchServiceMock.createScopedClusterClient(); }), })); + +jest.doMock('elasticsearch', () => { + const realES = jest.requireActual('elasticsearch'); + return { + ...realES, + // eslint-disable-next-line object-shorthand + Client: function() { + return elasticsearchServiceMock.createElasticsearchClient(); + }, + }; +}); diff --git a/src/core/server/http/integration_tests/core_services.test.ts b/src/core/server/http/integration_tests/core_services.test.ts index 7b1630a7de0be..c7925f5b6d821 100644 --- a/src/core/server/http/integration_tests/core_services.test.ts +++ b/src/core/server/http/integration_tests/core_services.test.ts @@ -43,7 +43,7 @@ describe('http service', () => { describe('auth', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot({ plugins: { initialize: false } }); }, 30000); afterEach(async () => { @@ -192,7 +192,7 @@ describe('http service', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot({ plugins: { initialize: false } }); }, 30000); afterEach(async () => { @@ -326,7 +326,7 @@ describe('http service', () => { describe('#basePath()', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot({ plugins: { initialize: false } }); }, 30000); afterEach(async () => await root.shutdown()); @@ -355,7 +355,7 @@ describe('http service', () => { describe('elasticsearch', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot({ plugins: { initialize: false } }); }, 30000); afterEach(async () => { diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 86192245bd2d1..cf999875b18f8 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -288,7 +288,17 @@ export { MetricsServiceSetup, } from './metrics'; -export { RecursiveReadonly } from '../utils'; +export { + RecursiveReadonly, + DEFAULT_APP_CATEGORIES, + getFlattenedObject, + URLMeaningfulParts, + modifyUrl, + isRelativeUrl, + Freezable, + deepFreeze, + assertNever, +} from '../utils'; export { SavedObject, diff --git a/src/core/server/legacy/plugins/get_nav_links.test.ts b/src/core/server/legacy/plugins/get_nav_links.test.ts index 44d080ec37a25..5e84f27acabd5 100644 --- a/src/core/server/legacy/plugins/get_nav_links.test.ts +++ b/src/core/server/legacy/plugins/get_nav_links.test.ts @@ -133,6 +133,7 @@ describe('getNavLinks', () => { id: 'app-a', title: 'AppA', category: { + id: 'foo', label: 'My Category', }, order: 42, @@ -151,6 +152,7 @@ describe('getNavLinks', () => { id: 'app-a', title: 'AppA', category: { + id: 'foo', label: 'My Category', }, order: 42, @@ -211,6 +213,7 @@ describe('getNavLinks', () => { id: 'link-a', title: 'AppA', category: { + id: 'foo', label: 'My Second Cat', }, order: 72, @@ -232,6 +235,7 @@ describe('getNavLinks', () => { id: 'link-a', title: 'AppA', category: { + id: 'foo', label: 'My Second Cat', }, order: 72, diff --git a/src/core/server/rendering/views/template.tsx b/src/core/server/rendering/views/template.tsx index 73e119a5a97e7..76af229ac02ba 100644 --- a/src/core/server/rendering/views/template.tsx +++ b/src/core/server/rendering/views/template.tsx @@ -74,7 +74,7 @@ export const Template: FunctionComponent = ({ - Elastic Kibana + Elastic {/* Favicons (generated from http://realfavicongenerator.net/) */} = ({ className="kbnWelcomeText" data-error-message={i18n('core.ui.welcomeErrorMessage', { defaultMessage: - 'Elastic Kibana did not load properly. Check the server output for more information.', + 'Elastic did not load properly. Check the server output for more information.', })} > - {i18n('core.ui.welcomeMessage', { defaultMessage: 'Loading Elastic Kibana' })} + {i18n('core.ui.welcomeMessage', { defaultMessage: 'Loading Elastic' })}
@@ -146,7 +146,7 @@ export const Template: FunctionComponent = ({
{i18n('core.ui.legacyBrowserMessage', { defaultMessage: - 'This Kibana installation has strict security requirements enabled that your current browser does not meet.', + 'This Elastic installation has strict security requirements enabled that your current browser does not meet.', })}
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index e8b77a8570291..62d11ee7cf9a7 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -103,6 +103,7 @@ import { NodesInfoParams } from 'elasticsearch'; import { NodesStatsParams } from 'elasticsearch'; import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; +import { ParsedQuery } from 'query-string'; import { PeerCertificate } from 'tls'; import { PingParams } from 'elasticsearch'; import { PutScriptParams } from 'elasticsearch'; @@ -388,6 +389,9 @@ export interface APICaller { (endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; } +// @public +export function assertNever(x: never): never; + // @public (undocumented) export interface AssistanceAPIResponse { // (undocumented) @@ -691,6 +695,36 @@ export interface CustomHttpResponseOptions(object: T): RecursiveReadonly; + +// @internal (undocumented) +export const DEFAULT_APP_CATEGORIES: Readonly<{ + kibana: { + id: string; + label: string; + euiIconType: string; + order: number; + }; + observability: { + id: string; + label: string; + euiIconType: string; + order: number; + }; + security: { + id: string; + label: string; + order: number; + euiIconType: string; + }; + management: { + id: string; + label: string; + order: number; + }; +}>; + // @public (undocumented) export interface DeprecationAPIClientParams extends GenericParams { // (undocumented) @@ -838,6 +872,11 @@ export interface FakeRequest { headers: Headers; } +// @public (undocumented) +export type Freezable = { + [k: string]: any; +} | any[]; + // @public export type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; @@ -847,6 +886,11 @@ export type GetAuthState = (request: KibanaRequest | LegacyRequest) state: T; }; +// @public +export function getFlattenedObject(rootValue: Record): { + [key: string]: any; +}; + // @public export type HandlerContextType> = T extends HandlerFunction ? U : never; @@ -1034,6 +1078,9 @@ export type ISavedObjectTypeRegistry = Omit; +// @public +export function isRelativeUrl(candidatePath: string): boolean; + // @public export interface IUiSettingsClient { get: (key: string) => Promise; @@ -1289,6 +1336,9 @@ export type MIGRATION_ASSISTANCE_INDEX_ACTION = 'upgrade' | 'reindex'; // @public (undocumented) export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critical'; +// @public +export function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; + // @public export type MutatingOperationRefreshSetting = boolean | 'wait_for'; @@ -2447,6 +2497,26 @@ export interface UiSettingsServiceStart { // @public export type UiSettingsType = 'undefined' | 'json' | 'markdown' | 'number' | 'select' | 'boolean' | 'string' | 'array' | 'image'; +// @public +export interface URLMeaningfulParts { + // (undocumented) + auth?: string | null; + // (undocumented) + hash?: string | null; + // (undocumented) + hostname?: string | null; + // (undocumented) + pathname?: string | null; + // (undocumented) + port?: string | null; + // (undocumented) + protocol?: string | null; + // (undocumented) + query: ParsedQuery; + // (undocumented) + slashes?: boolean | null; +} + // @public export interface UserProvidedValues { // (undocumented) diff --git a/src/core/types/app_category.ts b/src/core/types/app_category.ts index 83a3693f009b6..8b39889b43a82 100644 --- a/src/core/types/app_category.ts +++ b/src/core/types/app_category.ts @@ -24,6 +24,11 @@ * @public */ export interface AppCategory { + /** + * Unique identifier for the categories + */ + id: string; + /** * Label used for cateogry name. * Also used as aria-label if one isn't set. diff --git a/src/core/utils/assert_never.ts b/src/core/utils/assert_never.ts index 8e47f07a02a87..c713b373493c5 100644 --- a/src/core/utils/assert_never.ts +++ b/src/core/utils/assert_never.ts @@ -17,8 +17,12 @@ * under the License. */ -// Can be used in switch statements to ensure we perform exhaustive checks, see -// https://www.typescriptlang.org/docs/handbook/advanced-types.html#exhaustiveness-checking +/** + * Can be used in switch statements to ensure we perform exhaustive checks, see + * https://www.typescriptlang.org/docs/handbook/advanced-types.html#exhaustiveness-checking + * + * @public + */ export function assertNever(x: never): never { throw new Error(`Unexpected object: ${x}`); } diff --git a/src/core/utils/deep_freeze.ts b/src/core/utils/deep_freeze.ts index 8c3f8f2258b61..b0f283c60d0fc 100644 --- a/src/core/utils/deep_freeze.ts +++ b/src/core/utils/deep_freeze.ts @@ -17,8 +17,6 @@ * under the License. */ -type Freezable = { [k: string]: any } | any[]; - // if we define this inside RecursiveReadonly TypeScript complains // eslint-disable-next-line @typescript-eslint/no-empty-interface interface RecursiveReadonlyArray extends Array> {} @@ -32,6 +30,15 @@ export type RecursiveReadonly = T extends (...args: any[]) => any ? Readonly<{ [K in keyof T]: RecursiveReadonly }> : T; +/** @public */ +export type Freezable = { [k: string]: any } | any[]; + +/** + * Apply Object.freeze to a value recursively and convert the return type to + * Readonly variant recursively + * + * @public + */ export function deepFreeze(object: T) { // for any properties that reference an object, makes sure that object is // recursively frozen as well diff --git a/src/core/utils/default_app_categories.ts b/src/core/utils/default_app_categories.ts index 2285bd6afd365..5708bcfeac31a 100644 --- a/src/core/utils/default_app_categories.ts +++ b/src/core/utils/default_app_categories.ts @@ -21,13 +21,16 @@ import { i18n } from '@kbn/i18n'; /** @internal */ export const DEFAULT_APP_CATEGORIES = Object.freeze({ - analyze: { - label: i18n.translate('core.ui.analyzeNavList.label', { - defaultMessage: 'Analyze', + kibana: { + id: 'kibana', + label: i18n.translate('core.ui.kibanaNavList.label', { + defaultMessage: 'Kibana', }), + euiIconType: 'logoKibana', order: 1000, }, observability: { + id: 'observability', label: i18n.translate('core.ui.observabilityNavList.label', { defaultMessage: 'Observability', }), @@ -35,6 +38,7 @@ export const DEFAULT_APP_CATEGORIES = Object.freeze({ order: 2000, }, security: { + id: 'security', label: i18n.translate('core.ui.securityNavList.label', { defaultMessage: 'Security', }), @@ -42,9 +46,10 @@ export const DEFAULT_APP_CATEGORIES = Object.freeze({ euiIconType: 'logoSecurity', }, management: { + id: 'management', label: i18n.translate('core.ui.managementNavList.label', { defaultMessage: 'Management', }), - euiIconType: 'managementApp', + order: 5000, }, }); diff --git a/src/core/utils/get_flattened_object.ts b/src/core/utils/get_flattened_object.ts index ce03793284236..25ca0c7c83e26 100644 --- a/src/core/utils/get_flattened_object.ts +++ b/src/core/utils/get_flattened_object.ts @@ -30,8 +30,7 @@ function shouldReadKeys(value: unknown): value is Record { * getFlattenedObject({ a: { b: 1, c: [2,3] } }) * // => { 'a.b': 1, 'a.c': [2,3] } * - * @param {Object} rootValue - * @returns {Object} + * @public */ export function getFlattenedObject(rootValue: Record) { if (!shouldReadKeys(rootValue)) { diff --git a/src/core/utils/url.ts b/src/core/utils/url.ts index c2bf80ce3f86f..910fc8eaa4381 100644 --- a/src/core/utils/url.ts +++ b/src/core/utils/url.ts @@ -23,6 +23,8 @@ import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; * We define our own typings because the current version of @types/node * declares properties to be optional "hostname?: string". * Although, parse call returns "hostname: null | string". + * + * @public */ export interface URLMeaningfulParts { auth?: string | null; @@ -63,6 +65,7 @@ export interface URLMeaningfulParts { * @param url The string url to parse. * @param urlModifier A function that will modify the parsed url, or return a new one. * @returns The modified and reformatted url + * @public */ export function modifyUrl( url: string, @@ -100,6 +103,12 @@ export function modifyUrl( } as UrlObject); } +/** + * Determine if a url is relative. Any url including a protocol, hostname, or + * port is not considered relative. This means that absolute *paths* are considered + * to be relative *urls* + * @public + */ export function isRelativeUrl(candidatePath: string) { // validate that `candidatePath` is not attempting a redirect to somewhere // outside of this Kibana install diff --git a/src/dev/build/build_distributables.js b/src/dev/build/build_distributables.js index 6c2efeebc60c3..910313ac87059 100644 --- a/src/dev/build/build_distributables.js +++ b/src/dev/build/build_distributables.js @@ -40,6 +40,7 @@ import { CreatePackageJsonTask, CreateReadmeTask, CreateRpmPackageTask, + CreateStaticFsWithNodeModulesTask, DownloadNodeBuildsTask, ExtractNodeBuildsTask, InstallDependenciesTask, @@ -126,6 +127,7 @@ export async function buildDistributables(options) { await run(CleanTypescriptTask); await run(CleanExtraFilesFromModulesTask); await run(CleanEmptyFoldersTask); + await run(CreateStaticFsWithNodeModulesTask); /** * copy generic build outputs into platform-specific build diff --git a/src/dev/build/tasks/create_static_fs_with_node_modules_task.js b/src/dev/build/tasks/create_static_fs_with_node_modules_task.js new file mode 100644 index 0000000000000..0ab296fc5c163 --- /dev/null +++ b/src/dev/build/tasks/create_static_fs_with_node_modules_task.js @@ -0,0 +1,64 @@ +/* + * 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 del from 'del'; +import globby from 'globby'; +import { resolve } from 'path'; +import { generateStaticFsVolume } from '@elastic/static-fs'; + +async function deletePathsList(list) { + for (const path of list) { + await del(path); + } +} + +async function getTopLevelNodeModulesFolders(rootDir) { + const nodeModulesFoldersForCwd = await globby(['**/node_modules', '!**/node_modules/**/*'], { + cwd: rootDir, + onlyDirectories: true, + }); + + return nodeModulesFoldersForCwd.map(folder => resolve(rootDir, folder)); +} + +export const CreateStaticFsWithNodeModulesTask = { + description: + 'Creating static filesystem with node_modules, patching entryPoints and deleting node_modules folder', + + async run(config, log, build) { + const rootDir = build.resolvePath('.'); + + // Get all the top node_modules folders + const nodeModulesFolders = await getTopLevelNodeModulesFolders(rootDir); + + // Define root entry points + const rootEntryPoints = [build.resolvePath('src/setup_node_env/index.js')]; + + // Creates the static filesystem with + // every node_module we have + const staticFsAddedPaths = await generateStaticFsVolume( + rootDir, + nodeModulesFolders, + rootEntryPoints + ); + + // Delete node_modules folder + await deletePathsList(staticFsAddedPaths); + }, +}; diff --git a/src/dev/build/tasks/index.js b/src/dev/build/tasks/index.js index 8105fa8a7d5d4..0232ac4b1b5f3 100644 --- a/src/dev/build/tasks/index.js +++ b/src/dev/build/tasks/index.js @@ -25,6 +25,7 @@ export * from './create_archives_task'; export * from './create_empty_dirs_and_files_task'; export * from './create_package_json_task'; export * from './create_readme_task'; +export * from './create_static_fs_with_node_modules_task'; export * from './install_dependencies_task'; export * from './license_file_task'; export * from './nodejs'; diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 8630221b3e94f..601dcc86352a7 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -35,8 +35,8 @@ export const IGNORE_FILE_GLOBS = [ '**/Gruntfile.js', 'tasks/config/**/*', '**/{Dockerfile,docker-compose.yml}', - 'x-pack/legacy/plugins/canvas/tasks/**/*', - 'x-pack/legacy/plugins/canvas/canvas_plugin_src/**/*', + 'x-pack/plugins/canvas/tasks/**/*', + 'x-pack/plugins/canvas/canvas_plugin_src/**/*', 'x-pack/plugins/monitoring/public/lib/jquery_flot/**/*', '**/.*', '**/{webpackShims,__mocks__}/**/*', @@ -48,7 +48,7 @@ export const IGNORE_FILE_GLOBS = [ 'vars/*', // Files in this directory must match a pre-determined name in some cases. - 'x-pack/legacy/plugins/canvas/.storybook/*', + 'x-pack/plugins/canvas/.storybook/*', // filename must match language code which requires capital letters '**/translations/*.json', diff --git a/src/dev/register_git_hook/register_git_hook.js b/src/dev/register_git_hook/register_git_hook.js deleted file mode 100644 index 8820327d3adc0..0000000000000 --- a/src/dev/register_git_hook/register_git_hook.js +++ /dev/null @@ -1,169 +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 chalk from 'chalk'; -import { chmod, unlink, writeFile } from 'fs'; -import dedent from 'dedent'; -import normalizePath from 'normalize-path'; -import os from 'os'; -import { resolve } from 'path'; -import { promisify } from 'util'; -import SimpleGit from 'simple-git'; -import { REPO_ROOT } from '../constants'; - -const simpleGit = new SimpleGit(REPO_ROOT); - -const chmodAsync = promisify(chmod); -const gitRevParseAsync = promisify(simpleGit.revparse.bind(simpleGit)); -const unlinkAsync = promisify(unlink); -const writeFileAsync = promisify(writeFile); - -async function getPrecommitGitHookScriptPath(rootPath) { - // Retrieves the correct location for the .git dir for - // every git setup (including git worktree) - const gitDirPath = (await gitRevParseAsync(['--git-common-dir'])).trim(); - - return resolve(rootPath, gitDirPath, 'hooks/pre-commit'); -} - -function getKbnPrecommitGitHookScript(rootPath, nodeHome, platform) { - return dedent(` - #!/usr/bin/env bash - # - # ** THIS IS AN AUTO-GENERATED FILE ** - # ** PLEASE DO NOT CHANGE IT MANUALLY ** - # - # GENERATED BY ${__dirname} - # IF YOU WANNA CHANGE SOMETHING INTO THIS SCRIPT - # PLEASE RE-RUN 'yarn kbn bootstrap' or 'node scripts/register_git_hook' IN THE ROOT - # OF THE CURRENT PROJECT ${rootPath} - - # pre-commit script takes zero arguments: https://git-scm.com/docs/githooks#_pre_commit - - set -euo pipefail - - # Make it possible to terminate pre commit hook - # using ctrl-c so nothing else would happen or be - # sent to the output. - # - # The correct exit code on that situation - # according the linux documentation project is 130 - # https://www.tldp.org/LDP/abs/html/exitcodes.html - trap "exit 130" INT - - has_node() { - command -v node >/dev/null 2>&1 - } - - has_nvm() { - command -v nvm >/dev/null 2>&1 - } - - try_load_node_from_nvm_paths () { - # If nvm is not loaded, load it - has_node || { - NVM_SH="${nodeHome}/.nvm/nvm.sh" - - if [ "${platform}" == "darwin" ] && [ -s "$(brew --prefix nvm)/nvm.sh" ]; then - NVM_SH="$(brew --prefix nvm)/nvm.sh" - fi - - export NVM_DIR=${nodeHome}/.nvm - - [ -s "$NVM_SH" ] && \. "$NVM_SH" - - # If nvm has been loaded correctly, use project .nvmrc - has_nvm && nvm use - } - } - - extend_user_path() { - if [ "${platform}" == "win32" ]; then - export PATH="$PATH:/c/Program Files/nodejs" - else - export PATH="$PATH:/usr/local/bin:/usr/local" - try_load_node_from_nvm_paths - fi - } - - # Extend path with common path locations for node - # in order to make the hook working on git GUI apps - extend_user_path - - # Check if we have node js bin in path - has_node || { - echo "Can't found node bin in the PATH. Please update the PATH to proceed." - echo "If your PATH already has the node bin, maybe you are using some git GUI app." - echo "Can't found node bin in the PATH. Please update the PATH to proceed." - echo "If your PATH already has the node bin, maybe you are using some git GUI app not launched from the shell." - echo "In order to proceed, you need to config the PATH used by the application that are launching your git GUI app." - echo "If you are running macOS, you can do that using:" - echo "'sudo launchctl config user path /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'" - - exit 1 - } - - execute_precommit_hook() { - node scripts/precommit_hook || return 1 - - PRECOMMIT_FILE="./.git/hooks/pre-commit.local" - if [ -x "\${PRECOMMIT_FILE}" ]; then - echo "Executing local precommit hook found in \${PRECOMMIT_FILE}" - "$PRECOMMIT_FILE" || return 1 - fi - } - - execute_precommit_hook || { - echo "Pre-commit hook failed (add --no-verify to bypass)"; - echo ' For eslint failures you can try running \`node scripts/precommit_hook --fix\`'; - exit 1; - } - - exit 0 - `); -} - -export async function registerPrecommitGitHook(log) { - log.write(chalk.bold(`Registering Kibana pre-commit git hook...\n`)); - - try { - await writeGitHook( - await getPrecommitGitHookScriptPath(REPO_ROOT), - getKbnPrecommitGitHookScript(REPO_ROOT, normalizePath(os.homedir()), process.platform) - ); - } catch (e) { - log.write( - `${chalk.red('fail')} Kibana pre-commit git hook was not installed as an error occur.\n` - ); - throw e; - } - - log.write(`${chalk.green('success')} Kibana pre-commit git hook was installed successfully.\n`); -} - -async function writeGitHook(gitHookScriptPath, kbnHookScriptSource) { - try { - await unlinkAsync(gitHookScriptPath); - } catch (e) { - /* no-op */ - } - - await writeFileAsync(gitHookScriptPath, kbnHookScriptSource); - await chmodAsync(gitHookScriptPath, 0o755); -} diff --git a/src/dev/renovate/package_groups.ts b/src/dev/renovate/package_groups.ts index 1bc65fd149f47..9f5aa8556ac21 100644 --- a/src/dev/renovate/package_groups.ts +++ b/src/dev/renovate/package_groups.ts @@ -159,7 +159,17 @@ export const RENOVATE_PACKAGE_GROUPS: PackageGroup[] = [ { name: 'hapi', packageWords: ['hapi'], - packageNames: ['hapi', 'joi', 'boom', 'hoek', 'h2o2', '@elastic/good', 'good-squeeze', 'inert'], + packageNames: [ + 'hapi', + 'joi', + 'boom', + 'hoek', + 'h2o2', + '@elastic/good', + 'good-squeeze', + 'inert', + 'accept', + ], }, { diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 4dc930dae3e25..416702c56d852 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -18,12 +18,13 @@ */ export const storybookAliases = { + advanced_ui_actions: 'x-pack/plugins/advanced_ui_actions/scripts/storybook.js', apm: 'x-pack/plugins/apm/scripts/storybook.js', - canvas: 'x-pack/legacy/plugins/canvas/scripts/storybook_new.js', + canvas: 'x-pack/plugins/canvas/scripts/storybook_new.js', codeeditor: 'src/plugins/kibana_react/public/code_editor/scripts/storybook.ts', + dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/scripts/storybook.js', drilldowns: 'x-pack/plugins/drilldowns/scripts/storybook.js', embeddable: 'src/plugins/embeddable/scripts/storybook.js', infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js', siem: 'x-pack/plugins/siem/scripts/storybook.js', - ui_actions: 'x-pack/plugins/advanced_ui_actions/scripts/storybook.js', }; diff --git a/src/legacy/core_plugins/interpreter/README.md b/src/legacy/core_plugins/interpreter/README.md deleted file mode 100644 index 6d90ce2d5e2eb..0000000000000 --- a/src/legacy/core_plugins/interpreter/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Interpreter legacy plugin has been migrated to the New Platform. Use -`expressions` New Platform plugin instead. diff --git a/src/legacy/core_plugins/interpreter/index.ts b/src/legacy/core_plugins/interpreter/index.ts deleted file mode 100644 index 9427a2f8a2d0f..0000000000000 --- a/src/legacy/core_plugins/interpreter/index.ts +++ /dev/null @@ -1,44 +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 { resolve } from 'path'; -import { Legacy } from '../../../../kibana'; -import { init } from './init'; - -// eslint-disable-next-line -export default function InterpreterPlugin(kibana: any) { - const config: Legacy.PluginSpecOptions = { - id: 'interpreter', - require: ['kibana', 'elasticsearch'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - injectDefaultVars: server => ({ - serverBasePath: server.config().get('server.basePath'), - }), - }, - config: (Joi: any) => { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - init, - }; - - return new kibana.Plugin(config); -} diff --git a/src/legacy/core_plugins/interpreter/init.ts b/src/legacy/core_plugins/interpreter/init.ts deleted file mode 100644 index 46da1539afadb..0000000000000 --- a/src/legacy/core_plugins/interpreter/init.ts +++ /dev/null @@ -1,52 +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. - */ - -/* eslint-disable max-classes-per-file */ - -// @ts-ignore -import { register, registryFactory, Registry, Fn } from '@kbn/interpreter/common'; - -import { Legacy } from '../../../../kibana'; - -export async function init(server: Legacy.Server /* options */) { - server.injectUiAppVars('canvas', () => { - const config = server.config(); - const basePath = config.get('server.basePath'); - const reportingBrowserType = (() => { - const configKey = 'xpack.reporting.capture.browser.type'; - if (!config.has(configKey)) { - return null; - } - return config.get(configKey); - })(); - - return { - kbnIndex: config.get('kibana.index'), - serverFunctions: (server.newPlatform.setup.plugins.expressions as any).__LEGACY - .registries() - .serverFunctions.toArray(), - basePath, - reportingBrowserType, - }; - }); - - // Expose server.plugins.interpreter.register(specs) and - // server.plugins.interpreter.registries() (a getter). - server.expose((server.newPlatform.setup.plugins.expressions as any).__LEGACY); -} diff --git a/src/legacy/core_plugins/interpreter/package.json b/src/legacy/core_plugins/interpreter/package.json deleted file mode 100644 index 3265dadd7fbfc..0000000000000 --- a/src/legacy/core_plugins/interpreter/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "interpreter", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/interpreter/public/canvas/load_legacy_server_function_wrappers.ts b/src/legacy/core_plugins/interpreter/public/canvas/load_legacy_server_function_wrappers.ts deleted file mode 100644 index fed157846a1a1..0000000000000 --- a/src/legacy/core_plugins/interpreter/public/canvas/load_legacy_server_function_wrappers.ts +++ /dev/null @@ -1,33 +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. - */ - -/** - * This file needs to be deleted by 8.0 release. It is here to load available - * server side functions and create a wrappers around them on client side, to - * execute them from client side. This functionality is used only by Canvas - * and all server side functions are in Canvas plugin. - * - * In 8.0 there will be no server-side functions, plugins will register only - * client side functions and if they need those to execute something on the - * server side, it should be respective function's internal implementation detail. - */ - -import { npSetup } from 'ui/new_platform'; - -export const { loadLegacyServerFunctionWrappers } = npSetup.plugins.expressions.__LEGACY; diff --git a/src/legacy/core_plugins/interpreter/public/interpreter.ts b/src/legacy/core_plugins/interpreter/public/interpreter.ts deleted file mode 100644 index 319a2779010c3..0000000000000 --- a/src/legacy/core_plugins/interpreter/public/interpreter.ts +++ /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 'uiExports/interpreter'; -// @ts-ignore -import { register, registryFactory } from '@kbn/interpreter/common'; -import { npSetup } from 'ui/new_platform'; -import { registries } from './registries'; -import { Executor, ExpressionExecutor } from '../../../../plugins/expressions/public'; - -// Expose kbnInterpreter.register(specs) and kbnInterpreter.registries() globally so that plugins -// can register without a transpile step. -// TODO: This will be left behind in then legacy platform? -(global as any).kbnInterpreter = Object.assign( - (global as any).kbnInterpreter || {}, - registryFactory(registries) -); - -// TODO: This function will be left behind in the legacy platform. -let executorPromise: Promise | undefined; -export const getInterpreter = async () => { - if (!executorPromise) { - const executor = npSetup.plugins.expressions.__LEGACY.getExecutor(); - executorPromise = Promise.resolve(executor); - } - return await executorPromise; -}; - -// TODO: This function will be left behind in the legacy platform. -export const interpretAst: Executor['run'] = async (ast, context, handlers) => { - const { interpreter } = await getInterpreter(); - return await interpreter.interpretAst(ast, context, handlers); -}; diff --git a/src/legacy/core_plugins/interpreter/public/registries.karma_mock.ts b/src/legacy/core_plugins/interpreter/public/registries.karma_mock.ts deleted file mode 100644 index 0f37f33cc1b13..0000000000000 --- a/src/legacy/core_plugins/interpreter/public/registries.karma_mock.ts +++ /dev/null @@ -1,44 +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 sinon from 'sinon'; - -export const functionsRegistry = {}; -export const renderersRegistry = {}; -export const typesRegistry = {}; -export const registries = { - browserFunctions: functionsRegistry, - renderers: renderersRegistry, - types: typesRegistry, - loadLegacyServerFunctionWrappers: () => Promise.resolve(), -}; - -const resetRegistry = (registry: any) => { - registry.wrapper = sinon.stub(); - registry.register = sinon.stub(); - registry.toJS = sinon.stub(); - registry.toArray = sinon.stub(); - registry.get = sinon.stub(); - registry.getProp = sinon.stub(); - registry.reset = sinon.stub(); -}; -const resetAll = () => Object.values(registries).forEach(resetRegistry); - -resetAll(); -afterEach(resetAll); diff --git a/src/legacy/core_plugins/interpreter/public/registries.ts b/src/legacy/core_plugins/interpreter/public/registries.ts deleted file mode 100644 index 63fd9089acf4a..0000000000000 --- a/src/legacy/core_plugins/interpreter/public/registries.ts +++ /dev/null @@ -1,29 +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 { npSetup } from 'ui/new_platform'; - -export const functionsRegistry = npSetup.plugins.expressions.__LEGACY.functions; -export const renderersRegistry = npSetup.plugins.expressions.__LEGACY.renderers; -export const typesRegistry = npSetup.plugins.expressions.__LEGACY.types; -export const registries = { - browserFunctions: functionsRegistry, - renderers: renderersRegistry, - types: typesRegistry, -}; diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 48d86e3628e49..6664cf0d7366d 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -24,11 +24,11 @@ import { promisify } from 'util'; import { importApi } from './server/routes/api/import'; import { exportApi } from './server/routes/api/export'; import mappings from './mappings.json'; -import { getUiSettingDefaults } from './ui_setting_defaults'; +import { getUiSettingDefaults } from './server/ui_setting_defaults'; import { registerCspCollector } from './server/lib/csp_usage_collector'; import { injectVars } from './inject_vars'; import { i18n } from '@kbn/i18n'; -import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; import { kbnBaseUrl } from '../../../plugins/kibana_legacy/server'; const mkdirAsync = promisify(Fs.mkdir); @@ -53,7 +53,7 @@ export default function(kibana) { }, uiExports: { - hacks: ['plugins/kibana/discover/legacy', 'plugins/kibana/dev_tools'], + hacks: ['plugins/kibana/dev_tools'], app: { id: 'kibana', title: 'Kibana', @@ -67,33 +67,33 @@ export default function(kibana) { title: i18n.translate('kbn.discoverTitle', { defaultMessage: 'Discover', }), - order: -1003, + order: 2000, url: `${kbnBaseUrl}#/discover`, euiIconType: 'discoverApp', disableSubUrlTracking: true, - category: DEFAULT_APP_CATEGORIES.analyze, + category: DEFAULT_APP_CATEGORIES.kibana, }, { id: 'kibana:visualize', title: i18n.translate('kbn.visualizeTitle', { defaultMessage: 'Visualize', }), - order: -1002, + order: 7000, url: `${kbnBaseUrl}#/visualize`, euiIconType: 'visualizeApp', disableSubUrlTracking: true, - category: DEFAULT_APP_CATEGORIES.analyze, + category: DEFAULT_APP_CATEGORIES.kibana, }, { id: 'kibana:dashboard', title: i18n.translate('kbn.dashboardTitle', { defaultMessage: 'Dashboard', }), - order: -1001, + order: 1000, url: `${kbnBaseUrl}#/dashboards`, euiIconType: 'dashboardApp', disableSubUrlTracking: true, - category: DEFAULT_APP_CATEGORIES.analyze, + category: DEFAULT_APP_CATEGORIES.kibana, }, { id: 'kibana:dev_tools', @@ -108,7 +108,7 @@ export default function(kibana) { { id: 'kibana:stack_management', title: i18n.translate('kbn.managementTitle', { - defaultMessage: 'Management', + defaultMessage: 'Stack Management', }), order: 9003, url: `${kbnBaseUrl}#/management`, diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js b/src/legacy/core_plugins/kibana/public/__tests__/discover/doc_table.js similarity index 96% rename from src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js rename to src/legacy/core_plugins/kibana/public/__tests__/discover/doc_table.js index 9e74df08233da..edf65fdb56220 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/discover/doc_table.js @@ -16,18 +16,15 @@ * specific language governing permissions and limitations * under the License. */ - import angular from 'angular'; import expect from '@kbn/expect'; import _ from 'lodash'; import ngMock from 'ng_mock'; import 'ui/private'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; +import { pluginInstance } from './legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import hits from 'fixtures/real_hits'; -// Load the kibana app dependencies. - let $parentScope; let $scope; @@ -60,6 +57,7 @@ const destroy = function() { describe('docTable', function() { let $elem; + beforeEach(() => pluginInstance.initializeInnerAngular()); beforeEach(() => pluginInstance.initializeServices()); beforeEach(ngMock.module('app/discover')); diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/fixed_scroll.js b/src/legacy/core_plugins/kibana/public/__tests__/discover/fixed_scroll.js similarity index 89% rename from src/legacy/core_plugins/kibana/public/discover/__tests__/directives/fixed_scroll.js rename to src/legacy/core_plugins/kibana/public/__tests__/discover/fixed_scroll.js index 49a0df54079ea..4a8736cc0d6a4 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/fixed_scroll.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/discover/fixed_scroll.js @@ -17,21 +17,33 @@ * under the License. */ +/* eslint-disable @kbn/eslint/no-restricted-paths */ + +import angular from 'angular'; import expect from '@kbn/expect'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; import ngMock from 'ng_mock'; import $ from 'jquery'; import sinon from 'sinon'; +import { PrivateProvider } from '../../../../../../plugins/kibana_legacy/public'; +import { FixedScrollProvider } from '../../../../../../plugins/discover/public/application/angular/directives/fixed_scroll'; +import { DebounceProviderTimeout } from '../../../../../../plugins/discover/public/application/angular/directives/debounce/debounce'; + +const testModuleName = 'fixedScroll'; + +angular + .module(testModuleName, []) + .provider('Private', PrivateProvider) + .service('debounce', ['$timeout', DebounceProviderTimeout]) + .directive('fixedScroll', FixedScrollProvider); + describe('FixedScroll directive', function() { const sandbox = sinon.createSandbox(); let compile; let flushPendingTasks; const trash = []; - beforeEach(() => pluginInstance.initializeServices()); - beforeEach(() => pluginInstance.initializeInnerAngular()); - beforeEach(ngMock.module('app/discover')); + beforeEach(ngMock.module(testModuleName)); beforeEach( ngMock.inject(function($compile, $rootScope, $timeout) { flushPendingTasks = function flushPendingTasks() { diff --git a/src/legacy/core_plugins/kibana/public/discover/legacy.ts b/src/legacy/core_plugins/kibana/public/__tests__/discover/legacy.ts similarity index 80% rename from src/legacy/core_plugins/kibana/public/discover/legacy.ts rename to src/legacy/core_plugins/kibana/public/__tests__/discover/legacy.ts index f08fd22c71850..ecda2a8c15395 100644 --- a/src/legacy/core_plugins/kibana/public/discover/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/__tests__/discover/legacy.ts @@ -17,11 +17,11 @@ * under the License. */ -import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { plugin } from './index'; +import { plugin } from '../../../../../../plugins/discover/public'; +import { coreMock } from '../../../../../../core/public/mocks'; +const context = coreMock.createPluginInitializerContext(); -// Legacy compatibility part - to be removed at cutover, replaced by a kibana.json file -export const pluginInstance = plugin({} as PluginInitializerContext); +export const pluginInstance = plugin(context); export const setup = pluginInstance.setup(npSetup.core, npSetup.plugins); export const start = pluginInstance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/lib/rows_headers.js b/src/legacy/core_plugins/kibana/public/__tests__/discover/row_headers.js similarity index 98% rename from src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/lib/rows_headers.js rename to src/legacy/core_plugins/kibana/public/__tests__/discover/row_headers.js index 9b63b8cd18f3e..5450a4127b63c 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/lib/rows_headers.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/discover/row_headers.js @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - import angular from 'angular'; import _ from 'lodash'; import sinon from 'sinon'; @@ -24,7 +23,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { getFakeRow, getFakeRowVals } from 'fixtures/fake_row'; import $ from 'jquery'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; +import { pluginInstance } from './legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('Doc Table', function() { @@ -218,7 +217,7 @@ describe('Doc Table', function() { }); /** this no longer works with the new plugin approach - it('should render even when the row source contains a field with the same name as a meta field', function () { + it('should render even when the row source contains a field with the same name as a meta field', function () { setTimeout(() => { //this should be overridden by later changes }, 100); diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table.js index b212ecf578dd1..de85bec011eeb 100644 --- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table.js @@ -415,7 +415,7 @@ describe('Table Vis - AggTable Directive', function() { ); $percentageColValues.each((i, value) => { - const percentage = `${round((counts[i] / total) * 100, 1)}%`; + const percentage = `${round((counts[i] / total) * 100, 3)}%`; expect(value).to.be(percentage); }); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/_index.scss b/src/legacy/core_plugins/kibana/public/discover/_index.scss deleted file mode 100644 index 386472a9f6e01..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -// Discover plugin styles -@import 'np_ready/index'; diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts deleted file mode 100644 index 702331529b879..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts +++ /dev/null @@ -1,236 +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 { BehaviorSubject } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { AppMountParameters, CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import angular, { auto } from 'angular'; -import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public'; -import { - DataPublicPluginStart, - DataPublicPluginSetup, - esFilters, -} from '../../../../../plugins/data/public'; -import { registerFeature } from './np_ready/register_feature'; -import './kibana_services'; -import { EmbeddableStart, EmbeddableSetup } from '../../../../../plugins/embeddable/public'; -import { getInnerAngularModule, getInnerAngularModuleEmbeddable } from './get_inner_angular'; -import { getHistory, setAngularModule, setServices, setUrlTracker } from './kibana_services'; -import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; -import { ChartsPluginStart } from '../../../../../plugins/charts/public'; -import { buildServices } from './build_services'; -import { SharePluginStart } from '../../../../../plugins/share/public'; -import { - KibanaLegacySetup, - AngularRenderedAppUpdater, -} from '../../../../../plugins/kibana_legacy/public'; -import { DiscoverSetup, DiscoverStart } from '../../../../../plugins/discover/public'; -import { HomePublicPluginSetup } from '../../../../../plugins/home/public'; -import { - VisualizationsStart, - VisualizationsSetup, -} from '../../../../../plugins/visualizations/public'; -import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public'; - -export interface DiscoverSetupPlugins { - uiActions: UiActionsSetup; - embeddable: EmbeddableSetup; - kibanaLegacy: KibanaLegacySetup; - home: HomePublicPluginSetup; - visualizations: VisualizationsSetup; - data: DataPublicPluginSetup; - discover: DiscoverSetup; -} -export interface DiscoverStartPlugins { - uiActions: UiActionsStart; - embeddable: EmbeddableStart; - navigation: NavigationStart; - charts: ChartsPluginStart; - data: DataPublicPluginStart; - share: SharePluginStart; - inspector: any; - visualizations: VisualizationsStart; - discover: DiscoverStart; -} -const innerAngularName = 'app/discover'; -const embeddableAngularName = 'app/discoverEmbeddable'; - -/** - * Contains Discover, one of the oldest parts of Kibana - * There are 2 kinds of Angular bootstrapped for rendering, additionally to the main Angular - * Discover provides embeddables, those contain a slimmer Angular - */ -export class DiscoverPlugin implements Plugin { - private servicesInitialized: boolean = false; - private innerAngularInitialized: boolean = false; - private embeddableInjector: auto.IInjectorService | null = null; - private getEmbeddableInjector: (() => Promise) | null = null; - private appStateUpdater = new BehaviorSubject(() => ({})); - private stopUrlTracking: (() => void) | undefined = undefined; - - /** - * why are those functions public? they are needed for some mocha tests - * can be removed once all is Jest - */ - public initializeInnerAngular?: () => void; - public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>; - - setup(core: CoreSetup, plugins: DiscoverSetupPlugins) { - const { - appMounted, - appUnMounted, - stop: stopUrlTracker, - setActiveUrl: setTrackedUrl, - } = createKbnUrlTracker({ - // we pass getter here instead of plain `history`, - // so history is lazily created (when app is mounted) - // this prevents redundant `#` when not in discover app - getHistory, - baseUrl: core.http.basePath.prepend('/app/kibana'), - defaultSubUrl: '#/discover', - storageKey: `lastUrl:${core.http.basePath.get()}:discover`, - navLinkUpdater$: this.appStateUpdater, - toastNotifications: core.notifications.toasts, - stateParams: [ - { - kbnUrlKey: '_g', - stateUpdate$: plugins.data.query.state$.pipe( - filter( - ({ changes }) => !!(changes.globalFilters || changes.time || changes.refreshInterval) - ), - map(({ state }) => ({ - ...state, - filters: state.filters?.filter(esFilters.isFilterPinned), - })) - ), - }, - ], - }); - setUrlTracker({ setTrackedUrl }); - this.stopUrlTracking = () => { - stopUrlTracker(); - }; - - this.getEmbeddableInjector = this.getInjector.bind(this); - plugins.discover.docViews.setAngularInjectorGetter(this.getEmbeddableInjector); - plugins.kibanaLegacy.registerLegacyApp({ - id: 'discover', - title: 'Discover', - updater$: this.appStateUpdater.asObservable(), - navLinkId: 'kibana:discover', - order: -1004, - euiIconType: 'discoverApp', - mount: async (params: AppMountParameters) => { - if (!this.initializeServices) { - throw Error('Discover plugin method initializeServices is undefined'); - } - if (!this.initializeInnerAngular) { - throw Error('Discover plugin method initializeInnerAngular is undefined'); - } - appMounted(); - await this.initializeServices(); - await this.initializeInnerAngular(); - - // make sure the index pattern list is up to date - const [, { data: dataStart }] = await core.getStartServices(); - await dataStart.indexPatterns.clearCache(); - const { renderApp } = await import('./np_ready/application'); - const unmount = await renderApp(innerAngularName, params.element); - return () => { - unmount(); - appUnMounted(); - }; - }, - }); - registerFeature(plugins.home); - this.registerEmbeddable(core, plugins); - } - - start(core: CoreStart, plugins: DiscoverStartPlugins) { - // we need to register the application service at setup, but to render it - // there are some start dependencies necessary, for this reason - // initializeInnerAngular + initializeServices are assigned at start and used - // when the application/embeddable is mounted - this.initializeInnerAngular = async () => { - if (this.innerAngularInitialized) { - return; - } - // this is used by application mount and tests - const module = getInnerAngularModule(innerAngularName, core, plugins); - setAngularModule(module); - this.innerAngularInitialized = true; - }; - - this.initializeServices = async () => { - if (this.servicesInitialized) { - return { core, plugins }; - } - const services = await buildServices(core, plugins, getHistory); - setServices(services); - this.servicesInitialized = true; - - return { core, plugins }; - }; - } - - stop() { - if (this.stopUrlTracking) { - this.stopUrlTracking(); - } - } - - /** - * register embeddable with a slimmer embeddable version of inner angular - */ - private async registerEmbeddable( - core: CoreSetup, - plugins: DiscoverSetupPlugins - ) { - const { SearchEmbeddableFactory } = await import('./np_ready/embeddable'); - - if (!this.getEmbeddableInjector) { - throw Error('Discover plugin method getEmbeddableInjector is undefined'); - } - - const getStartServices = async () => { - const [coreStart, deps] = await core.getStartServices(); - return { - executeTriggerActions: deps.uiActions.executeTriggerActions, - isEditable: () => coreStart.application.capabilities.discover.save as boolean, - }; - }; - - const factory = new SearchEmbeddableFactory(getStartServices, this.getEmbeddableInjector); - plugins.embeddable.registerEmbeddableFactory(factory.type, factory); - } - - private async getInjector() { - if (!this.embeddableInjector) { - if (!this.initializeServices) { - throw Error('Discover plugin getEmbeddableInjector: initializeServices is undefined'); - } - const { core, plugins } = await this.initializeServices(); - getInnerAngularModuleEmbeddable(embeddableAngularName, core, plugins); - const mountpoint = document.createElement('div'); - this.embeddableInjector = angular.bootstrap(mountpoint, [embeddableAngularName]); - } - - return this.embeddableInjector; - } -} diff --git a/src/legacy/core_plugins/kibana/public/index.scss b/src/legacy/core_plugins/kibana/public/index.scss index 26805554370b9..6a2e65e3a9ff5 100644 --- a/src/legacy/core_plugins/kibana/public/index.scss +++ b/src/legacy/core_plugins/kibana/public/index.scss @@ -7,9 +7,6 @@ // Public UI styles @import 'src/legacy/ui/public/index'; -// Discover styles -@import 'discover/index'; - // Has to come after visualize because of some // bad cascading in the Editor layout @import '../../../../plugins/maps_legacy/public/index'; diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js index ea0d5ad3790b1..ad67a74121cc9 100644 --- a/src/legacy/core_plugins/kibana/public/kibana.js +++ b/src/legacy/core_plugins/kibana/public/kibana.js @@ -42,7 +42,6 @@ import 'uiExports/shareContextMenuExtensions'; import 'uiExports/interpreter'; import 'ui/autoload/all'; -import './discover/legacy'; import './management'; import './dev_tools'; import { showAppRedirectNotification } from '../../../../plugins/kibana_legacy/public'; diff --git a/src/legacy/core_plugins/kibana/public/management/index.js b/src/legacy/core_plugins/kibana/public/management/index.js index 6a36391c56b5c..2cba9fab7be22 100644 --- a/src/legacy/core_plugins/kibana/public/management/index.js +++ b/src/legacy/core_plugins/kibana/public/management/index.js @@ -69,7 +69,7 @@ export function updateLandingPage(version) {

diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap index ed65db10e0acb..1545ab8cb9b1c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap @@ -149,6 +149,8 @@ exports[`CreateIndexPatternWizard renders time field step when step is set to 2 indexPatternsService={ Object { "clearCache": [MockFunction], + "createField": [MockFunction], + "createFieldList": [MockFunction], "ensureDefaultIndexPattern": [MockFunction], "get": [MockFunction], "make": [Function], diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx index 564f115cf2c48..3b865f7d5e1ea 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx @@ -24,8 +24,8 @@ import { FieldEditor } from 'ui/field_editor'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { HttpStart, DocLinksStart } from 'src/core/public'; +import { IndexPattern, DataPublicPluginStart } from 'src/plugins/data/public'; import { IndexHeader } from '../index_header'; -import { IndexPattern, IndexPatternField } from '../../../../../../../../../plugins/data/public'; import { ChromeDocTitle, NotificationsStart } from '../../../../../../../../../core/public'; import { TAB_SCRIPTED_FIELDS, TAB_INDEXED_FIELDS } from '../constants'; @@ -36,6 +36,7 @@ interface CreateEditFieldProps extends RouteComponentProps { fieldFormatEditors: any; getConfig: (name: string) => any; services: { + dataStart: DataPublicPluginStart; notifications: NotificationsStart; docTitle: ChromeDocTitle; getHttpStart: () => HttpStart; @@ -63,10 +64,14 @@ export const CreateEditField = withRouter( const field = mode === 'edit' && fieldName ? indexPattern.fields.getByName(fieldName) - : new IndexPatternField(indexPattern, { - scripted: true, - type: 'number', - }); + : services.dataStart.indexPatterns.createField( + indexPattern, + { + scripted: true, + type: 'number', + }, + false + ); const url = `/management/kibana/index_patterns/${indexPattern.id}`; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index.js index e2f387c0291a7..ab1fa546e5ea8 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index.js @@ -112,6 +112,7 @@ const renderCreateEditField = ($scope, $route, getConfig, fieldFormatEditors) => fieldFormatEditors={fieldFormatEditors} getConfig={getConfig} services={{ + dataStart: npStart.plugins.data, getHttpStart: () => npStart.core.http, notifications: npStart.core.notifications, docTitle: npStart.core.chrome.docTitle, diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/server/ui_setting_defaults.js similarity index 98% rename from src/legacy/core_plugins/kibana/ui_setting_defaults.js rename to src/legacy/core_plugins/kibana/server/ui_setting_defaults.js index 85b1956f45333..91c61886d216c 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/server/ui_setting_defaults.js @@ -16,13 +16,14 @@ * specific language governing permissions and limitations * under the License. */ + import moment from 'moment-timezone'; import numeralLanguages from '@elastic/numeral/languages'; import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; -import { DEFAULT_QUERY_LANGUAGE } from '../../../plugins/data/common'; -import { isRelativeUrl } from '../../../core/utils'; +import { isRelativeUrl } from '../../../../core/server'; +import { DEFAULT_QUERY_LANGUAGE } from '../../../../plugins/data/common'; export function getUiSettingDefaults() { const weekdays = moment.weekdays().slice(); @@ -1171,5 +1172,25 @@ export function getUiSettingDefaults() { category: ['accessibility'], requiresPageReload: true, }, + pageNavigation: { + name: i18n.translate('kbn.advancedSettings.pageNavigationName', { + defaultMessage: 'Side nav style', + }), + value: 'modern', + description: i18n.translate('kbn.advancedSettings.pageNavigationDesc', { + defaultMessage: 'Change the style of navigation', + }), + type: 'select', + options: ['modern', 'legacy'], + optionLabels: { + modern: i18n.translate('kbn.advancedSettings.pageNavigationModern', { + defaultMessage: 'Modern', + }), + legacy: i18n.translate('kbn.advancedSettings.pageNavigationLegacy', { + defaultMessage: 'Legacy', + }), + }, + schema: schema.oneOf([schema.literal('modern'), schema.literal('legacy')]), + }, }; } diff --git a/src/legacy/core_plugins/timelion/index.ts b/src/legacy/core_plugins/timelion/index.ts index 41a15dc4e0186..31926f658ec13 100644 --- a/src/legacy/core_plugins/timelion/index.ts +++ b/src/legacy/core_plugins/timelion/index.ts @@ -21,7 +21,7 @@ import { resolve } from 'path'; import { i18n } from '@kbn/i18n'; import { Legacy } from 'kibana'; import { LegacyPluginApi, LegacyPluginInitializer } from 'src/legacy/plugin_discovery/types'; -import { DEFAULT_APP_CATEGORIES } from '../../../core/utils'; +import { DEFAULT_APP_CATEGORIES } from '../../../core/server'; const experimentalLabel = i18n.translate('timelion.uiSettings.experimentalLabel', { defaultMessage: 'experimental', @@ -54,11 +54,11 @@ const timelionPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPl uiExports: { app: { title: 'Timelion', - order: -1000, + order: 8000, icon: 'plugins/timelion/icon.svg', euiIconType: 'timelionApp', main: 'plugins/timelion/app', - category: DEFAULT_APP_CATEGORIES.analyze, + category: DEFAULT_APP_CATEGORIES.kibana, }, styleSheetPaths: resolve(__dirname, 'public/index.scss'), hacks: [resolve(__dirname, 'public/legacy')], diff --git a/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap b/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap index 365f3afdab395..ad13256c8245a 100644 --- a/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap +++ b/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap @@ -29,13 +29,8 @@ exports[`is rendered 1`] = ` data-test-subj="exitFullScreenModeText" >
-

- Elastic Kibana -

Exit full screen diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap index cba8e85a65249..f0766df176c0d 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap @@ -4,12 +4,7 @@ exports[`LabelTemplateFlyout should not render if not visible 1`] = `""`; exports[`LabelTemplateFlyout should render normally 1`] = ` diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap index 849e307f7b527..fd697a2a4c70a 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap @@ -4,12 +4,7 @@ exports[`UrlTemplateFlyout should not render if not visible 1`] = `""`; exports[`UrlTemplateFlyout should render normally 1`] = ` diff --git a/src/legacy/ui/public/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap b/src/legacy/ui/public/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap index 282e8e311d984..6991281dc86a9 100644 --- a/src/legacy/ui/public/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap +++ b/src/legacy/ui/public/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap @@ -2,13 +2,8 @@ exports[`ScriptingHelpFlyout should render normally 1`] = ` ({ FieldFormatEditor: 'field-format-editor', })); -const fields: Field[] = [ +const fields: IndexPatternField[] = [ { name: 'foobar', - } as Field, + } as IndexPatternField, ]; // @ts-ignore @@ -114,7 +114,7 @@ describe('FieldEditor', () => { beforeEach(() => { indexPattern = ({ - fields: fields as IndexPatternFieldList, + fields: fields as IIndexPatternFieldList, } as unknown) as IndexPattern; npStart.plugins.data.fieldFormats.getDefaultType = jest.fn( @@ -133,7 +133,7 @@ describe('FieldEditor', () => { const component = shallowWithI18nProvider( ); @@ -149,18 +149,18 @@ describe('FieldEditor', () => { name: 'test', script: 'doc.test.value', }; - indexPattern.fields.push(testField as Field); + indexPattern.fields.push(testField as IndexPatternField); indexPattern.fields.getByName = name => { const flds = { [testField.name]: testField, }; - return flds[name] as Field; + return flds[name] as IndexPatternField; }; const component = shallowWithI18nProvider( ); @@ -177,18 +177,18 @@ describe('FieldEditor', () => { script: 'doc.test.value', lang: 'testlang', }; - indexPattern.fields.push((testField as unknown) as Field); + indexPattern.fields.push((testField as unknown) as IndexPatternField); indexPattern.fields.getByName = name => { const flds = { [testField.name]: testField, }; - return flds[name] as Field; + return flds[name] as IndexPatternField; }; const component = shallowWithI18nProvider( ); @@ -203,7 +203,7 @@ describe('FieldEditor', () => { const component = shallowWithI18nProvider( ); @@ -226,7 +226,7 @@ describe('FieldEditor', () => { const component = shallowWithI18nProvider( ); diff --git a/src/legacy/ui/public/field_editor/field_editor.tsx b/src/legacy/ui/public/field_editor/field_editor.tsx index aa62a53f2c32a..7de70f5d956e8 100644 --- a/src/legacy/ui/public/field_editor/field_editor.tsx +++ b/src/legacy/ui/public/field_editor/field_editor.tsx @@ -55,13 +55,13 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { + IndexPatternField, + FieldFormatInstanceType, IndexPattern, IFieldType, KBN_FIELD_TYPES, ES_FIELD_TYPES, } from '../../../../plugins/data/public'; -import { FieldFormatInstanceType } from '../../../../plugins/data/common'; -import { Field } from '../../../../plugins/data/public'; import { ScriptingDisabledCallOut, ScriptingWarningCallOut, @@ -114,7 +114,7 @@ interface InitialFieldTypeFormat extends FieldTypeFormat { defaultFieldFormat: FieldFormatInstanceType; } -interface FieldClone extends Field { +interface FieldClone extends IndexPatternField { format: any; } @@ -139,7 +139,7 @@ export interface FieldEditorState { export interface FieldEdiorProps { indexPattern: IndexPattern; - field: Field; + field: IndexPatternField; helpers: { getConfig: (key: string) => any; getHttpStart: () => HttpStart; diff --git a/src/legacy/ui/public/management/breadcrumbs.ts b/src/legacy/ui/public/management/breadcrumbs.ts index e6156b6639ac4..936e99caff565 100644 --- a/src/legacy/ui/public/management/breadcrumbs.ts +++ b/src/legacy/ui/public/management/breadcrumbs.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; export const MANAGEMENT_BREADCRUMB = Object.freeze({ text: i18n.translate('common.ui.stackManagement.breadcrumb', { - defaultMessage: 'Management', + defaultMessage: 'Stack Management', }), href: '#/management', }); diff --git a/src/legacy/ui/public/styles/_legacy/_base.scss b/src/legacy/ui/public/styles/_legacy/_base.scss index 0fcfb515c7c90..fd0a1335f9685 100644 --- a/src/legacy/ui/public/styles/_legacy/_base.scss +++ b/src/legacy/ui/public/styles/_legacy/_base.scss @@ -1,3 +1,5 @@ +@import '@elastic/eui/src/components/collapsible_nav/variables'; + // Forms // Angular form states @@ -38,7 +40,9 @@ input[type='checkbox'], // Application Layout // chrome-context -.content { +// TODO #64541 +// Delete this block +.chrHeaderWrapper:not(.headerWrapper) .content { display: flex; flex-flow: row nowrap; width: 100%; @@ -119,7 +123,7 @@ input[type='checkbox'], } } -// A neccessary hack so that the above focus policy doesn't polute some EUI +// A necessary hack so that the above focus policy doesn't pollute some EUI // entrenched inputs. .euiComboBox { // :not() specificity needed to override the above @@ -128,6 +132,10 @@ input[type='checkbox'], } } +.euiBody--collapsibleNavIsDocked .euiBottomBar { + margin-left: $euiCollapsibleNavWidth; +} + // Utility classes .fullWidth { diff --git a/src/legacy/ui/public/styles/_legacy/components/_table.scss b/src/legacy/ui/public/styles/_legacy/components/_table.scss index c9472cbd2faa7..d0ac9d6f79862 100644 --- a/src/legacy/ui/public/styles/_legacy/components/_table.scss +++ b/src/legacy/ui/public/styles/_legacy/components/_table.scss @@ -1,4 +1,4 @@ -@import '../../../../../core_plugins/kibana/public/discover/np_ready/mixins'; +@import '../../../../../../plugins/discover/public/application/mixins'; .table { // Nesting diff --git a/src/legacy/ui/public/url/kibana_parsed_url.ts b/src/legacy/ui/public/url/kibana_parsed_url.ts index 93d2e17d6038f..22288160acc6d 100644 --- a/src/legacy/ui/public/url/kibana_parsed_url.ts +++ b/src/legacy/ui/public/url/kibana_parsed_url.ts @@ -19,7 +19,7 @@ import { parse } from 'url'; -import { modifyUrl } from '../../../../core/utils'; +import { modifyUrl } from '../../../../core/public'; import { prependPath } from './prepend_path'; interface Options { diff --git a/src/optimize/bundles_route/dynamic_asset_response.ts b/src/optimize/bundles_route/dynamic_asset_response.ts index a020c6935eeec..2f5395341abb1 100644 --- a/src/optimize/bundles_route/dynamic_asset_response.ts +++ b/src/optimize/bundles_route/dynamic_asset_response.ts @@ -21,6 +21,7 @@ import Fs from 'fs'; import { resolve } from 'path'; import { promisify } from 'util'; +import Accept from 'accept'; import Boom from 'boom'; import Hapi from 'hapi'; @@ -37,6 +38,41 @@ const asyncOpen = promisify(Fs.open); const asyncClose = promisify(Fs.close); const asyncFstat = promisify(Fs.fstat); +async function tryToOpenFile(filePath: string) { + try { + return await asyncOpen(filePath, 'r'); + } catch (e) { + if (e.code === 'ENOENT') { + return undefined; + } else { + throw e; + } + } +} + +async function selectCompressedFile(acceptEncodingHeader: string | undefined, path: string) { + let fd: number | undefined; + let fileEncoding: 'gzip' | 'br' | undefined; + + const supportedEncodings = Accept.encodings(acceptEncodingHeader, ['br', 'gzip']); + + if (supportedEncodings[0] === 'br') { + fileEncoding = 'br'; + fd = await tryToOpenFile(`${path}.br`); + } + if (!fd && supportedEncodings.includes('gzip')) { + fileEncoding = 'gzip'; + fd = await tryToOpenFile(`${path}.gz`); + } + if (!fd) { + fileEncoding = undefined; + // Use raw open to trigger exception if it does not exist + fd = await asyncOpen(path, 'r'); + } + + return { fd, fileEncoding }; +} + /** * Create a Hapi response for the requested path. This is designed * to replicate a subset of the features provided by Hapi's Inert @@ -74,6 +110,7 @@ export async function createDynamicAssetResponse({ isDist: boolean; }) { let fd: number | undefined; + let fileEncoding: 'gzip' | 'br' | undefined; try { const path = resolve(bundlesPath, request.params.path); @@ -86,7 +123,7 @@ export async function createDynamicAssetResponse({ // we use and manage a file descriptor mostly because // that's what Inert does, and since we are accessing // the file 2 or 3 times per request it seems logical - fd = await asyncOpen(path, 'r'); + ({ fd, fileEncoding } = await selectCompressedFile(request.headers['accept-encoding'], path)); const stat = await asyncFstat(fd); const hash = isDist ? undefined : await getFileHash(fileHashCache, path, stat, fd); @@ -113,6 +150,12 @@ export async function createDynamicAssetResponse({ response.header('cache-control', 'must-revalidate'); } + // If we manually selected a compressed file, specify the encoding header. + // Otherwise, let Hapi automatically gzip the response. + if (fileEncoding) { + response.header('content-encoding', fileEncoding); + } + return response; } catch (error) { if (fd) { diff --git a/src/plugins/advanced_settings/public/management_app/components/form/_form.scss b/src/plugins/advanced_settings/public/management_app/components/form/_form.scss index 02ebb90221d90..5fddaa178f580 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/_form.scss +++ b/src/plugins/advanced_settings/public/management_app/components/form/_form.scss @@ -1,6 +1,8 @@ @import '@elastic/eui/src/components/header/variables'; @import '@elastic/eui/src/components/nav_drawer/variables'; +// TODO #64541 +// Delete this whole file .mgtAdvancedSettingsForm__bottomBar { margin-left: $euiNavDrawerWidthCollapsed; z-index: 9; // Puts it inuder the nav drawer when expanded diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx index c859e8fdd7136..2cd4d3c0b43ec 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx @@ -19,6 +19,7 @@ import React, { PureComponent, Fragment } from 'react'; import classNames from 'classnames'; + import { EuiFlexGroup, EuiFlexItem, @@ -325,10 +326,18 @@ export class Form extends PureComponent { renderBottomBar = () => { const areChangesInvalid = this.areChangesInvalid(); - const bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', { - 'mgtAdvancedSettingsForm__bottomBar--pushForNav': - localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true', - }); + + // TODO #64541 + // Delete these classes + let bottomBarClasses = ''; + const pageNav = this.props.settings.general.find(setting => setting.name === 'pageNavigation'); + + if (pageNav?.value === 'legacy') { + bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', { + 'mgtAdvancedSettingsForm__bottomBar--pushForNav': + localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true', + }); + } return ( { unsubscribeResizer(); - mappings.clearSubscriptions(); + clearSubscriptions(); window.removeEventListener('hashchange', onHashChange); }; }, [saveCurrentTextObject, initialTextValue, history, setInputEditor, settingsService]); diff --git a/src/plugins/console/public/application/containers/settings.tsx b/src/plugins/console/public/application/containers/settings.tsx index e34cfcac8096b..81938a83435de 100644 --- a/src/plugins/console/public/application/containers/settings.tsx +++ b/src/plugins/console/public/application/containers/settings.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { AutocompleteOptions, DevToolsSettingsModal } from '../components'; // @ts-ignore -import mappings from '../../lib/mappings/mappings'; +import { retrieveAutoCompleteInfo } from '../../lib/mappings/mappings'; import { useServicesContext, useEditorActionContext } from '../contexts'; import { DevToolsSettings, Settings as SettingsService } from '../../services'; @@ -33,7 +33,7 @@ const getAutocompleteDiff = (newSettings: DevToolsSettings, prevSettings: DevToo }; const refreshAutocompleteSettings = (settings: SettingsService, selectedSettings: any) => { - mappings.retrieveAutoCompleteInfo(settings, selectedSettings); + retrieveAutoCompleteInfo(settings, selectedSettings); }; const fetchAutocompleteSettingsIfNeeded = ( @@ -61,10 +61,10 @@ const fetchAutocompleteSettingsIfNeeded = ( }, {} ); - mappings.retrieveAutoCompleteInfo(settings, changedSettings); + retrieveAutoCompleteInfo(settings, changedSettings); } else if (isPollingChanged && newSettings.polling) { // If the user has turned polling on, then we'll fetch all selected autocomplete settings. - mappings.retrieveAutoCompleteInfo(settings, settings.getAutocomplete()); + retrieveAutoCompleteInfo(settings, settings.getAutocomplete()); } } }; diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts index dde793d9b9691..f0ce61f1d3401 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts @@ -24,7 +24,7 @@ import { sendRequestToES } from './send_request_to_es'; import { track } from './track'; // @ts-ignore -import mappings from '../../../lib/mappings/mappings'; +import { retrieveAutoCompleteInfo } from '../../../lib/mappings/mappings'; export const useSendCurrentRequestToES = () => { const { @@ -73,7 +73,7 @@ export const useSendCurrentRequestToES = () => { // or templates may have changed, so we'll need to update this data. Assume that if // the user disables polling they're trying to optimize performance or otherwise // preserve resources, so they won't want this request sent either. - mappings.retrieveAutoCompleteInfo(settings, settings.getAutocomplete()); + retrieveAutoCompleteInfo(settings, settings.getAutocomplete()); } dispatch({ diff --git a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js index 5c86b0a1d2092..1db9ca7bc0a86 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js @@ -17,7 +17,7 @@ * under the License. */ import '../legacy_core_editor.test.mocks'; -const $ = require('jquery'); +import $ from 'jquery'; import RowParser from '../../../../lib/row_parser'; import ace from 'brace'; import { createReadOnlyAceEditor } from '../create_readonly'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js index d763db7ae5d79..77b4ba8cea6ff 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js @@ -19,20 +19,21 @@ import ace from 'brace'; import { workerModule } from './worker'; +import { ScriptMode } from './script'; const oop = ace.acequire('ace/lib/oop'); const TextMode = ace.acequire('ace/mode/text').Mode; -const ScriptMode = require('./script').ScriptMode; + const MatchingBraceOutdent = ace.acequire('ace/mode/matching_brace_outdent').MatchingBraceOutdent; const CstyleBehaviour = ace.acequire('ace/mode/behaviour/cstyle').CstyleBehaviour; const CStyleFoldMode = ace.acequire('ace/mode/folding/cstyle').FoldMode; const WorkerClient = ace.acequire('ace/worker/worker_client').WorkerClient; const AceTokenizer = ace.acequire('ace/tokenizer').Tokenizer; -const HighlightRules = require('./input_highlight_rules').InputHighlightRules; +import { InputHighlightRules } from './input_highlight_rules'; export function Mode() { - this.$tokenizer = new AceTokenizer(new HighlightRules().getRules()); + this.$tokenizer = new AceTokenizer(new InputHighlightRules().getRules()); this.$outdent = new MatchingBraceOutdent(); this.$behaviour = new CstyleBehaviour(); this.foldingRules = new CStyleFoldMode(); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js index 29f192f4ea858..1558cf0cb5554 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js @@ -17,7 +17,7 @@ * under the License. */ -const ace = require('brace'); +import ace from 'brace'; import { addXJsonToRules } from '../../../../../../es_ui_shared/public'; export function addEOL(tokens, reg, nextIfEOL, normalNext) { diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/output.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/output.js index 40e3128e396a3..5ad34532d1861 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/output.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/output.js @@ -18,11 +18,11 @@ */ import ace from 'brace'; -require('./output_highlight_rules'); + +import { OutputJsonHighlightRules } from './output_highlight_rules'; const oop = ace.acequire('ace/lib/oop'); const JSONMode = ace.acequire('ace/mode/json').Mode; -const HighlightRules = require('./output_highlight_rules').OutputJsonHighlightRules; const MatchingBraceOutdent = ace.acequire('ace/mode/matching_brace_outdent').MatchingBraceOutdent; const CstyleBehaviour = ace.acequire('ace/mode/behaviour/cstyle').CstyleBehaviour; const CStyleFoldMode = ace.acequire('ace/mode/folding/cstyle').FoldMode; @@ -30,7 +30,7 @@ ace.acequire('ace/worker/worker_client'); const AceTokenizer = ace.acequire('ace/tokenizer').Tokenizer; export function Mode() { - this.$tokenizer = new AceTokenizer(new HighlightRules().getRules()); + this.$tokenizer = new AceTokenizer(new OutputJsonHighlightRules().getRules()); this.$outdent = new MatchingBraceOutdent(); this.$behaviour = new CstyleBehaviour(); this.foldingRules = new CStyleFoldMode(); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js index c9d538ab6ceb1..448fd847aeacd 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js @@ -17,7 +17,7 @@ * under the License. */ -const ace = require('brace'); +import ace from 'brace'; import 'brace/mode/json'; import { addXJsonToRules } from '../../../../../../es_ui_shared/public'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js b/src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js index f79a171c65082..8a0eb9a03480b 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js @@ -18,7 +18,7 @@ */ /* eslint import/no-unresolved: 0 */ -const ace = require('brace'); +import ace from 'brace'; ace.define('ace/theme/sense-dark', ['require', 'exports', 'module'], function(require, exports) { exports.isDark = true; diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js index c5a0c2ebddf71..edd09885c1ad2 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js @@ -19,10 +19,10 @@ import '../sense_editor.test.mocks'; import { create } from '../create'; import _ from 'lodash'; -const $ = require('jquery'); +import $ from 'jquery'; -const kb = require('../../../../lib/kb/kb'); -const mappings = require('../../../../lib/mappings/mappings'); +import * as kb from '../../../../lib/kb/kb'; +import * as mappings from '../../../../lib/mappings/mappings'; describe('Integration', () => { let senseEditor; diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js index 34b4cad7fbb6b..219e6262ab346 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js @@ -23,7 +23,7 @@ import _ from 'lodash'; import { create } from '../create'; import { collapseLiteralStrings } from '../../../../../../es_ui_shared/public'; -const editorInput1 = require('./editor_input1.txt'); +import editorInput1 from './editor_input1.txt'; describe('Editor', () => { let input; diff --git a/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js index 0758a75695566..ebde8c39cffbc 100644 --- a/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js @@ -17,7 +17,7 @@ * under the License. */ -const _ = require('lodash'); +import _ from 'lodash'; import { URL_PATH_END_MARKER, UrlPatternMatcher, diff --git a/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js index 72fce53c4f1fe..286aefcd133a0 100644 --- a/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -const _ = require('lodash'); +import _ from 'lodash'; import { UrlParams } from '../../autocomplete/url_params'; import { populateContext } from '../../autocomplete/engine'; diff --git a/src/plugins/console/public/lib/autocomplete/body_completer.js b/src/plugins/console/public/lib/autocomplete/body_completer.js index 1aa315c50b9bf..9bb1f14a6266a 100644 --- a/src/plugins/console/public/lib/autocomplete/body_completer.js +++ b/src/plugins/console/public/lib/autocomplete/body_completer.js @@ -17,7 +17,7 @@ * under the License. */ -const _ = require('lodash'); +import _ from 'lodash'; import { WalkingState, walkTokenPath, wrapComponentWithDefaults } from './engine'; import { ConstantComponent, diff --git a/src/plugins/console/public/lib/autocomplete/components/field_autocomplete_component.js b/src/plugins/console/public/lib/autocomplete/components/field_autocomplete_component.js index 0574ffbcfc3a9..80f65406cf5d3 100644 --- a/src/plugins/console/public/lib/autocomplete/components/field_autocomplete_component.js +++ b/src/plugins/console/public/lib/autocomplete/components/field_autocomplete_component.js @@ -17,11 +17,11 @@ * under the License. */ import _ from 'lodash'; -import mappings from '../../mappings/mappings'; +import { getFields } from '../../mappings/mappings'; import { ListComponent } from './list_component'; function FieldGenerator(context) { - return _.map(mappings.getFields(context.indices, context.types), function(field) { + return _.map(getFields(context.indices, context.types), function(field) { return { name: field.name, meta: field.type }; }); } diff --git a/src/plugins/console/public/lib/autocomplete/components/index_autocomplete_component.js b/src/plugins/console/public/lib/autocomplete/components/index_autocomplete_component.js index 03d67c9e27ee8..ec6f24253e78d 100644 --- a/src/plugins/console/public/lib/autocomplete/components/index_autocomplete_component.js +++ b/src/plugins/console/public/lib/autocomplete/components/index_autocomplete_component.js @@ -17,14 +17,14 @@ * under the License. */ import _ from 'lodash'; -import mappings from '../../mappings/mappings'; +import { getIndices } from '../../mappings/mappings'; import { ListComponent } from './list_component'; function nonValidIndexType(token) { return !(token === '_all' || token[0] !== '_'); } export class IndexAutocompleteComponent extends ListComponent { constructor(name, parent, multiValued) { - super(name, mappings.getIndices, parent, multiValued); + super(name, getIndices, parent, multiValued); } validateTokens(tokens) { if (!this.multiValued && tokens.length > 1) { diff --git a/src/plugins/console/public/lib/autocomplete/components/template_autocomplete_component.js b/src/plugins/console/public/lib/autocomplete/components/template_autocomplete_component.js index cc62a2f9eeea6..14141980d493d 100644 --- a/src/plugins/console/public/lib/autocomplete/components/template_autocomplete_component.js +++ b/src/plugins/console/public/lib/autocomplete/components/template_autocomplete_component.js @@ -16,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import mappings from '../../mappings/mappings'; +import { getTemplates } from '../../mappings/mappings'; import { ListComponent } from './list_component'; export class TemplateAutocompleteComponent extends ListComponent { constructor(name, parent) { - super(name, mappings.getTemplates, parent, true, true); + super(name, getTemplates, parent, true, true); } getContextKey() { return 'template'; diff --git a/src/plugins/console/public/lib/autocomplete/components/type_autocomplete_component.js b/src/plugins/console/public/lib/autocomplete/components/type_autocomplete_component.js index 507817c1ed8c5..03d85eccaf385 100644 --- a/src/plugins/console/public/lib/autocomplete/components/type_autocomplete_component.js +++ b/src/plugins/console/public/lib/autocomplete/components/type_autocomplete_component.js @@ -18,9 +18,9 @@ */ import _ from 'lodash'; import { ListComponent } from './list_component'; -import mappings from '../../mappings/mappings'; +import { getTypes } from '../../mappings/mappings'; function TypeGenerator(context) { - return mappings.getTypes(context.indices); + return getTypes(context.indices); } function nonValidIndexType(token) { return !(token === '_all' || token[0] !== '_'); diff --git a/src/plugins/console/public/lib/autocomplete/components/username_autocomplete_component.js b/src/plugins/console/public/lib/autocomplete/components/username_autocomplete_component.js index 26b7bd5c48c99..14b77d4e70625 100644 --- a/src/plugins/console/public/lib/autocomplete/components/username_autocomplete_component.js +++ b/src/plugins/console/public/lib/autocomplete/components/username_autocomplete_component.js @@ -17,14 +17,14 @@ * under the License. */ import _ from 'lodash'; -import mappings from '../../mappings/mappings'; +import { getIndices } from '../../mappings/mappings'; import { ListComponent } from './list_component'; function nonValidUsernameType(token) { return token[0] === '_'; } export class UsernameAutocompleteComponent extends ListComponent { constructor(name, parent, multiValued) { - super(name, mappings.getIndices, parent, multiValued); + super(name, getIndices, parent, multiValued); } validateTokens(tokens) { if (!this.multiValued && tokens.length > 1) { diff --git a/src/plugins/console/public/lib/autocomplete/engine.js b/src/plugins/console/public/lib/autocomplete/engine.js index 7b64d91c95374..ae943a74ffb3a 100644 --- a/src/plugins/console/public/lib/autocomplete/engine.js +++ b/src/plugins/console/public/lib/autocomplete/engine.js @@ -17,7 +17,7 @@ * under the License. */ -const _ = require('lodash'); +import _ from 'lodash'; export function wrapComponentWithDefaults(component, defaults) { const originalGetTerms = component.getTerms; diff --git a/src/plugins/console/public/lib/autocomplete/url_params.js b/src/plugins/console/public/lib/autocomplete/url_params.js index 3f05b9ce85aab..0519a2daade87 100644 --- a/src/plugins/console/public/lib/autocomplete/url_params.js +++ b/src/plugins/console/public/lib/autocomplete/url_params.js @@ -17,7 +17,7 @@ * under the License. */ -const _ = require('lodash'); +import _ from 'lodash'; import { ConstantComponent, ListComponent, SharedComponent } from './components'; export class ParamComponent extends ConstantComponent { diff --git a/src/plugins/console/public/lib/curl_parsing/__tests__/curl_parsing.test.js b/src/plugins/console/public/lib/curl_parsing/__tests__/curl_parsing.test.js index 49a54eaefa9ef..6a8caebfc6874 100644 --- a/src/plugins/console/public/lib/curl_parsing/__tests__/curl_parsing.test.js +++ b/src/plugins/console/public/lib/curl_parsing/__tests__/curl_parsing.test.js @@ -17,15 +17,15 @@ * under the License. */ -const _ = require('lodash'); -const curl = require('../curl'); +import _ from 'lodash'; +import { detectCURL, parseCURL } from '../curl'; import curlTests from './curl_parsing.txt'; describe('CURL', () => { const notCURLS = ['sldhfsljfhs', 's;kdjfsldkfj curl -XDELETE ""', '{ "hello": 1 }']; _.each(notCURLS, function(notCURL, i) { test('cURL Detection - broken strings ' + i, function() { - expect(curl.detectCURL(notCURL)).toEqual(false); + expect(detectCURL(notCURL)).toEqual(false); }); }); @@ -39,8 +39,8 @@ describe('CURL', () => { const response = fixture[2].trim(); test('cURL Detection - ' + name, function() { - expect(curl.detectCURL(curlText)).toBe(true); - const r = curl.parseCURL(curlText); + expect(detectCURL(curlText)).toBe(true); + const r = parseCURL(curlText); expect(r).toEqual(response); }); }); diff --git a/src/plugins/console/public/lib/kb/__tests__/kb.test.js b/src/plugins/console/public/lib/kb/__tests__/kb.test.js index c2c69314a172d..c80f5671449b3 100644 --- a/src/plugins/console/public/lib/kb/__tests__/kb.test.js +++ b/src/plugins/console/public/lib/kb/__tests__/kb.test.js @@ -21,8 +21,8 @@ import _ from 'lodash'; import { populateContext } from '../../autocomplete/engine'; import '../../../application/models/sense_editor/sense_editor.test.mocks'; -const kb = require('../../kb'); -const mappings = require('../../mappings/mappings'); +import * as kb from '../../kb'; +import * as mappings from '../../mappings/mappings'; describe('Knowledge base', () => { beforeEach(() => { diff --git a/src/plugins/console/public/lib/kb/api.js b/src/plugins/console/public/lib/kb/api.js index eeec87060b770..c418a7cb414ef 100644 --- a/src/plugins/console/public/lib/kb/api.js +++ b/src/plugins/console/public/lib/kb/api.js @@ -17,7 +17,7 @@ * under the License. */ -const _ = require('lodash'); +import _ from 'lodash'; import { UrlPatternMatcher } from '../autocomplete/components'; import { UrlParams } from '../autocomplete/url_params'; import { diff --git a/src/plugins/console/public/lib/mappings/__tests__/mapping.test.js b/src/plugins/console/public/lib/mappings/__tests__/mapping.test.js index 27b3ce26b5588..292b1b4fb1bf5 100644 --- a/src/plugins/console/public/lib/mappings/__tests__/mapping.test.js +++ b/src/plugins/console/public/lib/mappings/__tests__/mapping.test.js @@ -17,7 +17,7 @@ * under the License. */ import '../../../application/models/sense_editor/sense_editor.test.mocks'; -const mappings = require('../mappings'); +import * as mappings from '../mappings'; describe('Mappings', () => { beforeEach(() => { diff --git a/src/plugins/console/public/lib/mappings/mappings.js b/src/plugins/console/public/lib/mappings/mappings.js index 330147118d42c..b5bcc2b105996 100644 --- a/src/plugins/console/public/lib/mappings/mappings.js +++ b/src/plugins/console/public/lib/mappings/mappings.js @@ -17,9 +17,9 @@ * under the License. */ -const $ = require('jquery'); -const _ = require('lodash'); -const es = require('../es/es'); +import $ from 'jquery'; +import _ from 'lodash'; +import * as es from '../es/es'; // NOTE: If this value ever changes to be a few seconds or less, it might introduce flakiness // due to timing issues in our app.js tests. @@ -32,7 +32,7 @@ let templates = []; const mappingObj = {}; -function expandAliases(indicesOrAliases) { +export function expandAliases(indicesOrAliases) { // takes a list of indices or aliases or a string which may be either and returns a list of indices // returns a list for multiple values or a string for a single. @@ -60,11 +60,11 @@ function expandAliases(indicesOrAliases) { return ret.length > 1 ? ret : ret[0]; } -function getTemplates() { +export function getTemplates() { return [...templates]; } -function getFields(indices, types) { +export function getFields(indices, types) { // get fields for indices and types. Both can be a list, a string or null (meaning all). let ret = []; indices = expandAliases(indices); @@ -103,7 +103,7 @@ function getFields(indices, types) { }); } -function getTypes(indices) { +export function getTypes(indices) { let ret = []; indices = expandAliases(indices); if (typeof indices === 'string') { @@ -129,7 +129,7 @@ function getTypes(indices) { return _.uniq(ret); } -function getIndices(includeAliases) { +export function getIndices(includeAliases) { const ret = []; $.each(perIndexTypes, function(index) { ret.push(index); @@ -200,7 +200,7 @@ function loadTemplates(templatesObject = {}) { templates = Object.keys(templatesObject); } -function loadMappings(mappings) { +export function loadMappings(mappings) { perIndexTypes = {}; $.each(mappings, function(index, indexMapping) { @@ -224,7 +224,7 @@ function loadMappings(mappings) { }); } -function loadAliases(aliases) { +export function loadAliases(aliases) { perAliasIndexes = {}; $.each(aliases || {}, function(index, omdexAliases) { // verify we have an index defined. useful when mapping loading is disabled @@ -246,7 +246,7 @@ function loadAliases(aliases) { perAliasIndexes._all = getIndices(false); } -function clear() { +export function clear() { perIndexTypes = {}; perAliasIndexes = {}; templates = []; @@ -285,7 +285,7 @@ function retrieveSettings(settingsKey, settingsToRetrieve) { // unchanged alone (both selected and unselected). // 3. Poll: Use saved. Fetch selected. Ignore unselected. -function clearSubscriptions() { +export function clearSubscriptions() { if (pollTimeoutId) { clearTimeout(pollTimeoutId); } @@ -296,7 +296,7 @@ function clearSubscriptions() { * @param settings Settings A way to retrieve the current settings * @param settingsToRetrieve any */ -function retrieveAutoCompleteInfo(settings, settingsToRetrieve) { +export function retrieveAutoCompleteInfo(settings, settingsToRetrieve) { clearSubscriptions(); const mappingPromise = retrieveSettings('fields', settingsToRetrieve); @@ -341,16 +341,3 @@ function retrieveAutoCompleteInfo(settings, settingsToRetrieve) { }, POLL_INTERVAL); }); } - -export default { - getFields, - getTemplates, - getIndices, - getTypes, - loadMappings, - loadAliases, - expandAliases, - clear, - retrieveAutoCompleteInfo, - clearSubscriptions, -}; diff --git a/src/plugins/console/public/lib/utils/__tests__/utils.test.js b/src/plugins/console/public/lib/utils/__tests__/utils.test.js index 6115be3c84ed9..3a2e6a54c1328 100644 --- a/src/plugins/console/public/lib/utils/__tests__/utils.test.js +++ b/src/plugins/console/public/lib/utils/__tests__/utils.test.js @@ -17,7 +17,7 @@ * under the License. */ -const utils = require('../'); +import * as utils from '../'; describe('Utils class', () => { test('extract deprecation messages', function() { diff --git a/src/plugins/console/server/lib/spec_definitions/js/aggregations.ts b/src/plugins/console/server/lib/spec_definitions/js/aggregations.ts index 1170c9edd2366..ce3155919bd1d 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/aggregations.ts +++ b/src/plugins/console/server/lib/spec_definitions/js/aggregations.ts @@ -148,7 +148,7 @@ const rules = { shard_size: 10, order: { __template: { - _term: 'asc', + _key: 'asc', }, _term: { __one_of: ['asc', 'desc'] }, _count: { __one_of: ['asc', 'desc'] }, diff --git a/src/plugins/dashboard/public/application/_dashboard_app.scss b/src/plugins/dashboard/public/application/_dashboard_app.scss index 8f389bb031df1..719d0a3268b5d 100644 --- a/src/plugins/dashboard/public/application/_dashboard_app.scss +++ b/src/plugins/dashboard/public/application/_dashboard_app.scss @@ -1,7 +1,8 @@ .dshAppContainer { display: flex; flex-direction: column; - height: 100%; + height: 100%; // TODO #64541 - can delete this + flex: 1; } .dshStartScreen { diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx index 4d15e7e899fa8..ff4e50ba8c327 100644 --- a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx +++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx @@ -39,7 +39,7 @@ export interface ClonePanelActionContext { export class ClonePanelAction implements ActionByType { public readonly type = ACTION_CLONE_PANEL; public readonly id = ACTION_CLONE_PANEL; - public order = 11; + public order = 45; constructor(private core: CoreStart) {} diff --git a/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx b/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx index ddc255295e89b..5526af2f83850 100644 --- a/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx +++ b/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx @@ -37,7 +37,7 @@ export interface ReplacePanelActionContext { export class ReplacePanelAction implements ActionByType { public readonly type = ACTION_REPLACE_PANEL; public readonly id = ACTION_REPLACE_PANEL; - public order = 11; + public order = 3; constructor( private core: CoreStart, diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 8346fd900caef..7e25d80c9d619 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -177,6 +177,7 @@ export class DashboardContainer extends Container []) as any, getEmbeddableFactories: start.getEmbeddableFactories, diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html b/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html index f473e91af7ae9..f57c10d1a48dd 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html @@ -8,4 +8,4 @@ listing-limit="listingLimit" hide-write-controls="hideWriteControls" initial-filter="initialFilter" -/> +> diff --git a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx index 5dab21ff671b4..6eb85faeea014 100644 --- a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx @@ -38,6 +38,7 @@ import { embeddablePluginMock } from '../../../../embeddable/public/mocks'; import { inspectorPluginMock } from '../../../../inspector/public/mocks'; import { KibanaContextProvider } from '../../../../kibana_react/public'; import { uiActionsPluginMock } from '../../../../ui_actions/public/mocks'; +import { applicationServiceMock } from '../../../../../core/public/mocks'; test('DashboardContainer in edit mode shows edit mode actions', async () => { const inspector = inspectorPluginMock.createStartContract(); @@ -46,7 +47,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { const editModeAction = createEditModeAction(); uiActionsSetup.registerAction(editModeAction); - uiActionsSetup.attachAction(CONTEXT_MENU_TRIGGER, editModeAction); + uiActionsSetup.addTriggerAction(CONTEXT_MENU_TRIGGER, editModeAction); setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, new ContactCardEmbeddableFactory((() => null) as any, {} as any) @@ -56,7 +57,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { const initialInput = getSampleDashboardInput({ viewMode: ViewMode.VIEW }); const options: DashboardContainerOptions = { - application: {} as any, + application: applicationServiceMock.createStartContract(), embeddable: start, notifications: {} as any, overlays: {} as any, @@ -84,7 +85,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { getAllEmbeddableFactories={(() => []) as any} getEmbeddableFactory={(() => null) as any} notifications={{} as any} - application={{} as any} + application={options.application} overlays={{} as any} inspector={inspector} SavedObjectFinder={() => null} diff --git a/src/plugins/dashboard/public/dashboard_constants.ts b/src/plugins/dashboard/public/dashboard_constants.ts index 0820ebd371004..490ddbed933d9 100644 --- a/src/plugins/dashboard/public/dashboard_constants.ts +++ b/src/plugins/dashboard/public/dashboard_constants.ts @@ -18,7 +18,6 @@ */ export const DashboardConstants = { - ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM: 'addToDashboard', LANDING_PAGE_PATH: '/dashboards', CREATE_NEW_DASHBOARD_URL: '/dashboard', ADD_EMBEDDABLE_ID: 'addEmbeddableId', diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 7de054f2eaa9c..b28822120b31e 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -136,7 +136,7 @@ export class DashboardPlugin ): Setup { const expandPanelAction = new ExpandPanelAction(); uiActions.registerAction(expandPanelAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction.id); const startServices = core.getStartServices(); if (share) { @@ -310,11 +310,11 @@ export class DashboardPlugin plugins.embeddable.getEmbeddableFactories ); uiActions.registerAction(changeViewAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction.id); const clonePanelAction = new ClonePanelAction(core); uiActions.registerAction(clonePanelAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction.id); const savedDashboardLoader = createSavedDashboardLoader({ savedObjectsClient: core.savedObjects.client, diff --git a/src/plugins/data/public/actions/apply_filter_action.ts b/src/plugins/data/public/actions/apply_filter_action.ts index bd20c6f632a3a..ebaac6b745bec 100644 --- a/src/plugins/data/public/actions/apply_filter_action.ts +++ b/src/plugins/data/public/actions/apply_filter_action.ts @@ -42,6 +42,7 @@ export function createFilterAction( return createAction({ type: ACTION_GLOBAL_APPLY_FILTER, id: ACTION_GLOBAL_APPLY_FILTER, + getIconType: () => 'filter', getDisplayName: () => { return i18n.translate('data.filter.applyFilterActionTitle', { defaultMessage: 'Apply filter to current view', diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 75deff23ce20d..d4433f3825fea 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -59,6 +59,7 @@ import { changeTimeFilter, mapAndFlattenFilters, extractTimeFilter, + convertRangeFilterToTimeRangeString, } from './query'; // Filter helpers namespace: @@ -96,6 +97,7 @@ export const esFilters = { onlyDisabledFiltersChanged, changeTimeFilter, + convertRangeFilterToTimeRangeString, mapAndFlattenFilters, extractTimeFilter, }; @@ -252,12 +254,12 @@ export const indexPatterns = { export { IndexPatternsContract, IndexPattern, + IIndexPatternFieldList, Field as IndexPatternField, TypeMeta as IndexPatternTypeMeta, AggregationRestrictions as IndexPatternAggRestrictions, // TODO: exported only in stub_index_pattern test. Move into data plugin and remove export. - FieldList as IndexPatternFieldList, - Field, + getIndexPatternFieldListCreator, } from './index_patterns'; export { diff --git a/src/plugins/data/public/index_patterns/fields/field.ts b/src/plugins/data/public/index_patterns/fields/field.ts index d83c0a7d3445e..12db09bbb846f 100644 --- a/src/plugins/data/public/index_patterns/fields/field.ts +++ b/src/plugins/data/public/index_patterns/fields/field.ts @@ -18,15 +18,26 @@ */ import { i18n } from '@kbn/i18n'; +import { ToastsStart } from 'kibana/public'; // @ts-ignore import { ObjDefine } from './obj_define'; import { IndexPattern } from '../index_patterns'; -import { getNotifications, getFieldFormats } from '../../services'; -import { IFieldType, getKbnFieldType, IFieldSubType, FieldFormat } from '../../../common'; -import { shortenDottedString } from '../../../common/utils'; +import { + IFieldType, + getKbnFieldType, + IFieldSubType, + FieldFormat, + shortenDottedString, +} from '../../../common'; +import { FieldFormatsStart } from '../../field_formats'; export type FieldSpec = Record; +interface FieldDependencies { + fieldFormats: FieldFormatsStart; + toastNotifications: ToastsStart; +} + export class Field implements IFieldType { name: string; type: string; @@ -52,7 +63,8 @@ export class Field implements IFieldType { constructor( indexPattern: IndexPattern, spec: FieldSpec | Field, - shortDotsEnable: boolean = false + shortDotsEnable: boolean, + { fieldFormats, toastNotifications }: FieldDependencies ) { // unwrap old instances of Field if (spec instanceof Field) spec = spec.$$spec; @@ -77,9 +89,8 @@ export class Field implements IFieldType { values: { name: spec.name, title: indexPattern.title }, defaultMessage: 'Field {name} in indexPattern {title} is using an unknown field type.', }); - const { toasts } = getNotifications(); - toasts.addDanger({ + toastNotifications.addDanger({ title, text, }); @@ -90,11 +101,9 @@ export class Field implements IFieldType { let format = spec.format; if (!FieldFormat.isInstanceOfFieldFormat(format)) { - const fieldFormatsService = getFieldFormats(); - format = indexPattern.fieldFormatMap[spec.name] || - fieldFormatsService.getDefaultInstance(spec.type, spec.esTypes); + fieldFormats.getDefaultInstance(spec.type, spec.esTypes); } const indexed = !!spec.indexed; diff --git a/src/plugins/data/public/index_patterns/fields/field_list.ts b/src/plugins/data/public/index_patterns/fields/field_list.ts index 9772370199b24..0631e00a1fb62 100644 --- a/src/plugins/data/public/index_patterns/fields/field_list.ts +++ b/src/plugins/data/public/index_patterns/fields/field_list.ts @@ -18,13 +18,20 @@ */ import { findIndex } from 'lodash'; +import { ToastsStart } from 'kibana/public'; import { IndexPattern } from '../index_patterns'; import { IFieldType } from '../../../common'; import { Field, FieldSpec } from './field'; +import { FieldFormatsStart } from '../../field_formats'; type FieldMap = Map; -export interface IFieldList extends Array { +interface FieldListDependencies { + fieldFormats: FieldFormatsStart; + toastNotifications: ToastsStart; +} + +export interface IIndexPatternFieldList extends Array { getByName(name: Field['name']): Field | undefined; getByType(type: Field['type']): Field[]; add(field: FieldSpec): void; @@ -32,51 +39,70 @@ export interface IFieldList extends Array { update(field: FieldSpec): void; } -export class FieldList extends Array implements IFieldList { - private byName: FieldMap = new Map(); - private groups: Map = new Map(); - private indexPattern: IndexPattern; - private shortDotsEnable: boolean; - private setByName = (field: Field) => this.byName.set(field.name, field); - private setByGroup = (field: Field) => { - if (typeof this.groups.get(field.type) === 'undefined') { - this.groups.set(field.type, new Map()); - } - this.groups.get(field.type)!.set(field.name, field); - }; - private removeByGroup = (field: IFieldType) => this.groups.get(field.type)!.delete(field.name); +export type CreateIndexPatternFieldList = ( + indexPattern: IndexPattern, + specs?: FieldSpec[], + shortDotsEnable?: boolean +) => IIndexPatternFieldList; - constructor(indexPattern: IndexPattern, specs: FieldSpec[] = [], shortDotsEnable = false) { - super(); - this.indexPattern = indexPattern; - this.shortDotsEnable = shortDotsEnable; +export const getIndexPatternFieldListCreator = ({ + fieldFormats, + toastNotifications, +}: FieldListDependencies): CreateIndexPatternFieldList => (...fieldListParams) => { + class FieldList extends Array implements IIndexPatternFieldList { + private byName: FieldMap = new Map(); + private groups: Map = new Map(); + private indexPattern: IndexPattern; + private shortDotsEnable: boolean; + private setByName = (field: Field) => this.byName.set(field.name, field); + private setByGroup = (field: Field) => { + if (typeof this.groups.get(field.type) === 'undefined') { + this.groups.set(field.type, new Map()); + } + this.groups.get(field.type)!.set(field.name, field); + }; + private removeByGroup = (field: IFieldType) => this.groups.get(field.type)!.delete(field.name); - specs.map(field => this.add(field)); - } + constructor(indexPattern: IndexPattern, specs: FieldSpec[] = [], shortDotsEnable = false) { + super(); + this.indexPattern = indexPattern; + this.shortDotsEnable = shortDotsEnable; - getByName = (name: Field['name']) => this.byName.get(name); - getByType = (type: Field['type']) => [...(this.groups.get(type) || new Map()).values()]; - add = (field: FieldSpec) => { - const newField = new Field(this.indexPattern, field, this.shortDotsEnable); - this.push(newField); - this.setByName(newField); - this.setByGroup(newField); - }; + specs.map(field => this.add(field)); + } - remove = (field: IFieldType) => { - this.removeByGroup(field); - this.byName.delete(field.name); + getByName = (name: Field['name']) => this.byName.get(name); + getByType = (type: Field['type']) => [...(this.groups.get(type) || new Map()).values()]; + add = (field: FieldSpec) => { + const newField = new Field(this.indexPattern, field, this.shortDotsEnable, { + fieldFormats, + toastNotifications, + }); + this.push(newField); + this.setByName(newField); + this.setByGroup(newField); + }; - const fieldIndex = findIndex(this, { name: field.name }); - this.splice(fieldIndex, 1); - }; + remove = (field: IFieldType) => { + this.removeByGroup(field); + this.byName.delete(field.name); - update = (field: FieldSpec) => { - const newField = new Field(this.indexPattern, field, this.shortDotsEnable); - const index = this.findIndex(f => f.name === newField.name); - this.splice(index, 1, newField); - this.setByName(newField); - this.removeByGroup(newField); - this.setByGroup(newField); - }; -} + const fieldIndex = findIndex(this, { name: field.name }); + this.splice(fieldIndex, 1); + }; + + update = (field: FieldSpec) => { + const newField = new Field(this.indexPattern, field, this.shortDotsEnable, { + fieldFormats, + toastNotifications, + }); + const index = this.findIndex(f => f.name === newField.name); + this.splice(index, 1, newField); + this.setByName(newField); + this.removeByGroup(newField); + this.setByGroup(newField); + }; + } + + return new FieldList(...fieldListParams); +}; diff --git a/src/plugins/data/public/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index.ts index dcf799184b01c..e05db0e4d4cec 100644 --- a/src/plugins/data/public/index_patterns/index.ts +++ b/src/plugins/data/public/index_patterns/index.ts @@ -29,7 +29,7 @@ export { export { getRoutes } from './utils'; export { flattenHitWrapper, formatHitProvider } from './index_patterns'; -export { Field, FieldList } from './fields'; +export { getIndexPatternFieldListCreator, Field, IIndexPatternFieldList } from './fields'; // TODO: figure out how to replace IndexPatterns in get_inner_angular. export { diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts index 768029136879d..f39be78433710 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts @@ -32,7 +32,7 @@ import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../. import { findByTitle, getRoutes } from '../utils'; import { IndexPatternMissingIndices } from '../lib'; -import { Field, FieldList, IFieldList } from '../fields'; +import { Field, IIndexPatternFieldList, getIndexPatternFieldListCreator } from '../fields'; import { createFieldsFetcher } from './_fields_fetcher'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; @@ -51,7 +51,7 @@ export class IndexPattern implements IIndexPattern { public type?: string; public fieldFormatMap: any; public typeMeta?: TypeMeta; - public fields: IFieldList; + public fields: IIndexPatternFieldList; public timeFieldName: string | undefined; public formatHit: any; public formatField: any; @@ -106,7 +106,12 @@ export class IndexPattern implements IIndexPattern { this.shortDotsEnable = this.getConfig('shortDots:enable'); this.metaFields = this.getConfig('metaFields'); - this.fields = new FieldList(this, [], this.shortDotsEnable); + this.createFieldList = getIndexPatternFieldListCreator({ + fieldFormats: getFieldFormats(), + toastNotifications: getNotifications().toasts, + }); + + this.fields = this.createFieldList(this, [], this.shortDotsEnable); this.fieldsFetcher = createFieldsFetcher(this, apiClient, this.getConfig('metaFields')); this.flattenHit = flattenHitWrapper(this, this.getConfig('metaFields')); this.formatHit = formatHitProvider( @@ -131,7 +136,7 @@ export class IndexPattern implements IIndexPattern { private initFields(input?: any) { const newValue = input || this.fields; - this.fields = new FieldList(this, newValue, this.shortDotsEnable); + this.fields = this.createFieldList(this, newValue, this.shortDotsEnable); } private isFieldRefreshRequired(): boolean { @@ -281,7 +286,11 @@ export class IndexPattern implements IIndexPattern { filterable: true, searchable: true, }, - false + false, + { + fieldFormats: getFieldFormats(), + toastNotifications: getNotifications().toasts, + } ) ); diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts index cf1f83d0e28cb..fc0be270e9c50 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts @@ -19,12 +19,13 @@ // eslint-disable-next-line max-classes-per-file import { IndexPatternsService } from './index_patterns'; -import { - SavedObjectsClientContract, - HttpSetup, - SavedObjectsFindResponsePublic, - CoreStart, -} from 'kibana/public'; +import { SavedObjectsClientContract, SavedObjectsFindResponsePublic } from 'kibana/public'; +import { coreMock, httpServiceMock } from '../../../../../core/public/mocks'; +import { fieldFormatsServiceMock } from '../../field_formats/mocks'; + +const core = coreMock.createStart(); +const http = httpServiceMock.createStartContract(); +const fieldFormats = fieldFormatsServiceMock.createStartContract(); jest.mock('./index_pattern', () => { class IndexPattern { @@ -61,10 +62,7 @@ describe('IndexPatterns', () => { }) as Promise> ); - const core = {} as CoreStart; - const http = {} as HttpSetup; - - indexPatterns = new IndexPatternsService(core, savedObjectsClient, http); + indexPatterns = new IndexPatternsService(core, savedObjectsClient, http, fieldFormats); }); test('does cache gets for the same id', async () => { diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts index b5d66a6aab60a..515f2e7cf95ee 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts @@ -32,28 +32,60 @@ import { createEnsureDefaultIndexPattern, EnsureDefaultIndexPattern, } from './ensure_default_index_pattern'; +import { + getIndexPatternFieldListCreator, + CreateIndexPatternFieldList, + Field, + FieldSpec, +} from '../fields'; +import { FieldFormatsStart } from '../../field_formats'; const indexPatternCache = createIndexPatternCache(); type IndexPatternCachedFieldType = 'id' | 'title'; +export interface IndexPatternSavedObjectAttrs { + title: string; +} + export class IndexPatternsService { private config: IUiSettingsClient; private savedObjectsClient: SavedObjectsClientContract; - private savedObjectsCache?: Array>> | null; + private savedObjectsCache?: Array> | null; private apiClient: IndexPatternsApiClient; ensureDefaultIndexPattern: EnsureDefaultIndexPattern; - - constructor(core: CoreStart, savedObjectsClient: SavedObjectsClientContract, http: HttpStart) { + createFieldList: CreateIndexPatternFieldList; + createField: ( + indexPattern: IndexPattern, + spec: FieldSpec | Field, + shortDotsEnable: boolean + ) => Field; + + constructor( + core: CoreStart, + savedObjectsClient: SavedObjectsClientContract, + http: HttpStart, + fieldFormats: FieldFormatsStart + ) { this.apiClient = new IndexPatternsApiClient(http); this.config = core.uiSettings; this.savedObjectsClient = savedObjectsClient; this.ensureDefaultIndexPattern = createEnsureDefaultIndexPattern(core); + this.createFieldList = getIndexPatternFieldListCreator({ + fieldFormats, + toastNotifications: core.notifications.toasts, + }); + this.createField = (indexPattern, spec, shortDotsEnable) => { + return new Field(indexPattern, spec, shortDotsEnable, { + fieldFormats, + toastNotifications: core.notifications.toasts, + }); + }; } private async refreshSavedObjectsCache() { this.savedObjectsCache = ( - await this.savedObjectsClient.find>({ + await this.savedObjectsClient.find({ type: 'index-pattern', fields: ['title'], perPage: 10000, diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index ba1df89c41358..7307c93139d59 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -57,6 +57,8 @@ const createStartContract = (): Start => { SearchBar: jest.fn(), }, indexPatterns: ({ + createField: jest.fn(() => {}), + createFieldList: jest.fn(() => []), ensureDefaultIndexPattern: jest.fn(), make: () => ({ fieldsFetcher: { diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index f3a88287313a0..08796216db1e0 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -126,12 +126,12 @@ export class DataPublicPlugin implements Plugin boolean; changeTimeFilter: typeof changeTimeFilter; + convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString; mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[]; extractTimeFilter: typeof extractTimeFilter; }; @@ -428,55 +430,6 @@ export interface FetchOptions { searchStrategyId?: string; } -// Warning: (ae-missing-release-tag) "Field" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -class Field implements IFieldType { - // Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts - // - // (undocumented) - $$spec: FieldSpec; - constructor(indexPattern: IndexPattern, spec: FieldSpec | Field, shortDotsEnable?: boolean); - // (undocumented) - aggregatable?: boolean; - // (undocumented) - conflictDescriptions?: Record; - // (undocumented) - count?: number; - // (undocumented) - displayName?: string; - // (undocumented) - esTypes?: string[]; - // (undocumented) - filterable?: boolean; - // (undocumented) - format: any; - // (undocumented) - indexPattern?: IndexPattern; - // (undocumented) - lang?: string; - // (undocumented) - name: string; - // (undocumented) - script?: string; - // (undocumented) - scripted?: boolean; - // (undocumented) - searchable?: boolean; - // (undocumented) - sortable?: boolean; - // (undocumented) - subType?: IFieldSubType; - // (undocumented) - type: string; - // (undocumented) - visualizable?: boolean; -} - -export { Field } - -export { Field as IndexPatternField } - // Warning: (ae-missing-release-tag) "FieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -657,6 +610,13 @@ export function getDefaultQuery(language?: QueryLanguage): { // @public (undocumented) export function getEsPreference(uiSettings: IUiSettingsClient_2, sessionId?: string): any; +// Warning: (ae-forgotten-export) The symbol "FieldListDependencies" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "CreateIndexPatternFieldList" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "getIndexPatternFieldListCreator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const getIndexPatternFieldListCreator: ({ fieldFormats, toastNotifications, }: FieldListDependencies) => CreateIndexPatternFieldList; + // Warning: (ae-missing-release-tag) "getKbnTypeNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -828,6 +788,24 @@ export interface IIndexPattern { type?: string; } +// Warning: (ae-missing-release-tag) "IIndexPatternFieldList" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IIndexPatternFieldList extends Array { + // Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts + // + // (undocumented) + add(field: FieldSpec): void; + // (undocumented) + getByName(name: IndexPatternField['name']): IndexPatternField | undefined; + // (undocumented) + getByType(type: IndexPatternField['type']): IndexPatternField[]; + // (undocumented) + remove(field: IFieldType): void; + // (undocumented) + update(field: FieldSpec): void; +} + // Warning: (ae-missing-release-tag) "IKibanaSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -869,10 +847,8 @@ export class IndexPattern implements IIndexPattern { _fetchFields(): Promise; // (undocumented) fieldFormatMap: any; - // Warning: (ae-forgotten-export) The symbol "IFieldList" needs to be exported by the entry point index.d.ts - // // (undocumented) - fields: IFieldList; + fields: IIndexPatternFieldList; // (undocumented) fieldsFetcher: any; // (undocumented) @@ -900,17 +876,17 @@ export class IndexPattern implements IIndexPattern { }[]; }; // (undocumented) - getFieldByName(name: string): Field | void; + getFieldByName(name: string): IndexPatternField | void; // (undocumented) - getNonScriptedFields(): Field[]; + getNonScriptedFields(): IndexPatternField[]; // (undocumented) - getScriptedFields(): Field[]; + getScriptedFields(): IndexPatternField[]; // (undocumented) getSourceFiltering(): { excludes: any[]; }; // (undocumented) - getTimeField(): Field | undefined; + getTimeField(): IndexPatternField | undefined; // (undocumented) id?: string; // (undocumented) @@ -987,21 +963,48 @@ export interface IndexPatternAttributes { typeMeta: string; } -// Warning: (ae-missing-release-tag) "FieldList" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "Field" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class IndexPatternFieldList extends Array implements IFieldList { - constructor(indexPattern: IndexPattern, specs?: FieldSpec[], shortDotsEnable?: boolean); +export class IndexPatternField implements IFieldType { + // (undocumented) + $$spec: FieldSpec; + // Warning: (ae-forgotten-export) The symbol "FieldDependencies" needs to be exported by the entry point index.d.ts + constructor(indexPattern: IndexPattern, spec: FieldSpec | IndexPatternField, shortDotsEnable: boolean, { fieldFormats, toastNotifications }: FieldDependencies); + // (undocumented) + aggregatable?: boolean; + // (undocumented) + conflictDescriptions?: Record; + // (undocumented) + count?: number; // (undocumented) - add: (field: Record) => void; + displayName?: string; + // (undocumented) + esTypes?: string[]; + // (undocumented) + filterable?: boolean; + // (undocumented) + format: any; + // (undocumented) + indexPattern?: IndexPattern; // (undocumented) - getByName: (name: string) => Field | undefined; + lang?: string; // (undocumented) - getByType: (type: string) => any[]; + name: string; // (undocumented) - remove: (field: IFieldType) => void; + script?: string; // (undocumented) - update: (field: Record) => void; + scripted?: boolean; + // (undocumented) + searchable?: boolean; + // (undocumented) + sortable?: boolean; + // (undocumented) + subType?: IFieldSubType; + // (undocumented) + type: string; + // (undocumented) + visualizable?: boolean; } // Warning: (ae-missing-release-tag) "indexPatterns" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1351,7 +1354,7 @@ export interface QueryState { // Warning: (ae-missing-release-tag) "QueryStringInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const QueryStringInput: React.FC>; +export const QueryStringInput: React.FC>; // @public (undocumented) export type QuerySuggestion = QuerySuggestionBasic | QuerySuggestionField; @@ -1563,8 +1566,8 @@ export const search: { // Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SearchBar: React.ComponentClass, "query" | "isLoading" | "filters" | "indexPatterns" | "refreshInterval" | "screenTitle" | "dataTestSubj" | "customSubmitButton" | "showQueryBar" | "showQueryInput" | "showFilterBar" | "showDatePicker" | "showAutoRefreshOnly" | "isRefreshPaused" | "dateRangeFrom" | "dateRangeTo" | "showSaveQuery" | "savedQuery" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated" | "onClearSavedQuery" | "onRefresh" | "timeHistory" | "onFiltersUpdated" | "onRefreshChange">, any> & { - WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; +export const SearchBar: React.ComponentClass, "query" | "isLoading" | "filters" | "indexPatterns" | "refreshInterval" | "customSubmitButton" | "screenTitle" | "dataTestSubj" | "showQueryBar" | "showQueryInput" | "showFilterBar" | "showDatePicker" | "showAutoRefreshOnly" | "isRefreshPaused" | "dateRangeFrom" | "dateRangeTo" | "showSaveQuery" | "savedQuery" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated" | "onClearSavedQuery" | "onRefresh" | "timeHistory" | "onFiltersUpdated" | "onRefreshChange">, any> & { + WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; }; // Warning: (ae-forgotten-export) The symbol "SearchBarOwnProps" needs to be exported by the entry point index.d.ts @@ -1783,52 +1786,53 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/common/es_query/filters/match_all_filter.ts:28:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:135:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:135:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:135:21 - (ae-forgotten-export) The symbol "luceneStringToDsl" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:135:21 - (ae-forgotten-export) The symbol "decorateQuery" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "DateNanosFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:377:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:378:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:137:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:137:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:137:21 - (ae-forgotten-export) The symbol "luceneStringToDsl" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:137:21 - (ae-forgotten-export) The symbol "decorateQuery" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "DateNanosFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:379:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:380:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/query/timefilter/index.ts b/src/plugins/data/public/query/timefilter/index.ts index 034af03842ab8..a5885a59f60ed 100644 --- a/src/plugins/data/public/query/timefilter/index.ts +++ b/src/plugins/data/public/query/timefilter/index.ts @@ -23,5 +23,5 @@ export * from './types'; export { Timefilter, TimefilterContract } from './timefilter'; export { TimeHistory, TimeHistoryContract } from './time_history'; export { getTime, calculateBounds } from './get_time'; -export { changeTimeFilter } from './lib/change_time_filter'; +export { changeTimeFilter, convertRangeFilterToTimeRangeString } from './lib/change_time_filter'; export { extractTimeFilter } from './lib/extract_time_filter'; diff --git a/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts b/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts index 8da83580ef5d6..cbbf2f2754312 100644 --- a/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts +++ b/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts @@ -20,7 +20,7 @@ import moment from 'moment'; import { keys } from 'lodash'; import { TimefilterContract } from '../../timefilter'; -import { RangeFilter } from '../../../../common'; +import { RangeFilter, TimeRange } from '../../../../common'; export function convertRangeFilterToTimeRange(filter: RangeFilter) { const key = keys(filter.range)[0]; @@ -32,6 +32,14 @@ export function convertRangeFilterToTimeRange(filter: RangeFilter) { }; } +export function convertRangeFilterToTimeRangeString(filter: RangeFilter): TimeRange { + const { from, to } = convertRangeFilterToTimeRange(filter); + return { + from: from?.toISOString(), + to: to?.toISOString(), + }; +} + export function changeTimeFilter(timeFilter: TimefilterContract, filter: RangeFilter) { timeFilter.setTime(convertRangeFilterToTimeRange(filter)); } diff --git a/src/plugins/data/public/search/aggs/agg_config.ts b/src/plugins/data/public/search/aggs/agg_config.ts index 973c69e3d4f5f..86a2c3e0e82e4 100644 --- a/src/plugins/data/public/search/aggs/agg_config.ts +++ b/src/plugins/data/public/search/aggs/agg_config.ts @@ -19,7 +19,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { Assign } from '@kbn/utility-types'; +import { Assign, Ensure } from '@kbn/utility-types'; import { ExpressionAstFunction, ExpressionAstArgument } from 'src/plugins/expressions/public'; import { IAggType } from './agg_type'; import { writeParams } from './agg_params'; @@ -31,17 +31,22 @@ import { FieldFormatsStart } from '../../field_formats'; type State = string | number | boolean | null | undefined | SerializableState; -interface SerializableState { +/** @internal **/ +export interface SerializableState { [key: string]: State | State[]; } -export interface AggConfigSerialized { - type: string; - enabled?: boolean; - id?: string; - params?: SerializableState; - schema?: string; -} +/** @internal **/ +export type AggConfigSerialized = Ensure< + { + type: string; + enabled?: boolean; + id?: string; + params?: SerializableState; + schema?: string; + }, + SerializableState +>; export interface AggConfigDependencies { fieldFormats: FieldFormatsStart; diff --git a/src/plugins/data/public/search/aggs/agg_types.ts b/src/plugins/data/public/search/aggs/agg_types.ts index da07f581c9274..2af29d3600246 100644 --- a/src/plugins/data/public/search/aggs/agg_types.ts +++ b/src/plugins/data/public/search/aggs/agg_types.ts @@ -105,6 +105,73 @@ export const getAggTypes = ({ ], }); +/** Buckets: **/ +import { aggFilter } from './buckets/filter_fn'; +import { aggFilters } from './buckets/filters_fn'; +import { aggSignificantTerms } from './buckets/significant_terms_fn'; +import { aggIpRange } from './buckets/ip_range_fn'; +import { aggDateRange } from './buckets/date_range_fn'; +import { aggRange } from './buckets/range_fn'; +import { aggGeoTile } from './buckets/geo_tile_fn'; +import { aggGeoHash } from './buckets/geo_hash_fn'; +import { aggHistogram } from './buckets/histogram_fn'; +import { aggDateHistogram } from './buckets/date_histogram_fn'; import { aggTerms } from './buckets/terms_fn'; -export const getAggTypesFunctions = () => [aggTerms]; +/** Metrics: **/ +import { aggAvg } from './metrics/avg_fn'; +import { aggBucketAvg } from './metrics/bucket_avg_fn'; +import { aggBucketMax } from './metrics/bucket_max_fn'; +import { aggBucketMin } from './metrics/bucket_min_fn'; +import { aggBucketSum } from './metrics/bucket_sum_fn'; +import { aggCardinality } from './metrics/cardinality_fn'; +import { aggCount } from './metrics/count_fn'; +import { aggCumulativeSum } from './metrics/cumulative_sum_fn'; +import { aggDerivative } from './metrics/derivative_fn'; +import { aggGeoBounds } from './metrics/geo_bounds_fn'; +import { aggGeoCentroid } from './metrics/geo_centroid_fn'; +import { aggMax } from './metrics/max_fn'; +import { aggMedian } from './metrics/median_fn'; +import { aggMin } from './metrics/min_fn'; +import { aggMovingAvg } from './metrics/moving_avg_fn'; +import { aggPercentileRanks } from './metrics/percentile_ranks_fn'; +import { aggPercentiles } from './metrics/percentiles_fn'; +import { aggSerialDiff } from './metrics/serial_diff_fn'; +import { aggStdDeviation } from './metrics/std_deviation_fn'; +import { aggSum } from './metrics/sum_fn'; +import { aggTopHit } from './metrics/top_hit_fn'; + +export const getAggTypesFunctions = () => [ + aggAvg, + aggBucketAvg, + aggBucketMax, + aggBucketMin, + aggBucketSum, + aggCardinality, + aggCount, + aggCumulativeSum, + aggDerivative, + aggGeoBounds, + aggGeoCentroid, + aggMax, + aggMedian, + aggMin, + aggMovingAvg, + aggPercentileRanks, + aggPercentiles, + aggSerialDiff, + aggStdDeviation, + aggSum, + aggTopHit, + aggFilter, + aggFilters, + aggSignificantTerms, + aggIpRange, + aggDateRange, + aggRange, + aggGeoTile, + aggGeoHash, + aggDateHistogram, + aggHistogram, + aggTerms, +]; diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts index 3ecdc17cb57f3..219bb5440c8da 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts @@ -27,7 +27,7 @@ import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { createFilterDateHistogram } from './create_filter/date_histogram'; import { intervalOptions } from './_interval_options'; -import { dateHistogramInterval } from '../../../../common'; +import { dateHistogramInterval, TimeRange } from '../../../../common'; import { writeParams } from '../agg_params'; import { isMetricAggType } from '../metrics/metric_agg_type'; @@ -35,6 +35,8 @@ import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../common'; import { TimefilterContract } from '../../../query'; import { QuerySetup } from '../../../query/query_service'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; +import { ExtendedBounds } from './lib/extended_bounds'; const detectedTimezone = moment.tz.guess(); const tzOffset = moment().format('Z'); @@ -67,6 +69,19 @@ export function isDateHistogramBucketAggConfig(agg: any): agg is IBucketDateHist return Boolean(agg.buckets); } +export interface AggParamsDateHistogram extends BaseAggParams { + field?: string; + timeRange?: TimeRange; + useNormalizedEsInterval?: boolean; + scaleMetricValues?: boolean; + interval?: string; + time_zone?: string; + drop_partials?: boolean; + format?: string; + min_doc_count?: number; + extended_bounds?: ExtendedBounds; +} + export const getDateHistogramBucketAgg = ({ uiSettings, query, @@ -89,6 +104,7 @@ export const getDateHistogramBucketAgg = ({ } const field = agg.getFieldDisplayName(); + return i18n.translate('data.search.aggs.buckets.dateHistogramLabel', { defaultMessage: '{fieldName} per {intervalDescription}', values: { diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.test.ts new file mode 100644 index 0000000000000..bd3c4f8dd58cf --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.test.ts @@ -0,0 +1,120 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggDateHistogram } from './date_histogram_fn'; + +describe('agg_expression_functions', () => { + describe('aggDateHistogram', () => { + const fn = functionWrapper(aggDateHistogram()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({}); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "drop_partials": undefined, + "extended_bounds": undefined, + "field": undefined, + "format": undefined, + "interval": undefined, + "json": undefined, + "min_doc_count": undefined, + "scaleMetricValues": undefined, + "timeRange": undefined, + "time_zone": undefined, + "useNormalizedEsInterval": undefined, + }, + "schema": undefined, + "type": "date_histogram", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'field', + timeRange: JSON.stringify({ + from: 'from', + to: 'to', + }), + useNormalizedEsInterval: true, + scaleMetricValues: true, + interval: 'interval', + time_zone: 'time_zone', + drop_partials: false, + format: 'format', + min_doc_count: 1, + extended_bounds: JSON.stringify({ + min: 1, + max: 2, + }), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "drop_partials": false, + "extended_bounds": Object { + "max": 2, + "min": 1, + }, + "field": "field", + "format": "format", + "interval": "interval", + "json": undefined, + "min_doc_count": 1, + "scaleMetricValues": true, + "timeRange": Object { + "from": "from", + "to": "to", + }, + "time_zone": "time_zone", + "useNormalizedEsInterval": true, + }, + "schema": undefined, + "type": "date_histogram", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.ts b/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.ts new file mode 100644 index 0000000000000..033b44da0880f --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.ts @@ -0,0 +1,155 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggDateHistogram'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggDateHistogram = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.dateHistogram.help', { + defaultMessage: 'Generates a serialized agg config for a Histogram agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.dateHistogram.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + useNormalizedEsInterval: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.useNormalizedEsInterval.help', { + defaultMessage: 'Specifies whether to use useNormalizedEsInterval for this aggregation', + }), + }, + time_zone: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.timeZone.help', { + defaultMessage: 'Time zone to use for this aggregation', + }), + }, + format: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.format.help', { + defaultMessage: 'Format to use for this aggregation', + }), + }, + scaleMetricValues: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.scaleMetricValues.help', { + defaultMessage: 'Specifies whether to use scaleMetricValues for this aggregation', + }), + }, + interval: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.interval.help', { + defaultMessage: 'Interval to use for this aggregation', + }), + }, + timeRange: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.timeRange.help', { + defaultMessage: 'Time Range to use for this aggregation', + }), + }, + min_doc_count: { + types: ['number'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.minDocCount.help', { + defaultMessage: 'Minimum document count to use for this aggregation', + }), + }, + drop_partials: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.dropPartials.help', { + defaultMessage: 'Specifies whether to use drop_partials for this aggregation', + }), + }, + extended_bounds: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.extendedBounds.help', { + defaultMessage: + 'With extended_bounds setting, you now can "force" the histogram aggregation to start building buckets on a specific min value and also keep on building buckets up to a max value ', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateHistogram.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.DATE_HISTOGRAM, + params: { + ...rest, + timeRange: getParsedValue(args, 'timeRange'), + extended_bounds: getParsedValue(args, 'extended_bounds'), + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.ts b/src/plugins/data/public/search/aggs/buckets/date_range.ts index 07d927e64a943..504958854cad4 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_range.ts @@ -29,6 +29,7 @@ import { convertDateRangeToString, DateRangeKey } from './lib/date_range'; import { KBN_FIELD_TYPES, FieldFormat, TEXT_CONTEXT_TYPE } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle', { defaultMessage: 'Date Range', @@ -39,6 +40,12 @@ export interface DateRangeBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsDateRange extends BaseAggParams { + field?: string; + ranges?: DateRangeKey[]; + time_zone?: string; +} + export const getDateRangeBucketAgg = ({ uiSettings, getInternalStartServices, diff --git a/src/plugins/data/public/search/aggs/buckets/date_range_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/date_range_fn.test.ts new file mode 100644 index 0000000000000..93bb791874e67 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/date_range_fn.test.ts @@ -0,0 +1,101 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggDateRange } from './date_range_fn'; + +describe('agg_expression_functions', () => { + describe('aggDateRange', () => { + const fn = functionWrapper(aggDateRange()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({}); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": undefined, + "json": undefined, + "ranges": undefined, + "time_zone": undefined, + }, + "schema": undefined, + "type": "date_range", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'date_field', + time_zone: 'UTC +3', + ranges: JSON.stringify([ + { from: 'now-1w/w', to: 'now' }, + { from: 1588163532470, to: 1588163532481 }, + ]), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "date_field", + "json": undefined, + "ranges": Array [ + Object { + "from": "now-1w/w", + "to": "now", + }, + Object { + "from": 1588163532470, + "to": 1588163532481, + }, + ], + "time_zone": "UTC +3", + }, + "schema": undefined, + "type": "date_range", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'date_field', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + field: 'date_field', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/date_range_fn.ts b/src/plugins/data/public/search/aggs/buckets/date_range_fn.ts new file mode 100644 index 0000000000000..1fe42ce63d815 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/date_range_fn.ts @@ -0,0 +1,111 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggDateRange'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggDateRange = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.dateRange.help', { + defaultMessage: 'Generates a serialized agg config for a Date Range agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateRange.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.dateRange.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateRange.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateRange.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + ranges: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateRange.ranges.help', { + defaultMessage: 'Serialized ranges to use for this aggregation.', + }), + }, + time_zone: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateRange.timeZone.help', { + defaultMessage: 'Time zone to use for this aggregation.', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateRange.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.dateRange.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.DATE_RANGE, + params: { + ...rest, + json: getParsedValue(args, 'json'), + ranges: getParsedValue(args, 'ranges'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/filter.ts b/src/plugins/data/public/search/aggs/buckets/filter.ts index accbdf4dd783d..69157edad4f68 100644 --- a/src/plugins/data/public/search/aggs/buckets/filter.ts +++ b/src/plugins/data/public/search/aggs/buckets/filter.ts @@ -21,6 +21,8 @@ import { i18n } from '@kbn/i18n'; import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { GetInternalStartServicesFn } from '../../../types'; +import { GeoBoundingBox } from './lib/geo_point'; +import { BaseAggParams } from '../types'; const filterTitle = i18n.translate('data.search.aggs.buckets.filterTitle', { defaultMessage: 'Filter', @@ -30,6 +32,10 @@ export interface FilterBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsFilter extends BaseAggParams { + geo_bounding_box?: GeoBoundingBox; +} + export const getFilterBucketAgg = ({ getInternalStartServices }: FilterBucketAggDependencies) => new BucketAggType( { diff --git a/src/plugins/data/public/search/aggs/buckets/filter_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/filter_fn.test.ts new file mode 100644 index 0000000000000..c820a73b0a894 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/filter_fn.test.ts @@ -0,0 +1,85 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggFilter } from './filter_fn'; + +describe('agg_expression_functions', () => { + describe('aggFilter', () => { + const fn = functionWrapper(aggFilter()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({}); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "geo_bounding_box": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "filter", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + geo_bounding_box: JSON.stringify({ + wkt: 'BBOX (-74.1, -71.12, 40.73, 40.01)', + }), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "geo_bounding_box": Object { + "wkt": "BBOX (-74.1, -71.12, 40.73, 40.01)", + }, + "json": undefined, + }, + "schema": undefined, + "type": "filter", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/filter_fn.ts b/src/plugins/data/public/search/aggs/buckets/filter_fn.ts new file mode 100644 index 0000000000000..4a7180fc86c71 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/filter_fn.ts @@ -0,0 +1,99 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggFilter'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggFilter = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.filter.help', { + defaultMessage: 'Generates a serialized agg config for a Filter agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filter.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.filter.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filter.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + geo_bounding_box: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filter.geoBoundingBox.help', { + defaultMessage: 'Filter results based on a point location within a bounding box', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filter.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filter.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.FILTER, + params: { + ...rest, + json: getParsedValue(args, 'json'), + geo_bounding_box: getParsedValue(args, 'geo_bounding_box'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/filters.ts b/src/plugins/data/public/search/aggs/buckets/filters.ts index fe013928bba65..8654645d46a9b 100644 --- a/src/plugins/data/public/search/aggs/buckets/filters.ts +++ b/src/plugins/data/public/search/aggs/buckets/filters.ts @@ -29,6 +29,7 @@ import { Storage } from '../../../../../../plugins/kibana_utils/public'; import { getEsQueryConfig, buildEsQuery, Query } from '../../../../common'; import { getQueryLog } from '../../../query'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const filtersTitle = i18n.translate('data.search.aggs.buckets.filtersTitle', { defaultMessage: 'Filters', @@ -47,6 +48,13 @@ export interface FiltersBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsFilters extends Omit { + filters?: Array<{ + input: Query; + label: string; + }>; +} + export const getFiltersBucketAgg = ({ uiSettings, getInternalStartServices, diff --git a/src/plugins/data/public/search/aggs/buckets/filters_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/filters_fn.test.ts new file mode 100644 index 0000000000000..99c4f7d8c2b65 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/filters_fn.test.ts @@ -0,0 +1,91 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggFilters } from './filters_fn'; + +describe('agg_expression_functions', () => { + describe('aggFilters', () => { + const fn = functionWrapper(aggFilters()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({}); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "filters": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "filters", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + filters: JSON.stringify([ + { + query: 'query', + language: 'lucene', + label: 'test', + }, + ]), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "filters": Array [ + Object { + "label": "test", + "language": "lucene", + "query": "query", + }, + ], + "json": undefined, + }, + "schema": undefined, + "type": "filters", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/filters_fn.ts b/src/plugins/data/public/search/aggs/buckets/filters_fn.ts new file mode 100644 index 0000000000000..6ffd5369d7087 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/filters_fn.ts @@ -0,0 +1,93 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggFilters'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggFilters = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.filters.help', { + defaultMessage: 'Generates a serialized agg config for a Filter agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filters.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.filters.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filters.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + filters: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filters.filters.help', { + defaultMessage: 'Filters to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.filters.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.FILTERS, + params: { + ...rest, + filters: getParsedValue(args, 'filters'), + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash.ts index eab10edad60f6..be339de5d7fae 100644 --- a/src/plugins/data/public/search/aggs/buckets/geo_hash.ts +++ b/src/plugins/data/public/search/aggs/buckets/geo_hash.ts @@ -22,6 +22,8 @@ import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { KBN_FIELD_TYPES } from '../../../../common'; import { BUCKET_TYPES } from './bucket_agg_types'; import { GetInternalStartServicesFn } from '../../../types'; +import { GeoBoundingBox } from './lib/geo_point'; +import { BaseAggParams } from '../types'; const defaultBoundingBox = { top_left: { lat: 1, lon: 1 }, @@ -38,6 +40,15 @@ export interface GeoHashBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsGeoHash extends BaseAggParams { + field: string; + autoPrecision?: boolean; + precision?: number; + useGeocentroid?: boolean; + isFilteredByCollar?: boolean; + boundingBox?: GeoBoundingBox; +} + export const getGeoHashBucketAgg = ({ getInternalStartServices }: GeoHashBucketAggDependencies) => new BucketAggType( { diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.test.ts new file mode 100644 index 0000000000000..07ab8e66f1def --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.test.ts @@ -0,0 +1,112 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggGeoHash } from './geo_hash_fn'; + +describe('agg_expression_functions', () => { + describe('aggGeoHash', () => { + const fn = functionWrapper(aggGeoHash()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'geo_field', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "autoPrecision": undefined, + "boundingBox": undefined, + "customLabel": undefined, + "field": "geo_field", + "isFilteredByCollar": undefined, + "json": undefined, + "precision": undefined, + "useGeocentroid": undefined, + }, + "schema": undefined, + "type": "geohash_grid", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'geo_field', + autoPrecision: false, + precision: 10, + useGeocentroid: true, + isFilteredByCollar: false, + boundingBox: JSON.stringify({ + top_left: [-74.1, 40.73], + bottom_right: [-71.12, 40.01], + }), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "autoPrecision": false, + "boundingBox": Object { + "bottom_right": Array [ + -71.12, + 40.01, + ], + "top_left": Array [ + -74.1, + 40.73, + ], + }, + "customLabel": undefined, + "field": "geo_field", + "isFilteredByCollar": false, + "json": undefined, + "precision": 10, + "useGeocentroid": true, + }, + "schema": undefined, + "type": "geohash_grid", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'geo_field', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + field: 'geo_field', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts new file mode 100644 index 0000000000000..bbfa8575d486c --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts @@ -0,0 +1,129 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggGeoHash'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggGeoHash = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.geoHash.help', { + defaultMessage: 'Generates a serialized agg config for a Geo Hash agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoHash.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.geoHash.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoHash.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.geoHash.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + useGeocentroid: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.geoHash.useGeocentroid.help', { + defaultMessage: 'Specifies whether to use geocentroid for this aggregation', + }), + }, + autoPrecision: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.geoHash.autoPrecision.help', { + defaultMessage: 'Specifies whether to use auto precision for this aggregation', + }), + }, + isFilteredByCollar: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.geoHash.isFilteredByCollar.help', { + defaultMessage: 'Specifies whether to filter by collar', + }), + }, + boundingBox: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoHash.boundingBox.help', { + defaultMessage: 'Filter results based on a point location within a bounding box', + }), + }, + precision: { + types: ['number'], + help: i18n.translate('data.search.aggs.buckets.geoHash.precision.help', { + defaultMessage: 'Precision to use for this aggregation.', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoHash.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoHash.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.GEOHASH_GRID, + params: { + ...rest, + boundingBox: getParsedValue(args, 'boundingBox'), + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/geo_tile.ts b/src/plugins/data/public/search/aggs/buckets/geo_tile.ts index c981e8400f9a1..1212bba23a93a 100644 --- a/src/plugins/data/public/search/aggs/buckets/geo_tile.ts +++ b/src/plugins/data/public/search/aggs/buckets/geo_tile.ts @@ -25,6 +25,7 @@ import { BUCKET_TYPES } from './bucket_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { METRIC_TYPES } from '../metrics/metric_agg_types'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; export interface GeoTitleBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; @@ -34,6 +35,12 @@ const geotileGridTitle = i18n.translate('data.search.aggs.buckets.geotileGridTit defaultMessage: 'Geotile', }); +export interface AggParamsGeoTile extends BaseAggParams { + field: string; + useGeocentroid?: boolean; + precision?: number; +} + export const getGeoTitleBucketAgg = ({ getInternalStartServices }: GeoTitleBucketAggDependencies) => new BucketAggType( { diff --git a/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.test.ts new file mode 100644 index 0000000000000..bfaf47ede8734 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.test.ts @@ -0,0 +1,91 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggGeoTile } from './geo_tile_fn'; + +describe('agg_expression_functions', () => { + describe('aggGeoTile', () => { + const fn = functionWrapper(aggGeoTile()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'geo_field', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "geo_field", + "json": undefined, + "precision": undefined, + "useGeocentroid": undefined, + }, + "schema": undefined, + "type": "geotile_grid", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'geo_field', + useGeocentroid: false, + precision: 10, + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "geo_field", + "json": undefined, + "precision": 10, + "useGeocentroid": false, + }, + "schema": undefined, + "type": "geotile_grid", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'geo_field', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + field: 'geo_field', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts b/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts new file mode 100644 index 0000000000000..9c33ef45762af --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts @@ -0,0 +1,108 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggGeoTile'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggGeoTile = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.geoTile.help', { + defaultMessage: 'Generates a serialized agg config for a Geo Tile agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoTile.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.geoTile.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoTile.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.geoTile.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + useGeocentroid: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.geoTile.useGeocentroid.help', { + defaultMessage: 'Specifies whether to use geocentroid for this aggregation', + }), + }, + precision: { + types: ['number'], + help: i18n.translate('data.search.aggs.buckets.geoTile.precision.help', { + defaultMessage: 'Precision to use for this aggregation.', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoTile.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.geoTile.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.GEOTILE_GRID, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.ts b/src/plugins/data/public/search/aggs/buckets/histogram.ts index f8e8720d24ea9..d04df4f8aac6b 100644 --- a/src/plugins/data/public/search/aggs/buckets/histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/histogram.ts @@ -26,6 +26,8 @@ import { createFilterHistogram } from './create_filter/histogram'; import { BUCKET_TYPES } from './bucket_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; +import { ExtendedBounds } from './lib/extended_bounds'; export interface AutoBounds { min: number; @@ -42,6 +44,15 @@ export interface IBucketHistogramAggConfig extends IBucketAggConfig { getAutoBounds: () => AutoBounds; } +export interface AggParamsHistogram extends BaseAggParams { + field: string; + interval: string; + intervalBase?: number; + min_doc_count?: boolean; + has_extended_bounds?: boolean; + extended_bounds?: ExtendedBounds; +} + export const getHistogramBucketAgg = ({ uiSettings, getInternalStartServices, diff --git a/src/plugins/data/public/search/aggs/buckets/histogram_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/histogram_fn.test.ts new file mode 100644 index 0000000000000..34b6fa1a6dcd6 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/histogram_fn.test.ts @@ -0,0 +1,109 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggHistogram } from './histogram_fn'; + +describe('agg_expression_functions', () => { + describe('aggHistogram', () => { + const fn = functionWrapper(aggHistogram()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'field', + interval: '10', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "extended_bounds": undefined, + "field": "field", + "has_extended_bounds": undefined, + "interval": "10", + "intervalBase": undefined, + "json": undefined, + "min_doc_count": undefined, + }, + "schema": undefined, + "type": "histogram", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'field', + interval: '10', + intervalBase: 1, + min_doc_count: false, + has_extended_bounds: false, + extended_bounds: JSON.stringify({ + min: 1, + max: 2, + }), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "extended_bounds": Object { + "max": 2, + "min": 1, + }, + "field": "field", + "has_extended_bounds": false, + "interval": "10", + "intervalBase": 1, + "json": undefined, + "min_doc_count": false, + }, + "schema": undefined, + "type": "histogram", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'field', + interval: '10', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + field: 'field', + interval: '10', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/histogram_fn.ts b/src/plugins/data/public/search/aggs/buckets/histogram_fn.ts new file mode 100644 index 0000000000000..1e5a5a72c0ecb --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/histogram_fn.ts @@ -0,0 +1,132 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggHistogram'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggHistogram = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.histogram.help', { + defaultMessage: 'Generates a serialized agg config for a Histogram agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.histogram.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.histogram.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.histogram.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.histogram.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + interval: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.histogram.interval.help', { + defaultMessage: 'Interval to use for this aggregation', + }), + }, + intervalBase: { + types: ['number'], + help: i18n.translate('data.search.aggs.buckets.histogram.intervalBase.help', { + defaultMessage: 'IntervalBase to use for this aggregation', + }), + }, + min_doc_count: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.histogram.minDocCount.help', { + defaultMessage: 'Specifies whether to use min_doc_count for this aggregation', + }), + }, + has_extended_bounds: { + types: ['boolean'], + help: i18n.translate('data.search.aggs.buckets.histogram.hasExtendedBounds.help', { + defaultMessage: 'Specifies whether to use has_extended_bounds for this aggregation', + }), + }, + extended_bounds: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.histogram.extendedBounds.help', { + defaultMessage: + 'With extended_bounds setting, you now can "force" the histogram aggregation to start building buckets on a specific min value and also keep on building buckets up to a max value ', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.histogram.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.histogram.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.HISTOGRAM, + params: { + ...rest, + extended_bounds: getParsedValue(args, 'extended_bounds'), + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/index.ts b/src/plugins/data/public/search/aggs/buckets/index.ts index 3a402b1498a77..7036cc7785db7 100644 --- a/src/plugins/data/public/search/aggs/buckets/index.ts +++ b/src/plugins/data/public/search/aggs/buckets/index.ts @@ -19,11 +19,18 @@ export * from './_interval_options'; export * from './bucket_agg_types'; +export * from './histogram'; export * from './date_histogram'; export * from './date_range'; +export * from './range'; +export * from './filter'; +export * from './filters'; +export * from './geo_tile'; +export * from './geo_hash'; export * from './ip_range'; export * from './lib/cidr_mask'; export * from './lib/date_range'; export * from './lib/ip_range'; export * from './migrate_include_exclude_format'; +export * from './significant_terms'; export * from './terms'; diff --git a/src/plugins/data/public/search/aggs/buckets/ip_range.ts b/src/plugins/data/public/search/aggs/buckets/ip_range.ts index bde347d6e673d..029fd864154be 100644 --- a/src/plugins/data/public/search/aggs/buckets/ip_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/ip_range.ts @@ -23,18 +23,38 @@ import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { createFilterIpRange } from './create_filter/ip_range'; -import { IpRangeKey, convertIPRangeToString } from './lib/ip_range'; +import { + convertIPRangeToString, + IpRangeKey, + RangeIpRangeAggKey, + CidrMaskIpRangeAggKey, +} from './lib/ip_range'; import { KBN_FIELD_TYPES, FieldFormat, TEXT_CONTEXT_TYPE } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const ipRangeTitle = i18n.translate('data.search.aggs.buckets.ipRangeTitle', { defaultMessage: 'IPv4 Range', }); +export enum IP_RANGE_TYPES { + FROM_TO = 'fromTo', + MASK = 'mask', +} + export interface IpRangeBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsIpRange extends BaseAggParams { + field: string; + ipRangeType?: IP_RANGE_TYPES; + ranges?: Partial<{ + [IP_RANGE_TYPES.FROM_TO]: RangeIpRangeAggKey[]; + [IP_RANGE_TYPES.MASK]: CidrMaskIpRangeAggKey[]; + }>; +} + export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketAggDependencies) => new BucketAggType( { @@ -42,7 +62,7 @@ export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketA title: ipRangeTitle, createFilter: createFilterIpRange, getKey(bucket, key, agg): IpRangeKey { - if (agg.params.ipRangeType === 'mask') { + if (agg.params.ipRangeType === IP_RANGE_TYPES.MASK) { return { type: 'mask', mask: key }; } return { type: 'range', from: bucket.from, to: bucket.to }; @@ -74,7 +94,7 @@ export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketA }, { name: 'ipRangeType', - default: 'fromTo', + default: IP_RANGE_TYPES.FROM_TO, write: noop, }, { @@ -90,7 +110,7 @@ export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketA const ipRangeType = aggConfig.params.ipRangeType; let ranges = aggConfig.params.ranges[ipRangeType]; - if (ipRangeType === 'fromTo') { + if (ipRangeType === IP_RANGE_TYPES.FROM_TO) { ranges = map(ranges, (range: any) => omit(range, isNull)); } diff --git a/src/plugins/data/public/search/aggs/buckets/ip_range_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/ip_range_fn.test.ts new file mode 100644 index 0000000000000..5940345b25890 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/ip_range_fn.test.ts @@ -0,0 +1,102 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { IP_RANGE_TYPES } from './ip_range'; +import { aggIpRange } from './ip_range_fn'; + +describe('agg_expression_functions', () => { + describe('aggIpRange', () => { + const fn = functionWrapper(aggIpRange()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'ip_field', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "ip_field", + "ipRangeType": undefined, + "json": undefined, + "ranges": undefined, + }, + "schema": undefined, + "type": "ip_range", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'ip_field', + ipRangeType: IP_RANGE_TYPES.MASK, + ranges: JSON.stringify({ + mask: [{ mask: '10.0.0.0/25' }], + }), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "ip_field", + "ipRangeType": "mask", + "json": undefined, + "ranges": Object { + "mask": Array [ + Object { + "mask": "10.0.0.0/25", + }, + ], + }, + }, + "schema": undefined, + "type": "ip_range", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'ip_field', + ipRangeType: IP_RANGE_TYPES.MASK, + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + field: 'ip_field', + ipRangeType: IP_RANGE_TYPES.FROM_TO, + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts b/src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts new file mode 100644 index 0000000000000..554a8708d9164 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts @@ -0,0 +1,114 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggIpRange'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggIpRange = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.ipRange.help', { + defaultMessage: 'Generates a serialized agg config for a Ip Range agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.ipRange.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.ipRange.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.ipRange.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.ipRange.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + ipRangeType: { + types: ['string'], + options: ['mask', 'fromTo'], + help: i18n.translate('data.search.aggs.buckets.ipRange.ipRangeType.help', { + defaultMessage: + 'IP range type to use for this aggregation. Takes one of the following values: mask, fromTo.', + }), + }, + ranges: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.ipRange.ranges.help', { + defaultMessage: 'Serialized ranges to use for this aggregation.', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.ipRange.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.ipRange.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.IP_RANGE, + params: { + ...rest, + json: getParsedValue(args, 'json'), + ranges: getParsedValue(args, 'ranges'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/lib/date_range.ts b/src/plugins/data/public/search/aggs/buckets/lib/date_range.ts index 6eb9fe8414ec8..d52bdff993a2b 100644 --- a/src/plugins/data/public/search/aggs/buckets/lib/date_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/lib/date_range.ts @@ -18,8 +18,8 @@ */ export interface DateRangeKey { - from: number; - to: number; + from: number | string; + to: number | string; } export function convertDateRangeToString({ from, to }: DateRangeKey, format: (val: any) => string) { diff --git a/src/plugins/data/public/search/aggs/buckets/lib/extended_bounds.ts b/src/plugins/data/public/search/aggs/buckets/lib/extended_bounds.ts new file mode 100644 index 0000000000000..7a249a9daca91 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/lib/extended_bounds.ts @@ -0,0 +1,23 @@ +/* + * 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 interface ExtendedBounds { + min: number; + max: number; +} diff --git a/src/plugins/data/public/search/aggs/buckets/lib/geo_point.ts b/src/plugins/data/public/search/aggs/buckets/lib/geo_point.ts new file mode 100644 index 0000000000000..8ff4493e286cf --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/lib/geo_point.ts @@ -0,0 +1,86 @@ +/* + * 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. + */ + +type GeoPoint = + | { + lat: number; + lon: number; + } + | string + | [number, number]; + +interface GeoBox { + top: number; + left: number; + bottom: number; + right: number; +} + +/** GeoBoundingBox Accepted Formats: + * Lat Lon As Properties: + * "top_left" : { + * "lat" : 40.73, "lon" : -74.1 + * }, + * "bottom_right" : { + * "lat" : 40.01, "lon" : -71.12 + * } + * + * Lat Lon As Array: + * { + * "top_left" : [-74.1, 40.73], + * "bottom_right" : [-71.12, 40.01] + * } + * + * Lat Lon As String: + * { + * "top_left" : "40.73, -74.1", + * "bottom_right" : "40.01, -71.12" + * } + * + * Bounding Box as Well-Known Text (WKT): + * { + * "wkt" : "BBOX (-74.1, -71.12, 40.73, 40.01)" + * } + * + * Geohash: + * { + * "top_right" : "dr5r9ydj2y73", + * "bottom_left" : "drj7teegpus6" + * } + * + * Vertices: + * { + * "top" : 40.73, + * "left" : -74.1, + * "bottom" : 40.01, + * "right" : -71.12 + * } + * + * **/ +export type GeoBoundingBox = + | Partial<{ + top_left: GeoPoint; + top_right: GeoPoint; + bottom_right: GeoPoint; + bottom_left: GeoPoint; + }> + | { + wkt: string; + } + | GeoBox; diff --git a/src/plugins/data/public/search/aggs/buckets/lib/ip_range.ts b/src/plugins/data/public/search/aggs/buckets/lib/ip_range.ts index be1ac28934c7c..57e5337d4c365 100644 --- a/src/plugins/data/public/search/aggs/buckets/lib/ip_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/lib/ip_range.ts @@ -17,9 +17,18 @@ * under the License. */ -export type IpRangeKey = - | { type: 'mask'; mask: string } - | { type: 'range'; from: string; to: string }; +export interface CidrMaskIpRangeAggKey { + type: 'mask'; + mask: string; +} + +export interface RangeIpRangeAggKey { + type: 'range'; + from: string; + to: string; +} + +export type IpRangeKey = CidrMaskIpRangeAggKey | RangeIpRangeAggKey; export const convertIPRangeToString = (range: IpRangeKey, format: (val: any) => string) => { if (range.type === 'mask') { diff --git a/src/plugins/data/public/search/aggs/buckets/range.ts b/src/plugins/data/public/search/aggs/buckets/range.ts index 2c1303814a88a..02aad3bd5fed1 100644 --- a/src/plugins/data/public/search/aggs/buckets/range.ts +++ b/src/plugins/data/public/search/aggs/buckets/range.ts @@ -24,6 +24,7 @@ import { RangeKey } from './range_key'; import { createFilterRange } from './create_filter/range'; import { BUCKET_TYPES } from './bucket_agg_types'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const keyCaches = new WeakMap(); const formats = new WeakMap(); @@ -36,6 +37,14 @@ export interface RangeBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsRange extends BaseAggParams { + field: string; + ranges?: Array<{ + from: number; + to: number; + }>; +} + export const getRangeBucketAgg = ({ getInternalStartServices }: RangeBucketAggDependencies) => new BucketAggType( { diff --git a/src/plugins/data/public/search/aggs/buckets/range_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/range_fn.test.ts new file mode 100644 index 0000000000000..93ae4490196a8 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/range_fn.test.ts @@ -0,0 +1,100 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggRange } from './range_fn'; + +describe('agg_expression_functions', () => { + describe('aggRange', () => { + const fn = functionWrapper(aggRange()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'number_field', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "number_field", + "json": undefined, + "ranges": undefined, + }, + "schema": undefined, + "type": "range", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'number_field', + ranges: JSON.stringify([ + { from: 1, to: 2 }, + { from: 5, to: 100 }, + ]), + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "number_field", + "json": undefined, + "ranges": Array [ + Object { + "from": 1, + "to": 2, + }, + Object { + "from": 5, + "to": 100, + }, + ], + }, + "schema": undefined, + "type": "range", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'number_field', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + field: 'number_field', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/range_fn.ts b/src/plugins/data/public/search/aggs/buckets/range_fn.ts new file mode 100644 index 0000000000000..48686e7061de9 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/range_fn.ts @@ -0,0 +1,106 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggRange'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggRange = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.range.help', { + defaultMessage: 'Generates a serialized agg config for a Range agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.range.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.range.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.range.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.range.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + ranges: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.range.ranges.help', { + defaultMessage: 'Serialized ranges to use for this aggregation.', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.range.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.range.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.RANGE, + params: { + ...rest, + json: getParsedValue(args, 'json'), + ranges: getParsedValue(args, 'ranges'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms.ts index 49d797f3afbc9..e6afc56dfd31c 100644 --- a/src/plugins/data/public/search/aggs/buckets/significant_terms.ts +++ b/src/plugins/data/public/search/aggs/buckets/significant_terms.ts @@ -24,6 +24,7 @@ import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exc import { BUCKET_TYPES } from './bucket_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const significantTermsTitle = i18n.translate('data.search.aggs.buckets.significantTermsTitle', { defaultMessage: 'Significant Terms', @@ -33,6 +34,13 @@ export interface SignificantTermsBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsSignificantTerms extends BaseAggParams { + field: string; + size?: number; + exclude?: string; + include?: string; +} + export const getSignificantTermsBucketAgg = ({ getInternalStartServices, }: SignificantTermsBucketAggDependencies) => diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.test.ts new file mode 100644 index 0000000000000..71be4e9cfa9ac --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggSignificantTerms } from './significant_terms_fn'; + +describe('agg_expression_functions', () => { + describe('aggSignificantTerms', () => { + const fn = functionWrapper(aggSignificantTerms()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "exclude": undefined, + "field": "machine.os.keyword", + "include": undefined, + "json": undefined, + "size": undefined, + }, + "schema": undefined, + "type": "significant_terms", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + id: '1', + enabled: false, + schema: 'whatever', + field: 'machine.os.keyword', + size: 6, + include: 'win', + exclude: 'ios', + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": false, + "id": "1", + "params": Object { + "customLabel": undefined, + "exclude": "ios", + "field": "machine.os.keyword", + "include": "win", + "json": undefined, + "size": 6, + }, + "schema": "whatever", + "type": "significant_terms", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.ts new file mode 100644 index 0000000000000..83583070bddfe --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.ts @@ -0,0 +1,116 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggSignificantTerms'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; + +type Arguments = AggArgs; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggSignificantTerms = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.significantTerms.help', { + defaultMessage: 'Generates a serialized agg config for a Significant Terms agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.significantTerms.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.significantTerms.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.significantTerms.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.significantTerms.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + size: { + types: ['number'], + help: i18n.translate('data.search.aggs.buckets.significantTerms.size.help', { + defaultMessage: 'Max number of buckets to retrieve', + }), + }, + exclude: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.significantTerms.exclude.help', { + defaultMessage: 'Specific bucket values to exclude from results', + }), + }, + include: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.significantTerms.include.help', { + defaultMessage: 'Specific bucket values to include in results', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.significantTerms.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.significantTerms.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: BUCKET_TYPES.SIGNIFICANT_TERMS, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/buckets/terms.ts b/src/plugins/data/public/search/aggs/buckets/terms.ts index a12a1d7ac2d3d..1bfc508dc3871 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms.ts @@ -26,7 +26,7 @@ import { isStringOrNumberType, migrateIncludeExcludeFormat, } from './migrate_include_exclude_format'; -import { AggConfigSerialized, IAggConfigs } from '../types'; +import { AggConfigSerialized, BaseAggParams, IAggConfigs } from '../types'; import { Adapters } from '../../../../../inspector/public'; import { ISearchSource } from '../../search_source'; @@ -63,11 +63,11 @@ export interface TermsBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } -export interface AggParamsTerms { +export interface AggParamsTerms extends BaseAggParams { field: string; - order: 'asc' | 'desc'; orderBy: string; orderAgg?: AggConfigSerialized; + order?: 'asc' | 'desc'; size?: number; missingBucket?: boolean; missingBucketLabel?: string; @@ -76,7 +76,6 @@ export interface AggParamsTerms { // advanced exclude?: string; include?: string; - json?: string; } export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDependencies) => diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts index f55f1de796013..1384a9f17e4b6 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts @@ -27,7 +27,6 @@ describe('agg_expression_functions', () => { test('fills in defaults when only required args are provided', () => { const actual = fn({ field: 'machine.os.keyword', - order: 'asc', orderBy: '1', }); expect(actual).toMatchInlineSnapshot(` @@ -37,18 +36,19 @@ describe('agg_expression_functions', () => { "enabled": true, "id": undefined, "params": Object { + "customLabel": undefined, "exclude": undefined, "field": "machine.os.keyword", "include": undefined, "json": undefined, - "missingBucket": false, - "missingBucketLabel": "Missing", - "order": "asc", + "missingBucket": undefined, + "missingBucketLabel": undefined, + "order": undefined, "orderAgg": undefined, "orderBy": "1", - "otherBucket": false, - "otherBucketLabel": "Other", - "size": 5, + "otherBucket": undefined, + "otherBucketLabel": undefined, + "size": undefined, }, "schema": undefined, "type": "terms", @@ -70,6 +70,7 @@ describe('agg_expression_functions', () => { missingBucketLabel: 'missing', otherBucket: true, otherBucketLabel: 'other', + include: 'win', exclude: 'ios', }); @@ -78,9 +79,10 @@ describe('agg_expression_functions', () => { "enabled": false, "id": "1", "params": Object { + "customLabel": undefined, "exclude": "ios", "field": "machine.os.keyword", - "include": undefined, + "include": "win", "json": undefined, "missingBucket": true, "missingBucketLabel": "missing", @@ -107,37 +109,39 @@ describe('agg_expression_functions', () => { expect(actual.value.params).toMatchInlineSnapshot(` Object { + "customLabel": undefined, "exclude": undefined, "field": "machine.os.keyword", "include": undefined, "json": undefined, - "missingBucket": false, - "missingBucketLabel": "Missing", + "missingBucket": undefined, + "missingBucketLabel": undefined, "order": "asc", "orderAgg": Object { "enabled": true, "id": undefined, "params": Object { + "customLabel": undefined, "exclude": undefined, "field": "name", "include": undefined, "json": undefined, - "missingBucket": false, - "missingBucketLabel": "Missing", + "missingBucket": undefined, + "missingBucketLabel": undefined, "order": "asc", "orderAgg": undefined, "orderBy": "1", - "otherBucket": false, - "otherBucketLabel": "Other", - "size": 5, + "otherBucket": undefined, + "otherBucketLabel": undefined, + "size": undefined, }, "schema": undefined, "type": "terms", }, "orderBy": "1", - "otherBucket": false, - "otherBucketLabel": "Other", - "size": 5, + "otherBucket": undefined, + "otherBucketLabel": undefined, + "size": undefined, } `); }); diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.ts b/src/plugins/data/public/search/aggs/buckets/terms_fn.ts index 7980bfabe79fb..49520863fe1cc 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms_fn.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms_fn.ts @@ -20,27 +20,25 @@ import { i18n } from '@kbn/i18n'; import { Assign } from '@kbn/utility-types'; import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; -import { AggExpressionType, AggExpressionFunctionArgs } from '../'; +import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; -const aggName = 'terms'; const fnName = 'aggTerms'; type Input = any; -type AggArgs = AggExpressionFunctionArgs; +type AggArgs = AggExpressionFunctionArgs; + // Since the orderAgg param is an agg nested in a subexpression, we need to // overwrite the param type to expect a value of type AggExpressionType. -type Arguments = AggArgs & - Assign< - AggArgs, - { orderAgg?: AggArgs['orderAgg'] extends undefined ? undefined : AggExpressionType } - >; +type Arguments = Assign; + type Output = AggExpressionType; type FunctionDefinition = ExpressionFunctionDefinition; export const aggTerms = (): FunctionDefinition => ({ name: fnName, help: i18n.translate('data.search.aggs.function.buckets.terms.help', { - defaultMessage: 'Generates a serialized agg config for a terms agg', + defaultMessage: 'Generates a serialized agg config for a Terms agg', }), type: 'agg_type', args: { @@ -72,7 +70,7 @@ export const aggTerms = (): FunctionDefinition => ({ }, order: { types: ['string'], - required: true, + options: ['asc', 'desc'], help: i18n.translate('data.search.aggs.buckets.terms.order.help', { defaultMessage: 'Order in which to return the results: asc or desc', }), @@ -91,41 +89,30 @@ export const aggTerms = (): FunctionDefinition => ({ }, size: { types: ['number'], - default: 5, help: i18n.translate('data.search.aggs.buckets.terms.size.help', { defaultMessage: 'Max number of buckets to retrieve', }), }, missingBucket: { types: ['boolean'], - default: false, help: i18n.translate('data.search.aggs.buckets.terms.missingBucket.help', { defaultMessage: 'When set to true, groups together any buckets with missing fields', }), }, missingBucketLabel: { types: ['string'], - default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', { - defaultMessage: 'Missing', - description: `Default label used in charts when documents are missing a field. - Visible when you create a chart with a terms aggregation and enable "Show missing values"`, - }), help: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel.help', { defaultMessage: 'Default label used in charts when documents are missing a field.', }), }, otherBucket: { types: ['boolean'], - default: false, help: i18n.translate('data.search.aggs.buckets.terms.otherBucket.help', { defaultMessage: 'When set to true, groups together any buckets beyond the allowed size', }), }, otherBucketLabel: { types: ['string'], - default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', { - defaultMessage: 'Other', - }), help: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel.help', { defaultMessage: 'Default label used in charts for documents in the Other bucket', }), @@ -148,32 +135,27 @@ export const aggTerms = (): FunctionDefinition => ({ defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', }), }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.terms.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, }, fn: (input, args) => { const { id, enabled, schema, ...rest } = args; - let json; - try { - json = args.json ? JSON.parse(args.json) : undefined; - } catch (e) { - throw new Error('Unable to parse json argument string'); - } - - // Need to spread this object to work around TS bug: - // https://github.com/microsoft/TypeScript/issues/15300#issuecomment-436793742 - const orderAgg = args.orderAgg?.value ? { ...args.orderAgg.value } : undefined; - return { type: 'agg_type', value: { id, enabled, schema, - type: aggName, + type: BUCKET_TYPES.TERMS, params: { ...rest, - orderAgg, - json, + orderAgg: args.orderAgg?.value, + json: getParsedValue(args, 'json'), }, }, }; diff --git a/src/plugins/data/public/search/aggs/metrics/avg.ts b/src/plugins/data/public/search/aggs/metrics/avg.ts index d53ce8d3fc489..96be3e849a3e8 100644 --- a/src/plugins/data/public/search/aggs/metrics/avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/avg.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const averageTitle = i18n.translate('data.search.aggs.metrics.averageTitle', { defaultMessage: 'Average', }); +export interface AggParamsAvg extends BaseAggParams { + field: string; +} + export interface AvgMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts new file mode 100644 index 0000000000000..0e2ee00df49dd --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggAvg } from './avg_fn'; + +describe('agg_expression_functions', () => { + describe('aggAvg', () => { + const fn = functionWrapper(aggAvg()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "avg", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/avg_fn.ts b/src/plugins/data/public/search/aggs/metrics/avg_fn.ts new file mode 100644 index 0000000000000..c370623b2752a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/avg_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggAvg'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggAvg = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.avg.help', { + defaultMessage: 'Generates a serialized agg config for a Avg agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.avg.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.avg.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.AVG, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts b/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts index 2c32ebc671539..ded17eebf465b 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts @@ -23,8 +23,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketAvg extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketAvgMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts new file mode 100644 index 0000000000000..7e08bc9954510 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggBucketAvg } from './bucket_avg_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketAvg', () => { + const fn = functionWrapper(aggBucketAvg()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "avg_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "avg_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts new file mode 100644 index 0000000000000..56643a2df54bd --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts @@ -0,0 +1,107 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketAvg'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketAvg = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_avg.help', { + defaultMessage: 'Generates a serialized agg config for a Avg Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_avg.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.AVG_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max.ts b/src/plugins/data/public/search/aggs/metrics/bucket_max.ts index 1e57a2dd8e38e..dde328008b88a 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_max.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_max.ts @@ -22,8 +22,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketMax extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketMaxMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts new file mode 100644 index 0000000000000..b789bdf51ebd5 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggBucketMax } from './bucket_max_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketMax', () => { + const fn = functionWrapper(aggBucketMax()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "max_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "max_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts new file mode 100644 index 0000000000000..896e9cf839605 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts @@ -0,0 +1,107 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketMax'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketMax = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_max.help', { + defaultMessage: 'Generates a serialized agg config for a Max Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_max.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MAX_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min.ts b/src/plugins/data/public/search/aggs/metrics/bucket_min.ts index 0484af23a7141..9949524ce6110 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_min.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_min.ts @@ -22,8 +22,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketMin extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketMinMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts new file mode 100644 index 0000000000000..6ebc83417813b --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggBucketMin } from './bucket_min_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketMin', () => { + const fn = functionWrapper(aggBucketMin()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "min_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "min_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts new file mode 100644 index 0000000000000..2ae3d9211227a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts @@ -0,0 +1,107 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketMin'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketMin = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_min.help', { + defaultMessage: 'Generates a serialized agg config for a Min Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_min.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MIN_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts b/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts index 0a4d29a18a980..e69ae5798c6e1 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts @@ -22,8 +22,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketSum extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketSumMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts new file mode 100644 index 0000000000000..71549f41b1d15 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggBucketSum } from './bucket_sum_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketSum', () => { + const fn = functionWrapper(aggBucketSum()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "sum_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "sum_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts new file mode 100644 index 0000000000000..eceb11a90f293 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts @@ -0,0 +1,107 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketSum'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketSum = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_sum.help', { + defaultMessage: 'Generates a serialized agg config for a Sum Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_sum.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.SUM_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality.ts b/src/plugins/data/public/search/aggs/metrics/cardinality.ts index 10b6b5aff1abd..af594195fe027 100644 --- a/src/plugins/data/public/search/aggs/metrics/cardinality.ts +++ b/src/plugins/data/public/search/aggs/metrics/cardinality.ts @@ -22,11 +22,16 @@ import { MetricAggType, IMetricAggConfig } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const uniqueCountTitle = i18n.translate('data.search.aggs.metrics.uniqueCountTitle', { defaultMessage: 'Unique Count', }); +export interface AggParamsCardinality extends BaseAggParams { + field: string; +} + export interface CardinalityMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts new file mode 100644 index 0000000000000..4008819018ee5 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggCardinality } from './cardinality_fn'; + +describe('agg_expression_functions', () => { + describe('aggCardinality', () => { + const fn = functionWrapper(aggCardinality()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "cardinality", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts new file mode 100644 index 0000000000000..f30429993638f --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggCardinality'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggCardinality = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.cardinality.help', { + defaultMessage: 'Generates a serialized agg config for a Cardinality agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.cardinality.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.cardinality.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.CARDINALITY, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/count_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/count_fn.test.ts new file mode 100644 index 0000000000000..846feb9296fca --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/count_fn.test.ts @@ -0,0 +1,59 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggCount } from './count_fn'; + +describe('agg_expression_functions', () => { + describe('aggCount', () => { + const fn = functionWrapper(aggCount()); + + test('correctly creates agg type', () => { + const actual = fn({}); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "count", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/count_fn.ts b/src/plugins/data/public/search/aggs/metrics/count_fn.ts new file mode 100644 index 0000000000000..f4c7e8e854230 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/count_fn.ts @@ -0,0 +1,88 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggCount'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggCount = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.count.help', { + defaultMessage: 'Generates a serialized agg config for a Count agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.count.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.COUNT, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts b/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts index 8ca922e144a1f..67e907239799a 100644 --- a/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts @@ -22,8 +22,15 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsCumulativeSum extends BaseAggParams { + buckets_path: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface CumulativeSumMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts new file mode 100644 index 0000000000000..3cf53e3da153e --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts @@ -0,0 +1,120 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggCumulativeSum } from './cumulative_sum_fn'; + +describe('agg_expression_functions', () => { + describe('aggCumulativeSum', () => { + const fn = functionWrapper(aggCumulativeSum()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "cumulative_sum", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + }, + "schema": undefined, + "type": "cumulative_sum", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "cumulative_sum", + }, + "json": undefined, + "metricAgg": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts new file mode 100644 index 0000000000000..950df03b10134 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts @@ -0,0 +1,111 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggCumulativeSum'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggCumulativeSum = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.cumulative_sum.help', { + defaultMessage: 'Generates a serialized agg config for a Cumulative Sum agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.CUMULATIVE_SUM, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/derivative.ts b/src/plugins/data/public/search/aggs/metrics/derivative.ts index 5752a72c846aa..edb907ca4ed41 100644 --- a/src/plugins/data/public/search/aggs/metrics/derivative.ts +++ b/src/plugins/data/public/search/aggs/metrics/derivative.ts @@ -22,8 +22,15 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsDerivative extends BaseAggParams { + buckets_path: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface DerivativeMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts new file mode 100644 index 0000000000000..79ea7292104ee --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts @@ -0,0 +1,120 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggDerivative } from './derivative_fn'; + +describe('agg_expression_functions', () => { + describe('aggDerivative', () => { + const fn = functionWrapper(aggDerivative()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "derivative", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + }, + "schema": undefined, + "type": "derivative", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "derivative", + }, + "json": undefined, + "metricAgg": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/derivative_fn.ts b/src/plugins/data/public/search/aggs/metrics/derivative_fn.ts new file mode 100644 index 0000000000000..90b88b4de2712 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/derivative_fn.ts @@ -0,0 +1,111 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggDerivative'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggDerivative = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.derivative.help', { + defaultMessage: 'Generates a serialized agg config for a Derivative agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.derivative.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.derivative.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.derivative.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.DERIVATIVE, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts b/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts index 00927ebba56bf..864e97ca8dfe7 100644 --- a/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts +++ b/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsGeoBounds extends BaseAggParams { + field: string; +} export interface GeoBoundsMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts new file mode 100644 index 0000000000000..96bd31916784a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggGeoBounds } from './geo_bounds_fn'; + +describe('agg_expression_functions', () => { + describe('aggGeoBounds', () => { + const fn = functionWrapper(aggGeoBounds()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "geo_bounds", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts new file mode 100644 index 0000000000000..8ba71a098fc70 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggGeoBounds'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggGeoBounds = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.geo_bounds.help', { + defaultMessage: 'Generates a serialized agg config for a Geo Bounds agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.geo_bounds.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.geo_bounds.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.GEO_BOUNDS, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts b/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts index a4b084f794a5d..2bbb6b2de8d87 100644 --- a/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts +++ b/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsGeoCentroid extends BaseAggParams { + field: string; +} export interface GeoCentroidMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts new file mode 100644 index 0000000000000..bf9a4548bafbf --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggGeoCentroid } from './geo_centroid_fn'; + +describe('agg_expression_functions', () => { + describe('aggGeoCentroid', () => { + const fn = functionWrapper(aggGeoCentroid()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "geo_centroid", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts new file mode 100644 index 0000000000000..464f9b535cd8b --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggGeoCentroid'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggGeoCentroid = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.geo_centroid.help', { + defaultMessage: 'Generates a serialized agg config for a Geo Centroid agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.geo_centroid.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.geo_centroid.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.GEO_CENTROID, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/index.ts b/src/plugins/data/public/search/aggs/metrics/index.ts index eb93e99427f65..ef7de68b05de9 100644 --- a/src/plugins/data/public/search/aggs/metrics/index.ts +++ b/src/plugins/data/public/search/aggs/metrics/index.ts @@ -21,3 +21,23 @@ export * from './metric_agg_type'; export * from './metric_agg_types'; export * from './lib/parent_pipeline_agg_helper'; export * from './lib/sibling_pipeline_agg_helper'; +export { AggParamsAvg } from './avg'; +export { AggParamsCardinality } from './cardinality'; +export { AggParamsGeoBounds } from './geo_bounds'; +export { AggParamsGeoCentroid } from './geo_centroid'; +export { AggParamsMax } from './max'; +export { AggParamsMedian } from './median'; +export { AggParamsMin } from './min'; +export { AggParamsStdDeviation } from './std_deviation'; +export { AggParamsSum } from './sum'; +export { AggParamsBucketAvg } from './bucket_avg'; +export { AggParamsBucketMax } from './bucket_max'; +export { AggParamsBucketMin } from './bucket_min'; +export { AggParamsBucketSum } from './bucket_sum'; +export { AggParamsCumulativeSum } from './cumulative_sum'; +export { AggParamsDerivative } from './derivative'; +export { AggParamsMovingAvg } from './moving_avg'; +export { AggParamsPercentileRanks } from './percentile_ranks'; +export { AggParamsPercentiles } from './percentiles'; +export { AggParamsSerialDiff } from './serial_diff'; +export { AggParamsTopHit } from './top_hit'; diff --git a/src/plugins/data/public/search/aggs/metrics/max.ts b/src/plugins/data/public/search/aggs/metrics/max.ts index 88e8b485cb73f..49cbfba5a269d 100644 --- a/src/plugins/data/public/search/aggs/metrics/max.ts +++ b/src/plugins/data/public/search/aggs/metrics/max.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const maxTitle = i18n.translate('data.search.aggs.metrics.maxTitle', { defaultMessage: 'Max', }); +export interface AggParamsMax extends BaseAggParams { + field: string; +} + export interface MaxMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/max_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/max_fn.test.ts new file mode 100644 index 0000000000000..156b51ca54af5 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/max_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggMax } from './max_fn'; + +describe('agg_expression_functions', () => { + describe('aggMax', () => { + const fn = functionWrapper(aggMax()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "max", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/max_fn.ts b/src/plugins/data/public/search/aggs/metrics/max_fn.ts new file mode 100644 index 0000000000000..1d68c8919fca8 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/max_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMax'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMax = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.max.help', { + defaultMessage: 'Generates a serialized agg config for a Max agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.max.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.max.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MAX, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/median.ts b/src/plugins/data/public/search/aggs/metrics/median.ts index a398f017602b0..725fdcb2400d1 100644 --- a/src/plugins/data/public/search/aggs/metrics/median.ts +++ b/src/plugins/data/public/search/aggs/metrics/median.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const medianTitle = i18n.translate('data.search.aggs.metrics.medianTitle', { defaultMessage: 'Median', }); +export interface AggParamsMedian extends BaseAggParams { + field: string; +} + export interface MedianMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/median_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/median_fn.test.ts new file mode 100644 index 0000000000000..69200c35426c8 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/median_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggMedian } from './median_fn'; + +describe('agg_expression_functions', () => { + describe('aggMedian', () => { + const fn = functionWrapper(aggMedian()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "median", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/median_fn.ts b/src/plugins/data/public/search/aggs/metrics/median_fn.ts new file mode 100644 index 0000000000000..2e8e89992136e --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/median_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMedian'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMedian = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.median.help', { + defaultMessage: 'Generates a serialized agg config for a Median agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.median.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.median.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MEDIAN, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/min.ts b/src/plugins/data/public/search/aggs/metrics/min.ts index aae16f357186c..0f52aa8a4f788 100644 --- a/src/plugins/data/public/search/aggs/metrics/min.ts +++ b/src/plugins/data/public/search/aggs/metrics/min.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const minTitle = i18n.translate('data.search.aggs.metrics.minTitle', { defaultMessage: 'Min', }); +export interface AggParamsMin extends BaseAggParams { + field: string; +} + export interface MinMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/min_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/min_fn.test.ts new file mode 100644 index 0000000000000..ef32d086e41f7 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/min_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggMin } from './min_fn'; + +describe('agg_expression_functions', () => { + describe('aggMin', () => { + const fn = functionWrapper(aggMin()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "min", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/min_fn.ts b/src/plugins/data/public/search/aggs/metrics/min_fn.ts new file mode 100644 index 0000000000000..b51da46a137b0 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/min_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMin'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMin = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.min.help', { + defaultMessage: 'Generates a serialized agg config for a Min agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.min.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.min.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MIN, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg.ts b/src/plugins/data/public/search/aggs/metrics/moving_avg.ts index 94b9b1d8cd487..38a824629d304 100644 --- a/src/plugins/data/public/search/aggs/metrics/moving_avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/moving_avg.ts @@ -22,8 +22,17 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsMovingAvg extends BaseAggParams { + buckets_path: string; + window?: number; + script?: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface MovingAvgMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts new file mode 100644 index 0000000000000..d6c0e6b2cbd6e --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts @@ -0,0 +1,130 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggMovingAvg } from './moving_avg_fn'; + +describe('agg_expression_functions', () => { + describe('aggMovingAvg', () => { + const fn = functionWrapper(aggMovingAvg()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + "script": undefined, + "window": undefined, + }, + "schema": undefined, + "type": "moving_avg", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + window: 10, + script: 'test', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + "script": "test", + "window": 10, + }, + "schema": undefined, + "type": "moving_avg", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + "script": undefined, + "window": undefined, + }, + "schema": undefined, + "type": "moving_avg", + }, + "json": undefined, + "metricAgg": undefined, + "script": undefined, + "window": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts new file mode 100644 index 0000000000000..54a3fa176385b --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts @@ -0,0 +1,124 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMovingAvg'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMovingAvg = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.moving_avg.help', { + defaultMessage: 'Generates a serialized agg config for a Moving Average agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.moving_avg.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + window: { + types: ['number'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.window.help', { + defaultMessage: 'The size of window to "slide" across the histogram.', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.derivative.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + script: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.script.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MOVING_FN, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts index 0d79665ff9c4e..c8383f6bcc3d9 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts @@ -24,6 +24,12 @@ import { getPercentileValue } from './percentiles_get_value'; import { METRIC_TYPES } from './metric_agg_types'; import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsPercentileRanks extends BaseAggParams { + field: string; + values?: number[]; +} // required by the values editor export type IPercentileRanksAggConfig = IResponseAggConfig; diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts new file mode 100644 index 0000000000000..e3ce91bafd40a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggPercentileRanks } from './percentile_ranks_fn'; + +describe('agg_expression_functions', () => { + describe('aggPercentileRanks', () => { + const fn = functionWrapper(aggPercentileRanks()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "values": undefined, + }, + "schema": undefined, + "type": "percentile_ranks", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + values: [1, 2, 3], + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "values": Array [ + 1, + 2, + 3, + ], + }, + "schema": undefined, + "type": "percentile_ranks", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts new file mode 100644 index 0000000000000..851e938f28c1c --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts @@ -0,0 +1,102 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggPercentileRanks'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggPercentileRanks = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.percentile_ranks.help', { + defaultMessage: 'Generates a serialized agg config for a Percentile Ranks agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + values: { + types: ['number'], + multi: true, + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.values.help', { + defaultMessage: 'Range of percentiles ranks', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.PERCENTILE_RANKS, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.ts b/src/plugins/data/public/search/aggs/metrics/percentiles.ts index 040a52588dd94..ad3c19cfaffcc 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentiles.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentiles.ts @@ -25,6 +25,12 @@ import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_respons import { getPercentileValue } from './percentiles_get_value'; import { ordinalSuffix } from './lib/ordinal_suffix'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsPercentiles extends BaseAggParams { + field: string; + percents?: number[]; +} export type IPercentileAggConfig = IResponseAggConfig; diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts new file mode 100644 index 0000000000000..2074cc1d89527 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggPercentiles } from './percentiles_fn'; + +describe('agg_expression_functions', () => { + describe('aggPercentiles', () => { + const fn = functionWrapper(aggPercentiles()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "percents": undefined, + }, + "schema": undefined, + "type": "percentiles", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + percents: [1, 2, 3], + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "percents": Array [ + 1, + 2, + 3, + ], + }, + "schema": undefined, + "type": "percentiles", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts new file mode 100644 index 0000000000000..b799be07925fa --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts @@ -0,0 +1,102 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggPercentiles'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggPercentiles = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.percentiles.help', { + defaultMessage: 'Generates a serialized agg config for a Percentiles agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.percentiles.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.percentiles.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + percents: { + types: ['number'], + multi: true, + help: i18n.translate('data.search.aggs.metrics.percentiles.percents.help', { + defaultMessage: 'Range of percentiles ranks', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.PERCENTILES, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff.ts b/src/plugins/data/public/search/aggs/metrics/serial_diff.ts index 2b1498560f862..fe112a50ad3c1 100644 --- a/src/plugins/data/public/search/aggs/metrics/serial_diff.ts +++ b/src/plugins/data/public/search/aggs/metrics/serial_diff.ts @@ -22,8 +22,15 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsSerialDiff extends BaseAggParams { + buckets_path: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface SerialDiffMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts new file mode 100644 index 0000000000000..1bb859ad4bad8 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts @@ -0,0 +1,120 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggSerialDiff } from './serial_diff_fn'; + +describe('agg_expression_functions', () => { + describe('aggSerialDiff', () => { + const fn = functionWrapper(aggSerialDiff()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "serial_diff", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + }, + "schema": undefined, + "type": "serial_diff", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "serial_diff", + }, + "json": undefined, + "metricAgg": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts new file mode 100644 index 0000000000000..9ba313aff7386 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts @@ -0,0 +1,111 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggSerialDiff'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggSerialDiff = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.serial_diff.help', { + defaultMessage: 'Generates a serialized agg config for a Serial Differencing agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.serial_diff.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.serial_diff.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.SERIAL_DIFF, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation.ts index e972132542ceb..1733d5476f667 100644 --- a/src/plugins/data/public/search/aggs/metrics/std_deviation.ts +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation.ts @@ -24,6 +24,11 @@ import { METRIC_TYPES } from './metric_agg_types'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsStdDeviation extends BaseAggParams { + field: string; +} interface ValProp { valProp: string[]; diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts new file mode 100644 index 0000000000000..bfa6aa7cc4122 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggStdDeviation } from './std_deviation_fn'; + +describe('agg_expression_functions', () => { + describe('aggStdDeviation', () => { + const fn = functionWrapper(aggStdDeviation()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "std_dev", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts new file mode 100644 index 0000000000000..70623e2e48041 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggStdDeviation'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggStdDeviation = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.std_deviation.help', { + defaultMessage: 'Generates a serialized agg config for a Standard Deviation agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.std_deviation.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.std_deviation.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.STD_DEV, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/sum.ts b/src/plugins/data/public/search/aggs/metrics/sum.ts index 545c6d6a4939e..70fc379f2d5f1 100644 --- a/src/plugins/data/public/search/aggs/metrics/sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/sum.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const sumTitle = i18n.translate('data.search.aggs.metrics.sumTitle', { defaultMessage: 'Sum', }); +export interface AggParamsSum extends BaseAggParams { + field: string; +} + export interface SumMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts new file mode 100644 index 0000000000000..6e57632ba84cc --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggSum } from './sum_fn'; + +describe('agg_expression_functions', () => { + describe('aggSum', () => { + const fn = functionWrapper(aggSum()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "sum", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/sum_fn.ts b/src/plugins/data/public/search/aggs/metrics/sum_fn.ts new file mode 100644 index 0000000000000..a277aef02693f --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/sum_fn.ts @@ -0,0 +1,95 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggSum'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggSum = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.sum.help', { + defaultMessage: 'Generates a serialized agg config for a Sum agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.sum.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.sum.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.SUM, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.ts b/src/plugins/data/public/search/aggs/metrics/top_hit.ts index 15da2b485aee7..df7a76f151c07 100644 --- a/src/plugins/data/public/search/aggs/metrics/top_hit.ts +++ b/src/plugins/data/public/search/aggs/metrics/top_hit.ts @@ -23,6 +23,15 @@ import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsTopHit extends BaseAggParams { + field: string; + aggregate: 'min' | 'max' | 'sum' | 'average' | 'concat'; + sortField?: string; + size?: number; + sortOrder?: 'desc' | 'asc'; +} export interface TopHitMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts new file mode 100644 index 0000000000000..d0e9788f85025 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts @@ -0,0 +1,102 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggTopHit } from './top_hit_fn'; + +describe('agg_expression_functions', () => { + describe('aggTopHit', () => { + const fn = functionWrapper(aggTopHit()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + aggregate: 'min', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "aggregate": "min", + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "size": undefined, + "sortField": undefined, + "sortOrder": undefined, + }, + "schema": undefined, + "type": "top_hits", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + id: '1', + enabled: false, + schema: 'whatever', + field: 'machine.os.keyword', + sortOrder: 'asc', + size: 6, + aggregate: 'min', + sortField: '_score', + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": false, + "id": "1", + "params": Object { + "aggregate": "min", + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "size": 6, + "sortField": "_score", + "sortOrder": "asc", + }, + "schema": "whatever", + "type": "top_hits", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + aggregate: 'min', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + aggregate: 'min', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts new file mode 100644 index 0000000000000..adfd22b540e06 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts @@ -0,0 +1,122 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggTopHit'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggTopHit = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.top_hit.help', { + defaultMessage: 'Generates a serialized agg config for a Top Hit agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.top_hit.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.top_hit.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + aggregate: { + types: ['string'], + required: true, + options: ['min', 'max', 'sum', 'average', 'concat'], + help: i18n.translate('data.search.aggs.metrics.top_hit.aggregate.help', { + defaultMessage: 'Aggregate type', + }), + }, + size: { + types: ['number'], + help: i18n.translate('data.search.aggs.metrics.top_hit.size.help', { + defaultMessage: 'Max number of buckets to retrieve', + }), + }, + sortOrder: { + types: ['string'], + options: ['desc', 'asc'], + help: i18n.translate('data.search.aggs.metrics.top_hit.sortOrder.help', { + defaultMessage: 'Order in which to return the results: asc or desc', + }), + }, + sortField: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.sortField.help', { + defaultMessage: 'Field to order results by', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.TOP_HITS, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/types.ts b/src/plugins/data/public/search/aggs/types.ts index 1c5b5b458ce90..a784bfaada4c7 100644 --- a/src/plugins/data/public/search/aggs/types.ts +++ b/src/plugins/data/public/search/aggs/types.ts @@ -21,11 +21,43 @@ import { IndexPattern } from '../../index_patterns'; import { AggConfigSerialized, AggConfigs, + AggParamsRange, + AggParamsIpRange, + AggParamsDateRange, + AggParamsFilter, + AggParamsFilters, + AggParamsSignificantTerms, + AggParamsGeoTile, + AggParamsGeoHash, AggParamsTerms, + AggParamsAvg, + AggParamsCardinality, + AggParamsGeoBounds, + AggParamsGeoCentroid, + AggParamsMax, + AggParamsMedian, + AggParamsMin, + AggParamsStdDeviation, + AggParamsSum, + AggParamsBucketAvg, + AggParamsBucketMax, + AggParamsBucketMin, + AggParamsBucketSum, + AggParamsCumulativeSum, + AggParamsDerivative, + AggParamsMovingAvg, + AggParamsPercentileRanks, + AggParamsPercentiles, + AggParamsSerialDiff, + AggParamsTopHit, + AggParamsHistogram, + AggParamsDateHistogram, AggTypesRegistrySetup, AggTypesRegistryStart, CreateAggConfigParams, getCalculateAutoTimeExpression, + METRIC_TYPES, + BUCKET_TYPES, } from './'; export { IAggConfig, AggConfigSerialized } from './agg_config'; @@ -55,6 +87,12 @@ export interface SearchAggsStart { types: AggTypesRegistryStart; } +/** @internal */ +export interface BaseAggParams { + json?: string; + customLabel?: string; +} + /** @internal */ export interface AggExpressionType { type: 'agg_type'; @@ -74,5 +112,36 @@ export type AggExpressionFunctionArgs< * @internal */ export interface AggParamsMapping { - terms: AggParamsTerms; + [BUCKET_TYPES.RANGE]: AggParamsRange; + [BUCKET_TYPES.IP_RANGE]: AggParamsIpRange; + [BUCKET_TYPES.DATE_RANGE]: AggParamsDateRange; + [BUCKET_TYPES.FILTER]: AggParamsFilter; + [BUCKET_TYPES.FILTERS]: AggParamsFilters; + [BUCKET_TYPES.SIGNIFICANT_TERMS]: AggParamsSignificantTerms; + [BUCKET_TYPES.GEOTILE_GRID]: AggParamsGeoTile; + [BUCKET_TYPES.GEOHASH_GRID]: AggParamsGeoHash; + [BUCKET_TYPES.HISTOGRAM]: AggParamsHistogram; + [BUCKET_TYPES.DATE_HISTOGRAM]: AggParamsDateHistogram; + [BUCKET_TYPES.TERMS]: AggParamsTerms; + [METRIC_TYPES.AVG]: AggParamsAvg; + [METRIC_TYPES.CARDINALITY]: AggParamsCardinality; + [METRIC_TYPES.COUNT]: BaseAggParams; + [METRIC_TYPES.GEO_BOUNDS]: AggParamsGeoBounds; + [METRIC_TYPES.GEO_CENTROID]: AggParamsGeoCentroid; + [METRIC_TYPES.MAX]: AggParamsMax; + [METRIC_TYPES.MEDIAN]: AggParamsMedian; + [METRIC_TYPES.MIN]: AggParamsMin; + [METRIC_TYPES.STD_DEV]: AggParamsStdDeviation; + [METRIC_TYPES.SUM]: AggParamsSum; + [METRIC_TYPES.AVG_BUCKET]: AggParamsBucketAvg; + [METRIC_TYPES.MAX_BUCKET]: AggParamsBucketMax; + [METRIC_TYPES.MIN_BUCKET]: AggParamsBucketMin; + [METRIC_TYPES.SUM_BUCKET]: AggParamsBucketSum; + [METRIC_TYPES.CUMULATIVE_SUM]: AggParamsCumulativeSum; + [METRIC_TYPES.DERIVATIVE]: AggParamsDerivative; + [METRIC_TYPES.MOVING_FN]: AggParamsMovingAvg; + [METRIC_TYPES.PERCENTILE_RANKS]: AggParamsPercentileRanks; + [METRIC_TYPES.PERCENTILES]: AggParamsPercentiles; + [METRIC_TYPES.SERIAL_DIFF]: AggParamsSerialDiff; + [METRIC_TYPES.TOP_HITS]: AggParamsTopHit; } diff --git a/src/plugins/data/public/search/aggs/utils/get_parsed_value.ts b/src/plugins/data/public/search/aggs/utils/get_parsed_value.ts new file mode 100644 index 0000000000000..48e752369d1d3 --- /dev/null +++ b/src/plugins/data/public/search/aggs/utils/get_parsed_value.ts @@ -0,0 +1,34 @@ +/* + * 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. + */ + +/** + * This method parses a JSON string and constructs the Object or object described by the string. + * If the given string is not valid JSON, you will get a syntax error. + * @param data { Object } - an object that contains the required for parsing field + * @param key { string} - name of the field to be parsed + * + * @internal + */ +export const getParsedValue = (data: any, key: string) => { + try { + return data[key] ? JSON.parse(data[key]) : undefined; + } catch (e) { + throw new Error(`Unable to parse ${key} argument string`); + } +}; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/phrase_suggestor.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/phrase_suggestor.tsx index 2b2d83c9f5a8b..546365b89d9be 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/phrase_suggestor.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/phrase_suggestor.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { Component } from 'react'; +import React from 'react'; import { debounce } from 'lodash'; import { withKibana, KibanaReactContextValue } from '../../../../../kibana_react/public'; @@ -39,7 +39,7 @@ export interface PhraseSuggestorState { * aggregatable), we pull out the common logic for requesting suggestions into this component * which both of them extend. */ -export class PhraseSuggestorUI extends Component< +export class PhraseSuggestorUI extends React.Component< T, PhraseSuggestorState > { diff --git a/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx index 6ca1b7582001f..8ad1b5d392f3b 100644 --- a/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx +++ b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx @@ -180,6 +180,7 @@ export function SavedQueryManagementComponent({ }} anchorPosition="downLeft" panelPaddingSize="none" + buffer={-8} ownFocus >

void; } interface MountedDevToolDescriptor { - devTool: DevTool; + devTool: DevToolApp; mountpoint: HTMLElement; unmountHandler: () => void; } @@ -64,10 +63,10 @@ function DevToolsWrapper({ {devTools.map(currentDevTool => ( { - if (!currentDevTool.disabled) { + if (!currentDevTool.isDisabled()) { updateRoute(`/dev_tools/${currentDevTool.id}`); } }} @@ -151,7 +150,7 @@ export function renderApp( element: HTMLElement, appMountContext: AppMountContext, basePath: string, - devTools: readonly DevTool[] + devTools: readonly DevToolApp[] ) { if (redirectOnMissingCapabilities(appMountContext)) { return () => {}; @@ -162,21 +161,24 @@ export function renderApp( - {devTools.map(devTool => ( - ( - - )} - /> - ))} + {devTools + // Only create routes for devtools that are not disabled + .filter(devTool => !devTool.isDisabled()) + .map(devTool => ( + ( + + )} + /> + ))} diff --git a/src/plugins/dev_tools/public/dev_tool.ts b/src/plugins/dev_tools/public/dev_tool.ts new file mode 100644 index 0000000000000..943cca286a722 --- /dev/null +++ b/src/plugins/dev_tools/public/dev_tool.ts @@ -0,0 +1,106 @@ +/* + * 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 { App } from 'kibana/public'; + +/** + * Descriptor for a dev tool. A dev tool works similar to an application + * registered in the core application service. + */ +export type CreateDevToolArgs = Omit & { + disabled?: boolean; +}; + +export class DevToolApp { + /** + * The id of the dev tools. This will become part of the URL path + * (`dev_tools/${devTool.id}`. It has to be unique among registered + * dev tools. + */ + public readonly id: string; + /** + * The human readable name of the dev tool. Should be internationalized. + * This will be used as a label in the tab above the actual tool. + */ + public readonly title: string; + public readonly mount: App['mount']; + + /** + * Flag indicating to disable the tab of this dev tool. Navigating to a + * disabled dev tool will be treated as the navigation to an unknown route + * (redirect to the console). + */ + private disabled: boolean; + + /** + * Optional tooltip content of the tab. + */ + public readonly tooltipContent?: string; + /** + * Flag indicating whether the dev tool will do routing within the `dev_tools/${devTool.id}/` + * prefix. If it is set to true, the dev tool is responsible to redirect + * the user when navigating to unknown URLs within the prefix. If set + * to false only the root URL of the dev tool will be recognized as valid. + */ + public readonly enableRouting: boolean; + /** + * Number used to order the tabs. + */ + public readonly order: number; + + constructor( + id: string, + title: string, + mount: App['mount'], + enableRouting: boolean, + order: number, + toolTipContent = '', + disabled = false + ) { + this.id = id; + this.title = title; + this.mount = mount; + this.enableRouting = enableRouting; + this.order = order; + this.tooltipContent = toolTipContent; + this.disabled = disabled; + } + + public enable() { + this.disabled = false; + } + + public disable() { + this.disabled = true; + } + + public isDisabled(): boolean { + return this.disabled; + } +} + +export const createDevToolApp = ({ + id, + title, + mount, + enableRouting, + order, + tooltipContent, + disabled, +}: CreateDevToolArgs) => + new DevToolApp(id, title, mount, enableRouting, order, tooltipContent, disabled); diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts index df61271baf879..bedf92818315a 100644 --- a/src/plugins/dev_tools/public/plugin.ts +++ b/src/plugins/dev_tools/public/plugin.ts @@ -17,9 +17,10 @@ * under the License. */ -import { App, CoreSetup, Plugin } from 'kibana/public'; +import { CoreSetup, Plugin } from 'kibana/public'; import { sortBy } from 'lodash'; import { KibanaLegacySetup } from '../../kibana_legacy/public'; +import { CreateDevToolArgs, DevToolApp, createDevToolApp } from './dev_tool'; import './index.scss'; @@ -34,7 +35,7 @@ export interface DevToolsSetup { * to switch between the tools. * @param devTool The dev tools descriptor */ - register: (devTool: DevTool) => void; + register: (devTool: CreateDevToolArgs) => DevToolApp; } export interface DevToolsStart { @@ -46,53 +47,13 @@ export interface DevToolsStart { * becomes an implementation detail. * @deprecated */ - getSortedDevTools: () => readonly DevTool[]; -} - -/** - * Descriptor for a dev tool. A dev tool works similar to an application - * registered in the core application service. - */ -export interface DevTool { - /** - * The id of the dev tools. This will become part of the URL path - * (`dev_tools/${devTool.id}`. It has to be unique among registered - * dev tools. - */ - id: string; - /** - * The human readable name of the dev tool. Should be internationalized. - * This will be used as a label in the tab above the actual tool. - */ - title: string; - mount: App['mount']; - /** - * Flag indicating to disable the tab of this dev tool. Navigating to a - * disabled dev tool will be treated as the navigation to an unknown route - * (redirect to the console). - */ - disabled?: boolean; - /** - * Optional tooltip content of the tab. - */ - tooltipContent?: string; - /** - * Flag indicating whether the dev tool will do routing within the `dev_tools/${devTool.id}/` - * prefix. If it is set to true, the dev tool is responsible to redirect - * the user when navigating to unknown URLs within the prefix. If set - * to false only the root URL of the dev tool will be recognized as valid. - */ - enableRouting: boolean; - /** - * Number used to order the tabs. - */ - order: number; + getSortedDevTools: () => readonly DevToolApp[]; } export class DevToolsPlugin implements Plugin { - private readonly devTools = new Map(); + private readonly devTools = new Map(); - private getSortedDevTools(): readonly DevTool[] { + private getSortedDevTools(): readonly DevToolApp[] { return sortBy([...this.devTools.values()], 'order'); } @@ -115,14 +76,16 @@ export class DevToolsPlugin implements Plugin { }); return { - register: (devTool: DevTool) => { - if (this.devTools.has(devTool.id)) { + register: (devToolArgs: CreateDevToolArgs) => { + if (this.devTools.has(devToolArgs.id)) { throw new Error( - `Dev tool with id [${devTool.id}] has already been registered. Use a unique id.` + `Dev tool with id [${devToolArgs.id}] has already been registered. Use a unique id.` ); } + const devTool = createDevToolApp(devToolArgs); this.devTools.set(devTool.id, devTool); + return devTool; }, }; } diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index 2d41293f26369..0b3a07e98624e 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -2,5 +2,16 @@ "id": "discover", "version": "kibana", "server": true, - "ui": true + "ui": true, + "requiredPlugins": [ + "charts", + "data", + "embeddable", + "inspector", + "kibanaLegacy", + "navigation", + "uiActions", + "visualizations" + ], + "optionalPlugins": ["home", "share"] } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss b/src/plugins/discover/public/application/_discover.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss rename to src/plugins/discover/public/application/_discover.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/_hacks.scss b/src/plugins/discover/public/application/_hacks.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/_hacks.scss rename to src/plugins/discover/public/application/_hacks.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/_mixins.scss b/src/plugins/discover/public/application/_mixins.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/_mixins.scss rename to src/plugins/discover/public/application/_mixins.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/_index.scss b/src/plugins/discover/public/application/angular/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/_index.scss rename to src/plugins/discover/public/application/angular/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.html b/src/plugins/discover/public/application/angular/context.html similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.html rename to src/plugins/discover/public/application/angular/context.html diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js b/src/plugins/discover/public/application/angular/context.js similarity index 98% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js rename to src/plugins/discover/public/application/angular/context.js index 032ec7af09a30..33bbc8cb2e6cf 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js +++ b/src/plugins/discover/public/application/angular/context.js @@ -32,7 +32,7 @@ const k7Breadcrumbs = $route => { return [ ...getRootBreadcrumbs(), { - text: i18n.translate('kbn.context.breadcrumb', { + text: i18n.translate('discover.context.breadcrumb', { defaultMessage: 'Context of {indexPatternTitle}#{docId}', values: { indexPatternTitle: indexPattern.title, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/NOTES.md b/src/plugins/discover/public/application/angular/context/NOTES.md similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/NOTES.md rename to src/plugins/discover/public/application/angular/context/NOTES.md diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/_index.scss b/src/plugins/discover/public/application/angular/context/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/_index.scss rename to src/plugins/discover/public/application/angular/context/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/_stubs.js b/src/plugins/discover/public/application/angular/context/api/_stubs.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/_stubs.js rename to src/plugins/discover/public/application/angular/context/api/_stubs.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.js b/src/plugins/discover/public/application/angular/context/api/anchor.js similarity index 95% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.js rename to src/plugins/discover/public/application/angular/context/api/anchor.js index 4338d3e1dbdbd..4df5ba989f798 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.js +++ b/src/plugins/discover/public/application/angular/context/api/anchor.js @@ -46,7 +46,7 @@ export function fetchAnchorProvider(indexPatterns, searchSource) { if (_.get(response, ['hits', 'total'], 0) < 1) { throw new Error( - i18n.translate('kbn.context.failedToLoadAnchorDocumentErrorDescription', { + i18n.translate('discover.context.failedToLoadAnchorDocumentErrorDescription', { defaultMessage: 'Failed to load anchor document.', }) ); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.test.js b/src/plugins/discover/public/application/angular/context/api/anchor.test.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.test.js rename to src/plugins/discover/public/application/angular/context/api/anchor.test.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.predecessors.test.js b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.predecessors.test.js rename to src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.successors.test.js b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.successors.test.js rename to src/plugins/discover/public/application/angular/context/api/context.successors.test.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts similarity index 97% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts rename to src/plugins/discover/public/application/angular/context/api/context.ts index 2760eec38755e..0bca820f9a723 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.ts @@ -17,17 +17,13 @@ * under the License. */ +import { Filter, IndexPatternsContract, IndexPattern } from 'src/plugins/data/public'; import { reverseSortDir, SortDirection } from './utils/sorting'; import { extractNanos, convertIsoToMillis } from './utils/date_conversion'; import { fetchHitsInInterval } from './utils/fetch_hits_in_interval'; import { generateIntervals } from './utils/generate_intervals'; import { getEsQuerySearchAfter } from './utils/get_es_query_search_after'; import { getEsQuerySort } from './utils/get_es_query_sort'; -import { - Filter, - IndexPatternsContract, - IndexPattern, -} from '../../../../../../../../../plugins/data/public'; import { getServices } from '../../../../kibana_services'; export type SurrDocType = 'successors' | 'predecessors'; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/__tests__/date_conversion.test.ts b/src/plugins/discover/public/application/angular/context/api/utils/date_conversion.test.ts similarity index 96% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/__tests__/date_conversion.test.ts rename to src/plugins/discover/public/application/angular/context/api/utils/date_conversion.test.ts index b9ec105cc0e7b..223b174718296 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/__tests__/date_conversion.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/date_conversion.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { extractNanos } from '../date_conversion'; +import { extractNanos } from './date_conversion'; describe('function extractNanos', function() { test('extract nanos of 2014-01-01', function() { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/date_conversion.ts b/src/plugins/discover/public/application/angular/context/api/utils/date_conversion.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/date_conversion.ts rename to src/plugins/discover/public/application/angular/context/api/utils/date_conversion.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/fetch_hits_in_interval.ts b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts similarity index 95% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/fetch_hits_in_interval.ts rename to src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts index 8eed5d33ab004..437898201863f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/fetch_hits_in_interval.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts @@ -16,11 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { - ISearchSource, - EsQuerySortValue, - SortDirection, -} from '../../../../../../../../../../plugins/data/public'; +import { ISearchSource, EsQuerySortValue, SortDirection } from '../../../../../../../data/public'; import { convertTimeValueToIso } from './date_conversion'; import { EsHitRecordList } from '../context'; import { IntervalValue } from './generate_intervals'; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/generate_intervals.ts b/src/plugins/discover/public/application/angular/context/api/utils/generate_intervals.ts similarity index 95% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/generate_intervals.ts rename to src/plugins/discover/public/application/angular/context/api/utils/generate_intervals.ts index b14180d32b4f2..1497f54aa5079 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/generate_intervals.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/generate_intervals.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { SortDirection } from '../../../../../../../../../../plugins/data/public'; +import { SortDirection } from '../../../../../../../data/public'; export type IntervalValue = number | null; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/get_es_query_search_after.ts b/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/get_es_query_search_after.ts rename to src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/get_es_query_sort.ts b/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_sort.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/get_es_query_sort.ts rename to src/plugins/discover/public/application/angular/context/api/utils/get_es_query_sort.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/__tests__/sorting.test.ts b/src/plugins/discover/public/application/angular/context/api/utils/sorting.test.ts similarity index 94% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/__tests__/sorting.test.ts rename to src/plugins/discover/public/application/angular/context/api/utils/sorting.test.ts index eeae2aa2c5d0a..350a0a8ede8d5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/__tests__/sorting.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/sorting.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { reverseSortDir, SortDirection } from '../sorting'; +import { reverseSortDir, SortDirection } from './sorting'; describe('function reverseSortDir', function() { test('reverse a given sort direction', function() { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/sorting.ts b/src/plugins/discover/public/application/angular/context/api/utils/sorting.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/utils/sorting.ts rename to src/plugins/discover/public/application/angular/context/api/utils/sorting.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/_action_bar.scss b/src/plugins/discover/public/application/angular/context/components/action_bar/_action_bar.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/_action_bar.scss rename to src/plugins/discover/public/application/angular/context/components/action_bar/_action_bar.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/_index.scss b/src/plugins/discover/public/application/angular/context/components/action_bar/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/_index.scss rename to src/plugins/discover/public/application/angular/context/components/action_bar/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.test.tsx b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.test.tsx rename to src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.tsx b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.tsx similarity index 93% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.tsx rename to src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.tsx index 8fcfcba08955c..97a29ab21c581 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.tsx +++ b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.tsx @@ -111,7 +111,7 @@ export function ActionBar({ }} flush="right" > - + @@ -119,10 +119,10 @@ export function ActionBar({ {isSuccessor ? ( ) : ( )} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_directive.ts b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_directive.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_directive.ts rename to src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_directive.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_warning.tsx b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_warning.tsx similarity index 90% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_warning.tsx rename to src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_warning.tsx index 6b922bb05a243..db757881ad819 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_warning.tsx +++ b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_warning.tsx @@ -31,12 +31,12 @@ export function ActionBarWarning({ docCount, type }: { docCount: number; type: S title={ docCount === 0 ? ( ) : ( @@ -55,12 +55,12 @@ export function ActionBarWarning({ docCount, type }: { docCount: number; type: S title={ docCount === 0 ? ( ) : ( diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/index.ts b/src/plugins/discover/public/application/angular/context/components/action_bar/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/index.ts rename to src/plugins/discover/public/application/angular/context/components/action_bar/index.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/helpers/call_after_bindings_workaround.js b/src/plugins/discover/public/application/angular/context/helpers/call_after_bindings_workaround.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/helpers/call_after_bindings_workaround.js rename to src/plugins/discover/public/application/angular/context/helpers/call_after_bindings_workaround.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js b/src/plugins/discover/public/application/angular/context/query/actions.js similarity index 95% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js rename to src/plugins/discover/public/application/angular/context/query/actions.js index efc230d2cd4ae..1b1fa7138bfda 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js +++ b/src/plugins/discover/public/application/angular/context/query/actions.js @@ -26,7 +26,7 @@ import { fetchAnchorProvider } from '../api/anchor'; import { fetchContextProvider } from '../api/context'; import { getQueryParameterActions } from '../query_parameters'; import { FAILURE_REASONS, LOADING_STATUS } from './constants'; -import { MarkdownSimple } from '../../../../../../../../../plugins/kibana_react/public'; +import { MarkdownSimple } from '../../../../../../kibana_react/public'; export function QueryActionsProvider(Promise) { const { filterManager, indexPatterns, data } = getServices(); @@ -80,7 +80,7 @@ export function QueryActionsProvider(Promise) { error => { setFailedStatus(state)('anchor', { error }); getServices().toastNotifications.addDanger({ - title: i18n.translate('kbn.context.unableToLoadAnchorDocumentDescription', { + title: i18n.translate('discover.context.unableToLoadAnchorDocumentDescription', { defaultMessage: 'Unable to load the anchor document', }), text: {error.message}, @@ -133,7 +133,7 @@ export function QueryActionsProvider(Promise) { error => { setFailedStatus(state)(type, { error }); getServices().toastNotifications.addDanger({ - title: i18n.translate('kbn.context.unableToLoadDocumentDescription', { + title: i18n.translate('discover.context.unableToLoadDocumentDescription', { defaultMessage: 'Unable to load documents', }), text: {error.message}, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/constants.js b/src/plugins/discover/public/application/angular/context/query/constants.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/constants.js rename to src/plugins/discover/public/application/angular/context/query/constants.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/index.js b/src/plugins/discover/public/application/angular/context/query/index.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/index.js rename to src/plugins/discover/public/application/angular/context/query/index.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/state.js b/src/plugins/discover/public/application/angular/context/query/state.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/state.js rename to src/plugins/discover/public/application/angular/context/query/state.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/actions.js b/src/plugins/discover/public/application/angular/context/query_parameters/actions.js similarity index 96% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/actions.js rename to src/plugins/discover/public/application/angular/context/query_parameters/actions.js index 5c1700e776361..4f86dea08fe84 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/actions.js +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.js @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import { esFilters } from '../../../../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../data/public'; import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, QUERY_PARAMETER_KEYS } from './constants'; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/actions.test.ts b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts similarity index 97% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/actions.test.ts rename to src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts index 35fbd33fb4bc9..00747fcc81227 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/actions.test.ts +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts @@ -19,8 +19,8 @@ // @ts-ignore import { getQueryParameterActions } from './actions'; -import { FilterManager } from '../../../../../../../../../plugins/data/public'; -import { coreMock } from '../../../../../../../../../core/public/mocks'; +import { FilterManager } from '../../../../../../data/public'; +import { coreMock } from '../../../../../../../core/public/mocks'; const setupMock = coreMock.createSetup(); let state: { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/constants.ts b/src/plugins/discover/public/application/angular/context/query_parameters/constants.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/constants.ts rename to src/plugins/discover/public/application/angular/context/query_parameters/constants.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/index.js b/src/plugins/discover/public/application/angular/context/query_parameters/index.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/index.js rename to src/plugins/discover/public/application/angular/context/query_parameters/index.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/state.ts b/src/plugins/discover/public/application/angular/context/query_parameters/state.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query_parameters/state.ts rename to src/plugins/discover/public/application/angular/context/query_parameters/state.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_app.html b/src/plugins/discover/public/application/angular/context_app.html similarity index 78% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_app.html rename to src/plugins/discover/public/application/angular/context_app.html index 8bbb746fa45f8..1f2d6a073150b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_app.html +++ b/src/plugins/discover/public/application/angular/context_app.html @@ -21,7 +21,7 @@
@@ -30,7 +30,7 @@
@@ -92,7 +92,7 @@ >
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_app.js b/src/plugins/discover/public/application/angular/context_app.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_app.js rename to src/plugins/discover/public/application/angular/context_app.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts b/src/plugins/discover/public/application/angular/context_state.test.ts similarity index 97% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts rename to src/plugins/discover/public/application/angular/context_state.test.ts index 1fa71ed11643a..83bf1b1d7e3d5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts +++ b/src/plugins/discover/public/application/angular/context_state.test.ts @@ -19,8 +19,8 @@ import { getState } from './context_state'; import { createBrowserHistory, History } from 'history'; -import { FilterManager, Filter } from '../../../../../../../plugins/data/public'; -import { coreMock } from '../../../../../../../core/public/mocks'; +import { FilterManager, Filter } from '../../../../data/public'; +import { coreMock } from '../../../../../core/public/mocks'; const setupMock = coreMock.createSetup(); describe('Test Discover Context State', () => { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts b/src/plugins/discover/public/application/angular/context_state.ts similarity index 98% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts rename to src/plugins/discover/public/application/angular/context_state.ts index b46995d44d826..7a92a6ace125b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts +++ b/src/plugins/discover/public/application/angular/context_state.ts @@ -23,8 +23,8 @@ import { createKbnUrlStateStorage, syncStates, BaseStateContainer, -} from '../../../../../../../plugins/kibana_utils/public'; -import { esFilters, FilterManager, Filter, Query } from '../../../../../../../plugins/data/public'; +} from '../../../../kibana_utils/public'; +import { esFilters, FilterManager, Filter, Query } from '../../../../data/public'; export interface AppState { /** diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/__snapshots__/no_results.test.js.snap b/src/plugins/discover/public/application/angular/directives/__snapshots__/no_results.test.js.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/__snapshots__/no_results.test.js.snap rename to src/plugins/discover/public/application/angular/directives/__snapshots__/no_results.test.js.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/_histogram.scss b/src/plugins/discover/public/application/angular/directives/_histogram.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/_histogram.scss rename to src/plugins/discover/public/application/angular/directives/_histogram.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/_index.scss b/src/plugins/discover/public/application/angular/directives/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/_index.scss rename to src/plugins/discover/public/application/angular/directives/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/_no_results.scss b/src/plugins/discover/public/application/angular/directives/_no_results.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/_no_results.scss rename to src/plugins/discover/public/application/angular/directives/_no_results.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/_collapsible_sidebar.scss b/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_collapsible_sidebar.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/_collapsible_sidebar.scss rename to src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_collapsible_sidebar.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/_depth.scss b/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_depth.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/_depth.scss rename to src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_depth.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/_index.scss b/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/_index.scss rename to src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/collapsible_sidebar.ts b/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/collapsible_sidebar.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/collapsible_sidebar/collapsible_sidebar.ts rename to src/plugins/discover/public/application/angular/directives/collapsible_sidebar/collapsible_sidebar.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/debounce/debounce.js b/src/plugins/discover/public/application/angular/directives/debounce/debounce.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/debounce/debounce.js rename to src/plugins/discover/public/application/angular/directives/debounce/debounce.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/debounce/__tests__/debounce.js b/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts similarity index 73% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/debounce/__tests__/debounce.js rename to src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts index 43fa5ffbf299a..bc08d8566d48a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/debounce/__tests__/debounce.js +++ b/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts @@ -17,42 +17,56 @@ * under the License. */ -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { DebounceProvider } from '../index'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; - -let debounce; -let debounceFromProvider; -let $timeout; - -function init() { - pluginInstance.initializeServices(); - pluginInstance.initializeInnerAngular(); - ngMock.module('app/discover'); - - ngMock.inject(function($injector, _$timeout_, Private) { - $timeout = _$timeout_; - - debounce = $injector.get('debounce'); - debounceFromProvider = Private(DebounceProvider); - }); -} +import sinon, { SinonSpy } from 'sinon'; +import angular, { auto, ITimeoutService } from 'angular'; +import 'angular-mocks'; +import 'angular-sanitize'; +import 'angular-route'; + +// @ts-ignore +import { DebounceProvider } from './index'; +import { coreMock } from '../../../../../../../core/public/mocks'; +import { initializeInnerAngularModule } from '../../../../get_inner_angular'; +import { navigationPluginMock } from '../../../../../../navigation/public/mocks'; +import { dataPluginMock } from '../../../../../../data/public/mocks'; +import { initAngularBootstrap } from '../../../../../../kibana_legacy/public'; describe('debounce service', function() { - let spy; - beforeEach(function() { + let debounce: (fn: () => void, timeout: number, options?: any) => any; + let debounceFromProvider: (fn: () => void, timeout: number, options?: any) => any; + let $timeout: ITimeoutService; + let spy: SinonSpy; + + beforeEach(() => { spy = sinon.spy(); - init(); + + initAngularBootstrap(); + + initializeInnerAngularModule( + 'app/discover', + coreMock.createStart(), + navigationPluginMock.createStartContract(), + dataPluginMock.createStartContract() + ); + + angular.mock.module('app/discover'); + + angular.mock.inject( + ($injector: auto.IInjectorService, _$timeout_: ITimeoutService, Private: any) => { + $timeout = _$timeout_; + + debounce = $injector.get('debounce'); + debounceFromProvider = Private(DebounceProvider); + } + ); }); it('should have a cancel method', function() { const bouncer = debounce(() => {}, 100); const bouncerFromProvider = debounceFromProvider(() => {}, 100); - expect(bouncer).to.have.property('cancel'); - expect(bouncerFromProvider).to.have.property('cancel'); + expect(bouncer).toHaveProperty('cancel'); + expect(bouncerFromProvider).toHaveProperty('cancel'); }); describe('delayed execution', function() { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/debounce/index.js b/src/plugins/discover/public/application/angular/directives/debounce/index.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/debounce/index.js rename to src/plugins/discover/public/application/angular/directives/debounce/index.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/fixed_scroll.js b/src/plugins/discover/public/application/angular/directives/fixed_scroll.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/fixed_scroll.js rename to src/plugins/discover/public/application/angular/directives/fixed_scroll.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/histogram.tsx b/src/plugins/discover/public/application/angular/directives/histogram.tsx similarity index 97% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/histogram.tsx rename to src/plugins/discover/public/application/angular/directives/histogram.tsx index 8c55622e4c604..d856be58958f1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/histogram.tsx +++ b/src/plugins/discover/public/application/angular/directives/histogram.tsx @@ -39,6 +39,7 @@ import { TooltipType, ElementClickListener, XYChartElementEvent, + BrushEndListener, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; @@ -143,13 +144,12 @@ export class DiscoverHistogram extends Component { - const range = { - from: min, - to: max, - }; - - this.props.timefilterUpdateHandler(range); + public onBrushEnd: BrushEndListener = ({ x }) => { + if (!x) { + return; + } + const [from, to] = x; + this.props.timefilterUpdateHandler({ from, to }); }; public onElementClick = (xInterval: number): ElementClickListener => ([elementData]) => { @@ -175,7 +175,7 @@ export class DiscoverHistogram extends Component

@@ -114,14 +114,14 @@ export class DiscoverNoResults extends Component {

@@ -155,7 +155,7 @@ export class DiscoverNoResults extends Component { @@ -168,7 +168,7 @@ export class DiscoverNoResults extends Component { @@ -181,7 +181,7 @@ export class DiscoverNoResults extends Component { @@ -194,7 +194,7 @@ export class DiscoverNoResults extends Component { @@ -210,14 +210,14 @@ export class DiscoverNoResults extends Component {

@@ -256,7 +256,7 @@ export class DiscoverNoResults extends Component { } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/no_results.test.js b/src/plugins/discover/public/application/angular/directives/no_results.test.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/no_results.test.js rename to src/plugins/discover/public/application/angular/directives/no_results.test.js diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/render_complete.ts b/src/plugins/discover/public/application/angular/directives/render_complete.ts similarity index 92% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/render_complete.ts rename to src/plugins/discover/public/application/angular/directives/render_complete.ts index 7757deb806a18..635cf68f12fcb 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/render_complete.ts +++ b/src/plugins/discover/public/application/angular/directives/render_complete.ts @@ -17,7 +17,7 @@ * under the License. */ import { IScope } from 'angular'; -import { RenderCompleteHelper } from '../../../../../../../../plugins/kibana_utils/public'; +import { RenderCompleteHelper } from '../../../../../kibana_utils/public'; export function createRenderCompleteDirective() { return { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/uninitialized.tsx b/src/plugins/discover/public/application/angular/directives/uninitialized.tsx similarity index 92% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/uninitialized.tsx rename to src/plugins/discover/public/application/angular/directives/uninitialized.tsx index b308607bbfbb0..d04aea0933115 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/uninitialized.tsx +++ b/src/plugins/discover/public/application/angular/directives/uninitialized.tsx @@ -37,7 +37,7 @@ export const DiscoverUninitialized = ({ onRefresh }: Props) => { title={

@@ -45,7 +45,7 @@ export const DiscoverUninitialized = ({ onRefresh }: Props) => { body={

@@ -53,7 +53,7 @@ export const DiscoverUninitialized = ({ onRefresh }: Props) => { actions={ diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html b/src/plugins/discover/public/application/angular/discover.html similarity index 89% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html rename to src/plugins/discover/public/application/angular/discover.html index 1221b01657e45..b4db89b9275b4 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html +++ b/src/plugins/discover/public/application/angular/discover.html @@ -61,7 +61,7 @@

{{screenTitle}}

@@ -83,7 +83,7 @@

{{screenTitle}}

@@ -93,7 +93,7 @@

{{screenTitle}}

{{(hits || 0) | number:0}} @@ -104,12 +104,12 @@

{{screenTitle}}

id="reload_saved_search" ng-click="resetQuery()" > - {{::'kbn.discover.reloadSavedSearchButton' | i18n: {defaultMessage: 'Reset search'} }} + {{::'discover.reloadSavedSearchButton' | i18n: {defaultMessage: 'Reset search'} }}
@@ -117,7 +117,7 @@

{{screenTitle}}

@@ -141,7 +141,7 @@

{{screenTitle}}

> {{screenTitle}} >

{{screenTitle}} class="dscTable__footer" > {{screenTitle}}
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js similarity index 93% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js rename to src/plugins/discover/public/application/angular/discover.js index c1de704d1c00a..2afd0322f8701 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -26,11 +26,8 @@ import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; import { getState, splitState } from './discover_state'; -import { RequestAdapter } from '../../../../../../../plugins/inspector/public'; -import { - SavedObjectSaveModal, - showSaveModal, -} from '../../../../../../../plugins/saved_objects/public'; +import { RequestAdapter } from '../../../../inspector/public'; +import { SavedObjectSaveModal, showSaveModal } from '../../../../saved_objects/public'; import { getSortArray, getSortForSearchSource } from './doc_table'; import * as columnActions from './doc_table/actions/columns'; @@ -75,9 +72,9 @@ import { syncQueryStateWithUrl, getDefaultQuery, search, -} from '../../../../../../../plugins/data/public'; +} from '../../../../data/public'; import { getIndexPatternId } from '../helpers/get_index_pattern_id'; -import { addFatalError } from '../../../../../../../plugins/kibana_legacy/public'; +import { addFatalError } from '../../../../kibana_legacy/public'; const fetchStatuses = { UNINITIALIZED: 'uninitialized', @@ -99,10 +96,10 @@ app.config($routeProvider => { } return { - text: i18n.translate('kbn.discover.badge.readOnly.text', { + text: i18n.translate('discover.badge.readOnly.text', { defaultMessage: 'Read only', }), - tooltip: i18n.translate('kbn.discover.badge.readOnly.tooltip', { + tooltip: i18n.translate('discover.badge.readOnly.tooltip', { defaultMessage: 'Unable to save searches', }), iconType: 'glasses', @@ -333,10 +330,10 @@ function discoverController( const getTopNavLinks = () => { const newSearch = { id: 'new', - label: i18n.translate('kbn.discover.localMenu.localMenu.newSearchTitle', { + label: i18n.translate('discover.localMenu.localMenu.newSearchTitle', { defaultMessage: 'New', }), - description: i18n.translate('kbn.discover.localMenu.newSearchDescription', { + description: i18n.translate('discover.localMenu.newSearchDescription', { defaultMessage: 'New Search', }), run: function() { @@ -349,10 +346,10 @@ function discoverController( const saveSearch = { id: 'save', - label: i18n.translate('kbn.discover.localMenu.saveTitle', { + label: i18n.translate('discover.localMenu.saveTitle', { defaultMessage: 'Save', }), - description: i18n.translate('kbn.discover.localMenu.saveSearchDescription', { + description: i18n.translate('discover.localMenu.saveSearchDescription', { defaultMessage: 'Save Search', }), testId: 'discoverSaveButton', @@ -389,7 +386,7 @@ function discoverController( title={savedSearch.title} showCopyOnSave={!!savedSearch.id} objectType="search" - description={i18n.translate('kbn.discover.localMenu.saveSaveSearchDescription', { + description={i18n.translate('discover.localMenu.saveSaveSearchDescription', { defaultMessage: 'Save your Discover search so you can use it in visualizations and dashboards', })} @@ -402,10 +399,10 @@ function discoverController( const openSearch = { id: 'open', - label: i18n.translate('kbn.discover.localMenu.openTitle', { + label: i18n.translate('discover.localMenu.openTitle', { defaultMessage: 'Open', }), - description: i18n.translate('kbn.discover.localMenu.openSavedSearchDescription', { + description: i18n.translate('discover.localMenu.openSavedSearchDescription', { defaultMessage: 'Open Saved Search', }), testId: 'discoverOpenButton', @@ -419,10 +416,10 @@ function discoverController( const shareSearch = { id: 'share', - label: i18n.translate('kbn.discover.localMenu.shareTitle', { + label: i18n.translate('discover.localMenu.shareTitle', { defaultMessage: 'Share', }), - description: i18n.translate('kbn.discover.localMenu.shareSearchDescription', { + description: i18n.translate('discover.localMenu.shareSearchDescription', { defaultMessage: 'Share Search', }), testId: 'shareTopNavButton', @@ -446,10 +443,10 @@ function discoverController( const inspectSearch = { id: 'inspect', - label: i18n.translate('kbn.discover.localMenu.inspectTitle', { + label: i18n.translate('discover.localMenu.inspectTitle', { defaultMessage: 'Inspect', }), - description: i18n.translate('kbn.discover.localMenu.openInspectorForSearchDescription', { + description: i18n.translate('discover.localMenu.openInspectorForSearchDescription', { defaultMessage: 'Open Inspector for search', }), testId: 'openInspectorButton', @@ -492,7 +489,7 @@ function discoverController( const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : ''; chrome.docTitle.change(`Discover${pageTitleSuffix}`); - const discoverBreadcrumbsTitle = i18n.translate('kbn.discover.discoverBreadcrumbTitle', { + const discoverBreadcrumbsTitle = i18n.translate('discover.discoverBreadcrumbTitle', { defaultMessage: 'Discover', }); @@ -606,16 +603,16 @@ function discoverController( $scope.state.sort = getSortArray($scope.state.sort, $scope.indexPattern); $scope.getBucketIntervalToolTipText = () => { - return i18n.translate('kbn.discover.bucketIntervalTooltip', { + return i18n.translate('discover.bucketIntervalTooltip', { defaultMessage: 'This interval creates {bucketsDescription} to show in the selected time range, so it has been scaled to {bucketIntervalDescription}', values: { bucketsDescription: $scope.bucketInterval.scale > 1 - ? i18n.translate('kbn.discover.bucketIntervalTooltip.tooLargeBucketsText', { + ? i18n.translate('discover.bucketIntervalTooltip.tooLargeBucketsText', { defaultMessage: 'buckets that are too large', }) - : i18n.translate('kbn.discover.bucketIntervalTooltip.tooManyBucketsText', { + : i18n.translate('discover.bucketIntervalTooltip.tooManyBucketsText', { defaultMessage: 'too many buckets', }), bucketIntervalDescription: $scope.bucketInterval.description, @@ -748,7 +745,7 @@ function discoverController( $scope.$evalAsync(() => { if (id) { toastNotifications.addSuccess({ - title: i18n.translate('kbn.discover.notifications.savedSearchTitle', { + title: i18n.translate('discover.notifications.savedSearchTitle', { defaultMessage: `Search '{savedSearchTitle}' was saved`, values: { savedSearchTitle: savedSearch.title, @@ -769,7 +766,7 @@ function discoverController( return { id }; } catch (saveError) { toastNotifications.addDanger({ - title: i18n.translate('kbn.discover.notifications.notSavedSearchTitle', { + title: i18n.translate('discover.notifications.notSavedSearchTitle', { defaultMessage: `Search '{savedSearchTitle}' was not saved.`, values: { savedSearchTitle: savedSearch.title, @@ -812,7 +809,7 @@ function discoverController( $scope.fetchError = fetchError; } else { toastNotifications.addError(error, { - title: i18n.translate('kbn.discover.errorLoadingData', { + title: i18n.translate('discover.errorLoadingData', { defaultMessage: 'Error loading data', }), toastMessage: error.shortMessage || error.body?.message, @@ -903,10 +900,10 @@ function discoverController( function logInspectorRequest() { inspectorAdapters.requests.reset(); - const title = i18n.translate('kbn.discover.inspectorRequestDataTitle', { + const title = i18n.translate('discover.inspectorRequestDataTitle', { defaultMessage: 'data', }); - const description = i18n.translate('kbn.discover.inspectorRequestDescription', { + const description = i18n.translate('discover.inspectorRequestDescription', { defaultMessage: 'This request queries Elasticsearch to fetch the data for the search.', }); inspectorRequest = inspectorAdapters.requests.start(title, { description }); @@ -1049,7 +1046,7 @@ function discoverController( } function getIndexPatternWarning(index) { - return i18n.translate('kbn.discover.valueIsNotConfiguredIndexPatternIDWarningTitle', { + return i18n.translate('discover.valueIsNotConfiguredIndexPatternIDWarningTitle', { defaultMessage: '{stateVal} is not a configured index pattern ID', values: { stateVal: `"${index}"`, @@ -1076,7 +1073,7 @@ function discoverController( if (ownIndexPattern) { toastNotifications.addWarning({ title: warningTitle, - text: i18n.translate('kbn.discover.showingSavedIndexPatternWarningDescription', { + text: i18n.translate('discover.showingSavedIndexPatternWarningDescription', { defaultMessage: 'Showing the saved index pattern: "{ownIndexPatternTitle}" ({ownIndexPatternId})', values: { @@ -1090,7 +1087,7 @@ function discoverController( toastNotifications.addWarning({ title: warningTitle, - text: i18n.translate('kbn.discover.showingDefaultIndexPatternWarningDescription', { + text: i18n.translate('discover.showingDefaultIndexPatternWarningDescription', { defaultMessage: 'Showing the default index pattern: "{loadedIndexPatternTitle}" ({loadedIndexPatternId})', values: { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts b/src/plugins/discover/public/application/angular/discover_state.test.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts rename to src/plugins/discover/public/application/angular/discover_state.test.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts b/src/plugins/discover/public/application/angular/discover_state.ts similarity index 96% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts rename to src/plugins/discover/public/application/angular/discover_state.ts index 2a036f0ac60ad..46500d9fdf85e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts +++ b/src/plugins/discover/public/application/angular/discover_state.ts @@ -24,9 +24,9 @@ import { syncState, ReduxLikeStateContainer, IKbnUrlStateStorage, -} from '../../../../../../../plugins/kibana_utils/public'; -import { esFilters, Filter, Query } from '../../../../../../../plugins/data/public'; -import { migrateLegacyQuery } from '../../../../../../../plugins/kibana_legacy/public'; +} from '../../../../kibana_utils/public'; +import { esFilters, Filter, Query } from '../../../../data/public'; +import { migrateLegacyQuery } from '../../../../kibana_legacy/public'; export interface AppState { /** diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.html b/src/plugins/discover/public/application/angular/doc.html similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.html rename to src/plugins/discover/public/application/angular/doc.html diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts b/src/plugins/discover/public/application/angular/doc.ts similarity index 91% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts rename to src/plugins/discover/public/application/angular/doc.ts index 092e3c79b1007..2d0d45e5003fb 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts +++ b/src/plugins/discover/public/application/angular/doc.ts @@ -49,7 +49,9 @@ app.config(($routeProvider: any) => { }) // the new route, es 7 deprecated types, es 8 removed them .when('/discover/doc/:indexPattern/:index', { - controller: ($scope: LazyScope, $route: any, es: any) => { + // have to be written as function expression, because it's not compiled in dev mode + // eslint-disable-next-line object-shorthand + controller: function($scope: LazyScope, $route: any, es: any) { timefilter.disableAutoRefreshSelector(); timefilter.disableTimeRangeSelector(); $scope.esClient = es; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/_doc_table.scss b/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/_doc_table.scss rename to src/plugins/discover/public/application/angular/doc_table/_doc_table.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/_index.scss b/src/plugins/discover/public/application/angular/doc_table/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/_index.scss rename to src/plugins/discover/public/application/angular/doc_table/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/actions/columns.ts b/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/actions/columns.ts rename to src/plugins/discover/public/application/angular/doc_table/actions/columns.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/_index.scss b/src/plugins/discover/public/application/angular/doc_table/components/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/_index.scss rename to src/plugins/discover/public/application/angular/doc_table/components/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/_table_header.scss b/src/plugins/discover/public/application/angular/doc_table/components/_table_header.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/_table_header.scss rename to src/plugins/discover/public/application/angular/doc_table/components/_table_header.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap b/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap rename to src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap b/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap rename to src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/index.ts b/src/plugins/discover/public/application/angular/doc_table/components/pager/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/index.ts rename to src/plugins/discover/public/application/angular/doc_table/components/pager/index.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx similarity index 92% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx index 6f1cf81e2c541..e95153d85b064 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx @@ -35,7 +35,7 @@ export function ToolBarPagerButtons(props: Props) { disabled={!props.hasPreviousPage} data-test-subj="btnPrevPage" aria-label={i18n.translate( - 'kbn.discover.docTable.pager.toolbarPagerButtons.previousButtonAriaLabel', + 'discover.docTable.pager.toolbarPagerButtons.previousButtonAriaLabel', { defaultMessage: 'Previous page in table', } @@ -49,7 +49,7 @@ export function ToolBarPagerButtons(props: Props) { disabled={!props.hasNextPage} data-test-subj="btnNextPage" aria-label={i18n.translate( - 'kbn.discover.docTable.pager.toolbarPagerButtons.nextButtonAriaLabel', + 'discover.docTable.pager.toolbarPagerButtons.nextButtonAriaLabel', { defaultMessage: 'Next page in table', } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.test.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.test.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx similarity index 95% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx index 84338d817c86b..46e3cd9511eb5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx @@ -30,7 +30,7 @@ export function ToolBarPagerText({ startItem, endItem, totalItems }: Props) {
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header.ts rename to src/plugins/discover/public/application/angular/doc_table/components/table_header.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap b/src/plugins/discover/public/application/angular/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap rename to src/plugins/discover/public/application/angular/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/helpers.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/table_header/helpers.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header.test.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx similarity index 99% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header.test.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx index 89f73022627c5..b201bea26503e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header.test.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx @@ -25,8 +25,6 @@ import { findTestSubject } from '@elastic/eui/lib/test'; import { SortOrder } from './helpers'; import { IndexPattern, IFieldType } from '../../../../../kibana_services'; -jest.mock('ui/new_platform'); - function getMockIndexPattern() { return ({ id: 'test', diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header_column.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header_column.tsx similarity index 88% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header_column.tsx rename to src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header_column.tsx index d1a9a5146fb8a..4c09ff8701f30 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/table_header_column.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header_column.tsx @@ -80,21 +80,21 @@ export function TableHeaderColumn({ const getSortButtonAriaLabel = () => { const sortAscendingMessage = i18n.translate( - 'kbn.docTable.tableHeader.sortByColumnAscendingAriaLabel', + 'discover.docTable.tableHeader.sortByColumnAscendingAriaLabel', { defaultMessage: 'Sort {columnName} ascending', values: { columnName: name }, } ); const sortDescendingMessage = i18n.translate( - 'kbn.docTable.tableHeader.sortByColumnDescendingAriaLabel', + 'discover.docTable.tableHeader.sortByColumnDescendingAriaLabel', { defaultMessage: 'Sort {columnName} descending', values: { columnName: name }, } ); const stopSortingMessage = i18n.translate( - 'kbn.docTable.tableHeader.sortByColumnUnsortedAriaLabel', + 'discover.docTable.tableHeader.sortByColumnUnsortedAriaLabel', { defaultMessage: 'Stop sorting on {columnName}', values: { columnName: name }, @@ -126,42 +126,42 @@ export function TableHeaderColumn({ // Remove Button { active: isRemoveable && typeof onRemoveColumn === 'function', - ariaLabel: i18n.translate('kbn.docTable.tableHeader.removeColumnButtonAriaLabel', { + ariaLabel: i18n.translate('discover.docTable.tableHeader.removeColumnButtonAriaLabel', { defaultMessage: 'Remove {columnName} column', values: { columnName: name }, }), className: 'fa fa-remove kbnDocTableHeader__move', onClick: () => onRemoveColumn && onRemoveColumn(name), testSubject: `docTableRemoveHeader-${name}`, - tooltip: i18n.translate('kbn.docTable.tableHeader.removeColumnButtonTooltip', { + tooltip: i18n.translate('discover.docTable.tableHeader.removeColumnButtonTooltip', { defaultMessage: 'Remove Column', }), }, // Move Left Button { active: colLeftIdx >= 0 && typeof onMoveColumn === 'function', - ariaLabel: i18n.translate('kbn.docTable.tableHeader.moveColumnLeftButtonAriaLabel', { + ariaLabel: i18n.translate('discover.docTable.tableHeader.moveColumnLeftButtonAriaLabel', { defaultMessage: 'Move {columnName} column to the left', values: { columnName: name }, }), className: 'fa fa-angle-double-left kbnDocTableHeader__move', onClick: () => onMoveColumn && onMoveColumn(name, colLeftIdx), testSubject: `docTableMoveLeftHeader-${name}`, - tooltip: i18n.translate('kbn.docTable.tableHeader.moveColumnLeftButtonTooltip', { + tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnLeftButtonTooltip', { defaultMessage: 'Move column to the left', }), }, // Move Right Button { active: colRightIdx >= 0 && typeof onMoveColumn === 'function', - ariaLabel: i18n.translate('kbn.docTable.tableHeader.moveColumnRightButtonAriaLabel', { + ariaLabel: i18n.translate('discover.docTable.tableHeader.moveColumnRightButtonAriaLabel', { defaultMessage: 'Move {columnName} column to the right', values: { columnName: name }, }), className: 'fa fa-angle-double-right kbnDocTableHeader__move', onClick: () => onMoveColumn && onMoveColumn(name, colRightIdx), testSubject: `docTableMoveRightHeader-${name}`, - tooltip: i18n.translate('kbn.docTable.tableHeader.moveColumnRightButtonTooltip', { + tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnRightButtonTooltip', { defaultMessage: 'Move column to the right', }), }, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts similarity index 96% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row.ts rename to src/plugins/discover/public/application/angular/doc_table/components/table_row.ts index 698bfe7416d42..3b48c4c79365e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts @@ -23,15 +23,15 @@ import $ from 'jquery'; import rison from 'rison-node'; import '../../doc_viewer'; // @ts-ignore -import { noWhiteSpace } from '../../../../../../common/utils/no_white_space'; +import { noWhiteSpace } from '../../../../../../../legacy/core_plugins/kibana/common/utils/no_white_space'; import openRowHtml from './table_row/open.html'; import detailsHtml from './table_row/details.html'; -import { dispatchRenderComplete } from '../../../../../../../../../plugins/kibana_utils/public'; +import { dispatchRenderComplete } from '../../../../../../kibana_utils/public'; import cellTemplateHtml from '../components/table_row/cell.html'; import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html'; -import { esFilters } from '../../../../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../data/public'; import { getServices } from '../../../../kibana_services'; // guesstimate at the minimum number of chars wide cells in the table should be diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_cell.scss b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_cell.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_cell.scss rename to src/plugins/discover/public/application/angular/doc_table/components/table_row/_cell.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_details.scss b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_details.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_details.scss rename to src/plugins/discover/public/application/angular/doc_table/components/table_row/_details.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_index.scss b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_index.scss rename to src/plugins/discover/public/application/angular/doc_table/components/table_row/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_open.scss b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_open.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/_open.scss rename to src/plugins/discover/public/application/angular/doc_table/components/table_row/_open.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/cell.html b/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.html similarity index 66% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/cell.html rename to src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.html index 0704016a52bbd..e8c4fceeca7ff 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/cell.html +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.html @@ -18,17 +18,17 @@ data-column="<%- column %>" tooltip-append-to-body="1" data-test-subj="docTableCellFilter" - tooltip="{{ ::'kbn.docTable.tableRow.filterForValueButtonTooltip' | i18n: {defaultMessage: 'Filter for value'} }}" + tooltip="{{ ::'discover.docTable.tableRow.filterForValueButtonTooltip' | i18n: {defaultMessage: 'Filter for value'} }}" tooltip-placement="bottom" - aria-label="{{ ::'kbn.docTable.tableRow.filterForValueButtonAriaLabel' | i18n: {defaultMessage: 'Filter for value'} }}" + aria-label="{{ ::'discover.docTable.tableRow.filterForValueButtonAriaLabel' | i18n: {defaultMessage: 'Filter for value'} }}" > diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/details.html b/src/plugins/discover/public/application/angular/doc_table/components/table_row/details.html similarity index 89% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/details.html rename to src/plugins/discover/public/application/angular/doc_table/components/table_row/details.html index d149a9023816a..37ae08246d1d3 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/details.html +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/details.html @@ -8,7 +8,7 @@

@@ -22,7 +22,7 @@ data-test-subj="docTableRowAction" ng-href="{{ getContextAppHref() }}" ng-if="indexPattern.isTimeBased()" - i18n-id="kbn.docTable.tableRow.viewSurroundingDocumentsLinkText" + i18n-id="discover.docTable.tableRow.viewSurroundingDocumentsLinkText" i18n-default-message="View surrounding documents" >
@@ -31,7 +31,7 @@ class="euiLink" data-test-subj="docTableRowAction" ng-href="#/discover/doc/{{indexPattern.id}}/{{row._index}}?id={{uriEncodedId}}" - i18n-id="kbn.docTable.tableRow.viewSingleDocumentLinkText" + i18n-id="discover.docTable.tableRow.viewSingleDocumentLinkText" i18n-default-message="View single document" >
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/open.html b/src/plugins/discover/public/application/angular/doc_table/components/table_row/open.html similarity index 72% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/open.html rename to src/plugins/discover/public/application/angular/doc_table/components/table_row/open.html index d6c4b858d2b47..6a14b6fc70348 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_row/open.html +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/open.html @@ -2,7 +2,7 @@