From 82cad2cb90730ac8b5fcf492eab27f3a69dbbc60 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Wed, 30 Sep 2020 12:01:32 -0700 Subject: [PATCH 01/36] [Reporting] API Integration tests: fix flaky tests for Spaces CSV formatting (#78849) Co-authored-by: Elastic Machine --- .../reporting_and_security/spaces.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts index b216f137e27b5d..80ba3215ab96fb 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts @@ -62,7 +62,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should use formats from non-default spaces', async () => { - setSpaceConfig('non_default_space', { + await setSpaceConfig('non_default_space', { 'csv:separator': ';', 'csv:quoteValues': false, 'dateFormat:tz': 'US/Alaska', @@ -82,7 +82,10 @@ export default function ({ getService }: FtrProviderContext) { it(`should use browserTimezone in jobParams for date formatting`, async () => { const tzParam = 'America/Phoenix'; const tzSettings = 'Browser'; - setSpaceConfig('non_default_space', { 'csv:separator': ';', 'dateFormat:tz': tzSettings }); + await setSpaceConfig('non_default_space', { + 'csv:separator': ';', + 'dateFormat:tz': tzSettings, + }); const path = await reportingAPI.postJobJSON(`/api/reporting/generate/csv`, { jobParams: `(browserTimezone:${tzParam},conflictedTypesFields:!(),fields:!(order_date,category,customer_full_name,taxful_total_price,currency),indexPatternId:aac3e500-f2c7-11ea-8250-fb138aa491e7,metaFields:!(_source,_id,_type,_index,_score),objectType:search,searchRequest:(body:(_source:(includes:!(order_date,category,customer_full_name,taxful_total_price,currency)),docvalue_fields:!((field:order_date,format:date_time)),query:(bool:(filter:!((match_all:()),(range:(order_date:(format:strict_date_optional_time,gte:'2019-05-30T05:09:59.743Z',lte:'2019-07-26T08:47:09.682Z')))),must:!(),must_not:!(),should:!())),script_fields:(),sort:!((order_date:(order:desc,unmapped_type:boolean))),stored_fields:!(order_date,category,customer_full_name,taxful_total_price,currency),version:!t),index:'ec*'),title:'EC SEARCH from DEFAULT')`, }); From c9c30b0cb1da92782f74228ba43af0a4ac7a6309 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 30 Sep 2020 12:15:14 -0700 Subject: [PATCH 02/36] [security solution] only import beat_schema when needed (#78708) Co-authored-by: spalger Co-authored-by: Elastic Machine --- .../index_fields/index.test.ts | 4 ++++ .../search_strategy/index_fields/index.ts | 20 +++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts index 5a219304cea18b..04e81e4c934b31 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts @@ -8,6 +8,7 @@ import { sortBy } from 'lodash/fp'; import { formatIndexFields, formatFirstFields, formatSecondFields, createFieldItem } from './index'; import { mockAuditbeatIndexField, mockFilebeatIndexField, mockPacketbeatIndexField } from './mock'; +import { fieldsBeat as beatFields } from '../../utils/beat_schema/fields'; describe('Index Fields', () => { describe('formatIndexFields', () => { @@ -16,6 +17,7 @@ describe('Index Fields', () => { sortBy( 'name', await formatIndexFields( + beatFields, [mockAuditbeatIndexField, mockFilebeatIndexField, mockPacketbeatIndexField], ['auditbeat', 'filebeat', 'packetbeat'] ) @@ -167,6 +169,7 @@ describe('Index Fields', () => { describe('formatFirstFields', () => { test('Basic functionality', async () => { const fields = await formatFirstFields( + beatFields, [mockAuditbeatIndexField, mockFilebeatIndexField, mockPacketbeatIndexField], ['auditbeat', 'filebeat', 'packetbeat'] ); @@ -749,6 +752,7 @@ describe('Index Fields', () => { describe('createFieldItem', () => { test('Basic functionality', () => { const item = createFieldItem( + beatFields, ['auditbeat'], { name: '_id', diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts index 71b641237d6b01..da29cae0eebebc 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts @@ -12,14 +12,18 @@ import { IndexFieldsStrategyResponse, IndexField, IndexFieldsStrategyRequest, + BeatFields, } from '../../../common/search_strategy/index_fields'; -import { fieldsBeat } from '../../utils/beat_schema/fields'; - export const securitySolutionIndexFieldsProvider = (): ISearchStrategy< IndexFieldsStrategyRequest, IndexFieldsStrategyResponse > => { + // require the fields once we actually need them, rather than ahead of time, and pass + // them to createFieldItem to reduce the amount of work done as much as possible + // eslint-disable-next-line @typescript-eslint/no-var-requires + const beatFields: BeatFields = require('../../utils/beat_schema/fields').fieldsBeat; + return { search: async (context, request) => { const { elasticsearch } = context.core; @@ -41,6 +45,7 @@ export const securitySolutionIndexFieldsProvider = (): ISearchStrategy< if (!request.onlyCheckIfIndicesExist) { indexFields = await formatIndexFields( + beatFields, responsesIndexFields.filter((rif) => rif !== false) as FieldDescriptor[][], dedupeIndices ); @@ -116,6 +121,7 @@ const missingFields: FieldDescriptor[] = [ * @param indexesAliasIdx The index within the alias */ export const createFieldItem = ( + beatFields: BeatFields, indexesAlias: string[], index: FieldDescriptor, indexesAliasIdx: number @@ -126,7 +132,7 @@ export const createFieldItem = ( splitIndexName[splitIndexName.length - 1] === 'text' ? splitIndexName.slice(0, splitIndexName.length - 1).join('.') : index.name; - const beatIndex = fieldsBeat[indexName] ?? {}; + const beatIndex = beatFields[indexName] ?? {}; if (isEmpty(beatIndex.category)) { beatIndex.category = splitIndexName[0]; } @@ -151,6 +157,7 @@ export const createFieldItem = ( * @param indexesAlias The index aliases such as filebeat-* */ export const formatFirstFields = async ( + beatFields: BeatFields, responsesIndexFields: FieldDescriptor[][], indexesAlias: string[] ): Promise => { @@ -160,11 +167,11 @@ export const formatFirstFields = async ( responsesIndexFields.reduce( (accumulator: IndexField[], indexFields: FieldDescriptor[], indexesAliasIdx: number) => { missingFields.forEach((index) => { - const item = createFieldItem(indexesAlias, index, indexesAliasIdx); + const item = createFieldItem(beatFields, indexesAlias, index, indexesAliasIdx); accumulator.push(item); }); indexFields.forEach((index) => { - const item = createFieldItem(indexesAlias, index, indexesAliasIdx); + const item = createFieldItem(beatFields, indexesAlias, index, indexesAliasIdx); accumulator.push(item); }); return accumulator; @@ -224,10 +231,11 @@ export const formatSecondFields = async (fields: IndexField[]): Promise => { - const fields = await formatFirstFields(responsesIndexFields, indexesAlias); + const fields = await formatFirstFields(beatFields, responsesIndexFields, indexesAlias); const secondFields = await formatSecondFields(fields); return secondFields; }; From 90a6d2924ed3e20a4a44c2fb8ebb5875efee12f7 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Wed, 30 Sep 2020 21:19:06 +0200 Subject: [PATCH 03/36] Embeddables: basic documentation (#78900) --- docs/developer/plugin-list.asciidoc | 5 +- .../plugins/embeddable/public/index.md | 12 + ...gins-embeddable-public.action_add_panel.md | 11 + ...ins-embeddable-public.action_edit_panel.md | 11 + ...ugin-plugins-embeddable-public.adapters.md | 13 + ...ble-public.addpanelaction._constructor_.md | 24 + ...mbeddable-public.addpanelaction.execute.md | 22 + ...le-public.addpanelaction.getdisplayname.md | 15 + ...dable-public.addpanelaction.geticontype.md | 15 + ...ins-embeddable-public.addpanelaction.id.md | 11 + ...able-public.addpanelaction.iscompatible.md | 22 + ...lugins-embeddable-public.addpanelaction.md | 34 + ...s-embeddable-public.addpanelaction.type.md | 11 + ...ns-embeddable-public.chartactioncontext.md | 11 + ...beddable-public.container._constructor_.md | 23 + ...dable-public.container.addnewembeddable.md | 23 + ...ns-embeddable-public.container.children.md | 13 + ...le-public.container.createnewpanelstate.md | 23 + ...ins-embeddable-public.container.destroy.md | 15 + ...ns-embeddable-public.container.getchild.md | 22 + ...embeddable-public.container.getchildids.md | 15 + ...-embeddable-public.container.getfactory.md | 11 + ...able-public.container.getinheritedinput.md | 24 + ...dable-public.container.getinputforchild.md | 22 + ...beddable-public.container.getpanelstate.md | 22 + ...embeddable-public.container.iscontainer.md | 11 + ...gin-plugins-embeddable-public.container.md | 43 + ...gins-embeddable-public.container.reload.md | 15 + ...dable-public.container.removeembeddable.md | 22 + ...-public.container.untilembeddableloaded.md | 22 + ...le-public.container.updateinputforchild.md | 23 + ...e-public.containerinput.hidepaneltitles.md | 11 + ...lugins-embeddable-public.containerinput.md | 19 + ...embeddable-public.containerinput.panels.md | 15 + ...public.containeroutput.embeddableloaded.md | 13 + ...ugins-embeddable-public.containeroutput.md | 18 + ...-embeddable-public.context_menu_trigger.md | 11 + ...ns-embeddable-public.contextmenutrigger.md | 11 + ...public.defaultembeddablefactoryprovider.md | 11 + ...le-public.editpanelaction._constructor_.md | 22 + ...ble-public.editpanelaction.currentappid.md | 11 + ...beddable-public.editpanelaction.execute.md | 22 + ...ble-public.editpanelaction.getapptarget.md | 22 + ...e-public.editpanelaction.getdisplayname.md | 22 + ...beddable-public.editpanelaction.gethref.md | 22 + ...able-public.editpanelaction.geticontype.md | 15 + ...ns-embeddable-public.editpanelaction.id.md | 11 + ...ble-public.editpanelaction.iscompatible.md | 22 + ...ugins-embeddable-public.editpanelaction.md | 38 + ...embeddable-public.editpanelaction.order.md | 11 + ...-embeddable-public.editpanelaction.type.md | 11 + ...eddable-public.embeddable._constructor_.md | 22 + ...ns-embeddable-public.embeddable.destroy.md | 17 + ...s-embeddable-public.embeddable.getinput.md | 15 + ...-embeddable-public.embeddable.getinput_.md | 15 + ...-public.embeddable.getinspectoradapters.md | 17 + ...ddable-public.embeddable.getiscontainer.md | 15 + ...-embeddable-public.embeddable.getoutput.md | 15 + ...embeddable-public.embeddable.getoutput_.md | 15 + ...ns-embeddable-public.embeddable.getroot.md | 17 + ...s-embeddable-public.embeddable.gettitle.md | 15 + ...plugins-embeddable-public.embeddable.id.md | 11 + ...gins-embeddable-public.embeddable.input.md | 11 + ...mbeddable-public.embeddable.iscontainer.md | 11 + ...in-plugins-embeddable-public.embeddable.md | 51 ++ ...ins-embeddable-public.embeddable.output.md | 11 + ...ins-embeddable-public.embeddable.parent.md | 11 + ...ins-embeddable-public.embeddable.reload.md | 17 + ...ins-embeddable-public.embeddable.render.md | 22 + ...ddable-public.embeddable.rendercomplete.md | 11 + ...-embeddable-public.embeddable.runtimeid.md | 11 + ...ble-public.embeddable.supportedtriggers.md | 15 + ...ugins-embeddable-public.embeddable.type.md | 11 + ...mbeddable-public.embeddable.updateinput.md | 22 + ...beddable-public.embeddable.updateoutput.md | 22 + ...blic.embeddablechildpanel._constructor_.md | 20 + ....embeddablechildpanel.componentdidmount.md | 15 + ...beddablechildpanel.componentwillunmount.md | 15 + ...-public.embeddablechildpanel.embeddable.md | 11 + ...-embeddable-public.embeddablechildpanel.md | 35 + ...ble-public.embeddablechildpanel.mounted.md | 11 + ...able-public.embeddablechildpanel.render.md | 15 + ...lic.embeddablechildpanelprops.classname.md | 11 + ...lic.embeddablechildpanelprops.container.md | 11 + ....embeddablechildpanelprops.embeddableid.md | 11 + ...ddable-public.embeddablechildpanelprops.md | 21 + ...mbeddablechildpanelprops.panelcomponent.md | 11 + ...ble-public.embeddablecontext.embeddable.md | 11 + ...ins-embeddable-public.embeddablecontext.md | 18 + ...blic.embeddableeditorstate.embeddableid.md | 11 + ...embeddable-public.embeddableeditorstate.md | 22 + ...ic.embeddableeditorstate.originatingapp.md | 11 + ...public.embeddableeditorstate.valueinput.md | 11 + ...e-public.embeddablefactory.cancreatenew.md | 17 + ...eddable-public.embeddablefactory.create.md | 27 + ...embeddablefactory.createfromsavedobject.md | 26 + ...ublic.embeddablefactory.getdefaultinput.md | 24 + ...public.embeddablefactory.getdisplayname.md | 17 + ...blic.embeddablefactory.getexplicitinput.md | 17 + ...ublic.embeddablefactory.iscontainertype.md | 13 + ...ble-public.embeddablefactory.iseditable.md | 13 + ...ins-embeddable-public.embeddablefactory.md | 34 + ...c.embeddablefactory.savedobjectmetadata.md | 11 + ...mbeddable-public.embeddablefactory.type.md | 11 + ...able-public.embeddablefactorydefinition.md | 11 + ...dablefactorynotfounderror._constructor_.md | 20 + ...lic.embeddablefactorynotfounderror.code.md | 11 + ...e-public.embeddablefactorynotfounderror.md | 24 + ...ugins-embeddable-public.embeddableinput.md | 23 + ...blic.embeddableinstanceconfiguration.id.md | 11 + ...-public.embeddableinstanceconfiguration.md | 19 + ...ableinstanceconfiguration.savedobjectid.md | 11 + ...le-public.embeddableoutput.defaulttitle.md | 11 + ...ddable-public.embeddableoutput.editable.md | 11 + ...eddable-public.embeddableoutput.editapp.md | 11 + ...ddable-public.embeddableoutput.editpath.md | 11 + ...eddable-public.embeddableoutput.editurl.md | 11 + ...mbeddable-public.embeddableoutput.error.md | 11 + ...eddable-public.embeddableoutput.loading.md | 11 + ...gins-embeddable-public.embeddableoutput.md | 26 + ...e-public.embeddableoutput.savedobjectid.md | 11 + ...mbeddable-public.embeddableoutput.title.md | 11 + ...lic.embeddablepackagestate.embeddableid.md | 11 + ...ble-public.embeddablepackagestate.input.md | 11 + ...mbeddable-public.embeddablepackagestate.md | 22 + ...able-public.embeddablepackagestate.type.md | 11 + ...le-public.embeddablepanel._constructor_.md | 20 + ...embeddablepanel.closemycontextmenupanel.md | 11 + ...ublic.embeddablepanel.componentdidmount.md | 15 + ...ic.embeddablepanel.componentwillunmount.md | 15 + ...ugins-embeddable-public.embeddablepanel.md | 35 + ...mbeddable-public.embeddablepanel.onblur.md | 11 + ...beddable-public.embeddablepanel.onfocus.md | 11 + ...mbeddable-public.embeddablepanel.render.md | 15 + ...beddablepanel.unsafe_componentwillmount.md | 15 + ...ns-embeddable-public.embeddablepanelhoc.md | 14 + ...ns-embeddable-public.embeddablerenderer.md | 32 + ...beddable-public.embeddablerendererprops.md | 13 + ...ble-public.embeddableroot._constructor_.md | 20 + ...public.embeddableroot.componentdidmount.md | 15 + ...ublic.embeddableroot.componentdidupdate.md | 22 + ...lugins-embeddable-public.embeddableroot.md | 27 + ...embeddable-public.embeddableroot.render.md | 15 + ...ic.embeddableroot.shouldcomponentupdate.md | 22 + ...ugins-embeddable-public.embeddablesetup.md | 20 + ...beddablesetup.registerembeddablefactory.md | 11 + ...lic.embeddablesetup.registerenhancement.md | 11 + ...etup.setcustomembeddablefactoryprovider.md | 11 + ...public.embeddablesetupdependencies.data.md | 11 + ...able-public.embeddablesetupdependencies.md | 19 + ...c.embeddablesetupdependencies.uiactions.md | 11 + ...-public.embeddablestart.embeddablepanel.md | 11 + ....embeddablestart.getembeddablefactories.md | 11 + ...ic.embeddablestart.getembeddablefactory.md | 11 + ...blic.embeddablestart.getembeddablepanel.md | 11 + ...public.embeddablestart.getstatetransfer.md | 11 + ...ugins-embeddable-public.embeddablestart.md | 22 + ...public.embeddablestartdependencies.data.md | 11 + ...c.embeddablestartdependencies.inspector.md | 11 + ...able-public.embeddablestartdependencies.md | 20 + ...c.embeddablestartdependencies.uiactions.md | 11 + ...c.embeddablestatetransfer._constructor_.md | 22 + ...mbeddablestatetransfer.getappnamefromid.md | 13 + ...blestatetransfer.getincomingeditorstate.md | 26 + ...tetransfer.getincomingembeddablepackage.md | 26 + ...beddable-public.embeddablestatetransfer.md | 35 + ...mbeddablestatetransfer.navigatetoeditor.md | 29 + ...ransfer.navigatetowithembeddablepackage.md | 29 + ...public.enhancementregistrydefinition.id.md | 11 + ...le-public.enhancementregistrydefinition.md | 18 + ...le-public.errorembeddable._constructor_.md | 22 + ...beddable-public.errorembeddable.destroy.md | 15 + ...embeddable-public.errorembeddable.error.md | 11 + ...ugins-embeddable-public.errorembeddable.md | 33 + ...mbeddable-public.errorembeddable.reload.md | 15 + ...mbeddable-public.errorembeddable.render.md | 22 + ...-embeddable-public.errorembeddable.type.md | 11 + ...able-public.icontainer.addnewembeddable.md | 25 + ...s-embeddable-public.icontainer.getchild.md | 24 + ...able-public.icontainer.getinputforchild.md | 24 + ...in-plugins-embeddable-public.icontainer.md | 23 + ...able-public.icontainer.removeembeddable.md | 24 + ...public.icontainer.untilembeddableloaded.md | 24 + ...e-public.icontainer.updateinputforchild.md | 25 + ...s-embeddable-public.iembeddable.destroy.md | 17 + ...eddable-public.iembeddable.enhancements.md | 13 + ...-embeddable-public.iembeddable.getinput.md | 21 + ...embeddable-public.iembeddable.getinput_.md | 17 + ...public.iembeddable.getinspectoradapters.md | 17 + ...dable-public.iembeddable.getiscontainer.md | 17 + ...embeddable-public.iembeddable.getoutput.md | 21 + ...mbeddable-public.iembeddable.getoutput_.md | 17 + ...s-embeddable-public.iembeddable.getroot.md | 17 + ...-embeddable-public.iembeddable.gettitle.md | 17 + ...lugins-embeddable-public.iembeddable.id.md | 13 + ...beddable-public.iembeddable.iscontainer.md | 13 + ...n-plugins-embeddable-public.iembeddable.md | 41 + ...ns-embeddable-public.iembeddable.parent.md | 13 + ...ns-embeddable-public.iembeddable.reload.md | 17 + ...ns-embeddable-public.iembeddable.render.md | 24 + ...embeddable-public.iembeddable.runtimeid.md | 13 + ...le-public.iembeddable.supportedtriggers.md | 17 + ...gins-embeddable-public.iembeddable.type.md | 13 + ...beddable-public.iembeddable.updateinput.md | 24 + ...ins-embeddable-public.iserrorembeddable.md | 22 + ...able-public.israngeselecttriggercontext.md | 11 + ...ble-public.isreferenceorvalueembeddable.md | 22 + ...ble-public.issavedobjectembeddableinput.md | 22 + ...dable-public.isvalueclicktriggercontext.md | 11 + ...kibana-plugin-plugins-embeddable-public.md | 95 ++ ...ns-embeddable-public.openaddpanelflyout.md | 29 + ...in-plugins-embeddable-public.outputspec.md | 11 + ...s-embeddable-public.panel_badge_trigger.md | 11 + ...dable-public.panel_notification_trigger.md | 11 + ...ins-embeddable-public.panelbadgetrigger.md | 11 + ...public.panelnotfounderror._constructor_.md | 13 + ...beddable-public.panelnotfounderror.code.md | 11 + ...ns-embeddable-public.panelnotfounderror.md | 24 + ...eddable-public.panelnotificationtrigger.md | 11 + ...eddable-public.panelstate.explicitinput.md | 13 + ...plugins-embeddable-public.panelstate.id.md | 11 + ...in-plugins-embeddable-public.panelstate.md | 24 + ...ugins-embeddable-public.panelstate.type.md | 11 + ...plugin-plugins-embeddable-public.plugin.md | 22 + ...beddable-public.propertyspec.accesspath.md | 11 + ...eddable-public.propertyspec.description.md | 11 + ...eddable-public.propertyspec.displayname.md | 11 + ...ugins-embeddable-public.propertyspec.id.md | 11 + ...-plugins-embeddable-public.propertyspec.md | 22 + ...ns-embeddable-public.propertyspec.value.md | 11 + ...beddable-public.rangeselectcontext.data.md | 16 + ...le-public.rangeselectcontext.embeddable.md | 11 + ...ns-embeddable-public.rangeselectcontext.md | 19 + ...enceorvalueembeddable.getinputasreftype.md | 13 + ...ceorvalueembeddable.getinputasvaluetype.md | 13 + ...ferenceorvalueembeddable.inputisreftype.md | 13 + ...dable-public.referenceorvalueembeddable.md | 22 + ...dable-public.savedobjectembeddableinput.md | 18 + ...avedobjectembeddableinput.savedobjectid.md | 11 + ...mbeddable-public.valueclickcontext.data.md | 20 + ...ble-public.valueclickcontext.embeddable.md | 11 + ...ins-embeddable-public.valueclickcontext.md | 19 + ...ugin-plugins-embeddable-public.viewmode.md | 19 + ...dable-public.withembeddablesubscription.md | 17 + .../plugins/embeddable/server/index.md | 12 + ...-server.embeddableregistrydefinition.id.md | 11 + ...ble-server.embeddableregistrydefinition.md | 18 + ...ugins-embeddable-server.embeddablesetup.md | 19 + ...beddablesetup.registerembeddablefactory.md | 11 + ...ver.embeddablesetup.registerenhancement.md | 11 + ...server.enhancementregistrydefinition.id.md | 11 + ...le-server.enhancementregistrydefinition.md | 18 + ...kibana-plugin-plugins-embeddable-server.md | 20 + ...plugin-plugins-embeddable-server.plugin.md | 11 + src/dev/run_check_published_api_changes.ts | 2 + src/plugins/embeddable/README.asciidoc | 44 + src/plugins/embeddable/README.md | 31 - src/plugins/embeddable/public/public.api.md | 843 ++++++++++++++++++ src/plugins/embeddable/server/server.api.md | 50 ++ 259 files changed, 5290 insertions(+), 33 deletions(-) create mode 100644 docs/development/plugins/embeddable/public/index.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_add_panel.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_edit_panel.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.execute.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.getdisplayname.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.geticontype.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.iscompatible.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.chartactioncontext.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.addnewembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.children.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.createnewpanelstate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.destroy.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchild.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchildids.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getfactory.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinheritedinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinputforchild.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getpanelstate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.iscontainer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.reload.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.removeembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.untilembeddableloaded.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.updateinputforchild.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.hidepaneltitles.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.panels.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.embeddableloaded.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.context_menu_trigger.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.contextmenutrigger.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.defaultembeddablefactoryprovider.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.currentappid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.execute.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getapptarget.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getdisplayname.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.gethref.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.geticontype.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.iscompatible.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.order.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.destroy.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinspectoradapters.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getiscontainer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getroot.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.input.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.iscontainer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.output.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.parent.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.reload.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.render.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.rendercomplete.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.runtimeid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.supportedtriggers.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateoutput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentdidmount.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentwillunmount.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.embeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.mounted.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.render.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.classname.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.container.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.embeddableid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.panelcomponent.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.embeddableid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.originatingapp.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.valueinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.cancreatenew.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.create.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.createfromsavedobject.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdefaultinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdisplayname.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getexplicitinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iscontainertype.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iseditable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.savedobjectmetadata.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorydefinition.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.code.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.savedobjectid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.defaulttitle.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editapp.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editpath.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editurl.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.error.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.loading.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.savedobjectid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.title.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.embeddableid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.input.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.closemycontextmenupanel.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentdidmount.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentwillunmount.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onblur.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onfocus.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.render.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.unsafe_componentwillmount.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanelhoc.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerenderer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerendererprops.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidmount.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidupdate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.render.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.shouldcomponentupdate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerembeddablefactory.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerenhancement.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.setcustomembeddablefactoryprovider.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.uiactions.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.embeddablepanel.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.inspector.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.uiactions.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getappnamefromid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.destroy.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.error.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.reload.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.render.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.addnewembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getchild.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getinputforchild.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.removeembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.untilembeddableloaded.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.updateinputforchild.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.destroy.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.enhancements.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinspectoradapters.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getiscontainer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getroot.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.gettitle.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.iscontainer.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.parent.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.reload.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.render.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.runtimeid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.supportedtriggers.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.updateinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iserrorembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.israngeselecttriggercontext.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isreferenceorvalueembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.issavedobjectembeddableinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isvalueclicktriggercontext.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.outputspec.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_badge_trigger.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_notification_trigger.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelbadgetrigger.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror._constructor_.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.code.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotificationtrigger.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.explicitinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.type.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.plugin.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.accesspath.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.description.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.displayname.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.id.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.value.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasreftype.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasvaluetype.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.inputisreftype.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.savedobjectid.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.viewmode.md create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.withembeddablesubscription.md create mode 100644 docs/development/plugins/embeddable/server/index.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.id.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.id.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.md create mode 100644 docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.plugin.md create mode 100644 src/plugins/embeddable/README.asciidoc delete mode 100644 src/plugins/embeddable/README.md create mode 100644 src/plugins/embeddable/public/public.api.md create mode 100644 src/plugins/embeddable/server/server.api.md diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index da62d1707f065b..e314e55c34085e 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -62,8 +62,8 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |Contains the Discover application and the saved search embeddable. -|{kib-repo}blob/{branch}/src/plugins/embeddable/README.md[embeddable] -|Embeddables are re-usable widgets that can be rendered in any environment or plugin. Developers can embed them directly in their plugin. End users can dynamically add them to any embeddable containers. +|<> +|Embeddables are re-usable widgets that can be rendered in any environment or plugin. Developers can embed them directly in their plugin. End users can dynamically add them to any embeddable _containers_. |{kib-repo}blob/{branch}/src/plugins/es_ui_shared/README.md[esUiShared] @@ -530,6 +530,7 @@ in their infrastructure. |=== include::{kibana-root}/src/plugins/dashboard/README.asciidoc[leveloffset=+1] +include::{kibana-root}/src/plugins/embeddable/README.asciidoc[leveloffset=+1] include::{kibana-root}/src/plugins/expressions/README.asciidoc[leveloffset=+1] include::{kibana-root}/src/plugins/ui_actions/README.asciidoc[leveloffset=+1] include::{kibana-root}/x-pack/plugins/dashboard_enhanced/README.asciidoc[leveloffset=+1] diff --git a/docs/development/plugins/embeddable/public/index.md b/docs/development/plugins/embeddable/public/index.md new file mode 100644 index 00000000000000..5de9666f6d0b96 --- /dev/null +++ b/docs/development/plugins/embeddable/public/index.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) + +## API Reference + +## Packages + +| Package | Description | +| --- | --- | +| [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_add_panel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_add_panel.md new file mode 100644 index 00000000000000..37c7a546d11edd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_add_panel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ACTION\_ADD\_PANEL](./kibana-plugin-plugins-embeddable-public.action_add_panel.md) + +## ACTION\_ADD\_PANEL variable + +Signature: + +```typescript +ACTION_ADD_PANEL = "ACTION_ADD_PANEL" +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_edit_panel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_edit_panel.md new file mode 100644 index 00000000000000..89f02e69f22601 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.action_edit_panel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ACTION\_EDIT\_PANEL](./kibana-plugin-plugins-embeddable-public.action_edit_panel.md) + +## ACTION\_EDIT\_PANEL variable + +Signature: + +```typescript +ACTION_EDIT_PANEL = "editPanel" +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md new file mode 100644 index 00000000000000..9635b36cdf05a4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Adapters](./kibana-plugin-plugins-embeddable-public.adapters.md) + +## Adapters interface + +The interface that the adapters used to open an inspector have to fullfill. + +Signature: + +```typescript +export interface Adapters +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md new file mode 100644 index 00000000000000..388f0e064d8661 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md) + +## AddPanelAction.(constructor) + +Constructs a new instance of the `AddPanelAction` class + +Signature: + +```typescript +constructor(getFactory: EmbeddableStart['getEmbeddableFactory'], getAllFactories: EmbeddableStart['getEmbeddableFactories'], overlays: OverlayStart, notifications: NotificationsStart, SavedObjectFinder: React.ComponentType); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| getFactory | EmbeddableStart['getEmbeddableFactory'] | | +| getAllFactories | EmbeddableStart['getEmbeddableFactories'] | | +| overlays | OverlayStart | | +| notifications | NotificationsStart | | +| SavedObjectFinder | React.ComponentType<any> | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.execute.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.execute.md new file mode 100644 index 00000000000000..46629f3c654f8f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.execute.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) > [execute](./kibana-plugin-plugins-embeddable-public.addpanelaction.execute.md) + +## AddPanelAction.execute() method + +Signature: + +```typescript +execute(context: ActionExecutionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<ActionContext> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.getdisplayname.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.getdisplayname.md new file mode 100644 index 00000000000000..b3a181861572b2 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.getdisplayname.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) > [getDisplayName](./kibana-plugin-plugins-embeddable-public.addpanelaction.getdisplayname.md) + +## AddPanelAction.getDisplayName() method + +Signature: + +```typescript +getDisplayName(): string; +``` +Returns: + +`string` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.geticontype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.geticontype.md new file mode 100644 index 00000000000000..c02aa6613630b6 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.geticontype.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) > [getIconType](./kibana-plugin-plugins-embeddable-public.addpanelaction.geticontype.md) + +## AddPanelAction.getIconType() method + +Signature: + +```typescript +getIconType(): string; +``` +Returns: + +`string` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.id.md new file mode 100644 index 00000000000000..781fb8ed293727 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) > [id](./kibana-plugin-plugins-embeddable-public.addpanelaction.id.md) + +## AddPanelAction.id property + +Signature: + +```typescript +readonly id = "ACTION_ADD_PANEL"; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.iscompatible.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.iscompatible.md new file mode 100644 index 00000000000000..c8349b86cf348e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.iscompatible.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) > [isCompatible](./kibana-plugin-plugins-embeddable-public.addpanelaction.iscompatible.md) + +## AddPanelAction.isCompatible() method + +Signature: + +```typescript +isCompatible(context: ActionExecutionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<ActionContext> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md new file mode 100644 index 00000000000000..74a6c2b2183a2e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md @@ -0,0 +1,34 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) + +## AddPanelAction class + +Signature: + +```typescript +export declare class AddPanelAction implements Action +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(getFactory, getAllFactories, overlays, notifications, SavedObjectFinder)](./kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md) | | Constructs a new instance of the AddPanelAction class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [id](./kibana-plugin-plugins-embeddable-public.addpanelaction.id.md) | | | | +| [type](./kibana-plugin-plugins-embeddable-public.addpanelaction.type.md) | | | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [execute(context)](./kibana-plugin-plugins-embeddable-public.addpanelaction.execute.md) | | | +| [getDisplayName()](./kibana-plugin-plugins-embeddable-public.addpanelaction.getdisplayname.md) | | | +| [getIconType()](./kibana-plugin-plugins-embeddable-public.addpanelaction.geticontype.md) | | | +| [isCompatible(context)](./kibana-plugin-plugins-embeddable-public.addpanelaction.iscompatible.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.type.md new file mode 100644 index 00000000000000..d57974c984025a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) > [type](./kibana-plugin-plugins-embeddable-public.addpanelaction.type.md) + +## AddPanelAction.type property + +Signature: + +```typescript +readonly type = "ACTION_ADD_PANEL"; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.chartactioncontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.chartactioncontext.md new file mode 100644 index 00000000000000..1c9fc27d53f19e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.chartactioncontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ChartActionContext](./kibana-plugin-plugins-embeddable-public.chartactioncontext.md) + +## ChartActionContext type + +Signature: + +```typescript +export declare type ChartActionContext = ValueClickContext | RangeSelectContext; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container._constructor_.md new file mode 100644 index 00000000000000..c571bae7c76133 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container._constructor_.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.container._constructor_.md) + +## Container.(constructor) + +Constructs a new instance of the `Container` class + +Signature: + +```typescript +constructor(input: TContainerInput, output: TContainerOutput, getFactory: EmbeddableStart['getEmbeddableFactory'], parent?: Container); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | TContainerInput | | +| output | TContainerOutput | | +| getFactory | EmbeddableStart['getEmbeddableFactory'] | | +| parent | Container | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.addnewembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.addnewembeddable.md new file mode 100644 index 00000000000000..1a7b32fea5361a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.addnewembeddable.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [addNewEmbeddable](./kibana-plugin-plugins-embeddable-public.container.addnewembeddable.md) + +## Container.addNewEmbeddable() method + +Signature: + +```typescript +addNewEmbeddable = IEmbeddable>(type: string, explicitInput: Partial): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | +| explicitInput | Partial<EEI> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.children.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.children.md new file mode 100644 index 00000000000000..e8f140219ed9c3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.children.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [children](./kibana-plugin-plugins-embeddable-public.container.children.md) + +## Container.children property + +Signature: + +```typescript +protected readonly children: { + [key: string]: IEmbeddable | ErrorEmbeddable; + }; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.createnewpanelstate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.createnewpanelstate.md new file mode 100644 index 00000000000000..cb084192ccf236 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.createnewpanelstate.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [createNewPanelState](./kibana-plugin-plugins-embeddable-public.container.createnewpanelstate.md) + +## Container.createNewPanelState() method + +Signature: + +```typescript +protected createNewPanelState>(factory: EmbeddableFactory, partial?: Partial): PanelState; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| factory | EmbeddableFactory<TEmbeddableInput, any, TEmbeddable> | | +| partial | Partial<TEmbeddableInput> | | + +Returns: + +`PanelState` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.destroy.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.destroy.md new file mode 100644 index 00000000000000..d2776fb9e59441 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.destroy.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [destroy](./kibana-plugin-plugins-embeddable-public.container.destroy.md) + +## Container.destroy() method + +Signature: + +```typescript +destroy(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchild.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchild.md new file mode 100644 index 00000000000000..56d6a8a105bc71 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchild.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [getChild](./kibana-plugin-plugins-embeddable-public.container.getchild.md) + +## Container.getChild() method + +Signature: + +```typescript +getChild(id: string): E; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`E` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchildids.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchildids.md new file mode 100644 index 00000000000000..83a9b134cad3fb --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getchildids.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [getChildIds](./kibana-plugin-plugins-embeddable-public.container.getchildids.md) + +## Container.getChildIds() method + +Signature: + +```typescript +getChildIds(): string[]; +``` +Returns: + +`string[]` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getfactory.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getfactory.md new file mode 100644 index 00000000000000..f4ac95abbf372d --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getfactory.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [getFactory](./kibana-plugin-plugins-embeddable-public.container.getfactory.md) + +## Container.getFactory property + +Signature: + +```typescript +protected readonly getFactory: EmbeddableStart['getEmbeddableFactory']; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinheritedinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinheritedinput.md new file mode 100644 index 00000000000000..4c5823b890e65e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinheritedinput.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [getInheritedInput](./kibana-plugin-plugins-embeddable-public.container.getinheritedinput.md) + +## Container.getInheritedInput() method + +Return state that comes from the container and is passed down to the child. For instance, time range and filters are common inherited input state. Note that any state stored in `this.input.panels[embeddableId].explicitInput` will override inherited input. + +Signature: + +```typescript +protected abstract getInheritedInput(id: string): TChildInput; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`TChildInput` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinputforchild.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinputforchild.md new file mode 100644 index 00000000000000..803356d5540127 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getinputforchild.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [getInputForChild](./kibana-plugin-plugins-embeddable-public.container.getinputforchild.md) + +## Container.getInputForChild() method + +Signature: + +```typescript +getInputForChild(embeddableId: string): TEmbeddableInput; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| embeddableId | string | | + +Returns: + +`TEmbeddableInput` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getpanelstate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getpanelstate.md new file mode 100644 index 00000000000000..5981284e0497cc --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.getpanelstate.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [getPanelState](./kibana-plugin-plugins-embeddable-public.container.getpanelstate.md) + +## Container.getPanelState() method + +Signature: + +```typescript +protected getPanelState(embeddableId: string): PanelState; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| embeddableId | string | | + +Returns: + +`PanelState` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.iscontainer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.iscontainer.md new file mode 100644 index 00000000000000..af65381de78f79 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.iscontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [isContainer](./kibana-plugin-plugins-embeddable-public.container.iscontainer.md) + +## Container.isContainer property + +Signature: + +```typescript +readonly isContainer: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.md new file mode 100644 index 00000000000000..d14adc31e4123a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.md @@ -0,0 +1,43 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) + +## Container class + +Signature: + +```typescript +export declare abstract class Container = {}, TContainerInput extends ContainerInput = ContainerInput, TContainerOutput extends ContainerOutput = ContainerOutput> extends Embeddable implements IContainer +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(input, output, getFactory, parent)](./kibana-plugin-plugins-embeddable-public.container._constructor_.md) | | Constructs a new instance of the Container class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [children](./kibana-plugin-plugins-embeddable-public.container.children.md) | | {
[key: string]: IEmbeddable<any, any> | ErrorEmbeddable;
} | | +| [getFactory](./kibana-plugin-plugins-embeddable-public.container.getfactory.md) | | EmbeddableStart['getEmbeddableFactory'] | | +| [isContainer](./kibana-plugin-plugins-embeddable-public.container.iscontainer.md) | | boolean | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [addNewEmbeddable(type, explicitInput)](./kibana-plugin-plugins-embeddable-public.container.addnewembeddable.md) | | | +| [createNewPanelState(factory, partial)](./kibana-plugin-plugins-embeddable-public.container.createnewpanelstate.md) | | | +| [destroy()](./kibana-plugin-plugins-embeddable-public.container.destroy.md) | | | +| [getChild(id)](./kibana-plugin-plugins-embeddable-public.container.getchild.md) | | | +| [getChildIds()](./kibana-plugin-plugins-embeddable-public.container.getchildids.md) | | | +| [getInheritedInput(id)](./kibana-plugin-plugins-embeddable-public.container.getinheritedinput.md) | | Return state that comes from the container and is passed down to the child. For instance, time range and filters are common inherited input state. Note that any state stored in this.input.panels[embeddableId].explicitInput will override inherited input. | +| [getInputForChild(embeddableId)](./kibana-plugin-plugins-embeddable-public.container.getinputforchild.md) | | | +| [getPanelState(embeddableId)](./kibana-plugin-plugins-embeddable-public.container.getpanelstate.md) | | | +| [reload()](./kibana-plugin-plugins-embeddable-public.container.reload.md) | | | +| [removeEmbeddable(embeddableId)](./kibana-plugin-plugins-embeddable-public.container.removeembeddable.md) | | | +| [untilEmbeddableLoaded(id)](./kibana-plugin-plugins-embeddable-public.container.untilembeddableloaded.md) | | | +| [updateInputForChild(id, changes)](./kibana-plugin-plugins-embeddable-public.container.updateinputforchild.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.reload.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.reload.md new file mode 100644 index 00000000000000..902da827ac46c6 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.reload.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [reload](./kibana-plugin-plugins-embeddable-public.container.reload.md) + +## Container.reload() method + +Signature: + +```typescript +reload(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.removeembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.removeembeddable.md new file mode 100644 index 00000000000000..44594c0649d462 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.removeembeddable.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [removeEmbeddable](./kibana-plugin-plugins-embeddable-public.container.removeembeddable.md) + +## Container.removeEmbeddable() method + +Signature: + +```typescript +removeEmbeddable(embeddableId: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| embeddableId | string | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.untilembeddableloaded.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.untilembeddableloaded.md new file mode 100644 index 00000000000000..45c115f3706943 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.untilembeddableloaded.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [untilEmbeddableLoaded](./kibana-plugin-plugins-embeddable-public.container.untilembeddableloaded.md) + +## Container.untilEmbeddableLoaded() method + +Signature: + +```typescript +untilEmbeddableLoaded(id: string): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.updateinputforchild.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.updateinputforchild.md new file mode 100644 index 00000000000000..ae25f373a907bf --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.container.updateinputforchild.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Container](./kibana-plugin-plugins-embeddable-public.container.md) > [updateInputForChild](./kibana-plugin-plugins-embeddable-public.container.updateinputforchild.md) + +## Container.updateInputForChild() method + +Signature: + +```typescript +updateInputForChild(id: string, changes: Partial): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | +| changes | Partial<EEI> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.hidepaneltitles.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.hidepaneltitles.md new file mode 100644 index 00000000000000..5bb80ae411a78b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.hidepaneltitles.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ContainerInput](./kibana-plugin-plugins-embeddable-public.containerinput.md) > [hidePanelTitles](./kibana-plugin-plugins-embeddable-public.containerinput.hidepaneltitles.md) + +## ContainerInput.hidePanelTitles property + +Signature: + +```typescript +hidePanelTitles?: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.md new file mode 100644 index 00000000000000..dc24507b71cfb5 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ContainerInput](./kibana-plugin-plugins-embeddable-public.containerinput.md) + +## ContainerInput interface + +Signature: + +```typescript +export interface ContainerInput extends EmbeddableInput +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [hidePanelTitles](./kibana-plugin-plugins-embeddable-public.containerinput.hidepaneltitles.md) | boolean | | +| [panels](./kibana-plugin-plugins-embeddable-public.containerinput.panels.md) | {
[key: string]: PanelState<PanelExplicitInput & EmbeddableInput & {
id: string;
}>;
} | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.panels.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.panels.md new file mode 100644 index 00000000000000..82d45ebe9a10e4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containerinput.panels.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ContainerInput](./kibana-plugin-plugins-embeddable-public.containerinput.md) > [panels](./kibana-plugin-plugins-embeddable-public.containerinput.panels.md) + +## ContainerInput.panels property + +Signature: + +```typescript +panels: { + [key: string]: PanelState; + }; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.embeddableloaded.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.embeddableloaded.md new file mode 100644 index 00000000000000..3f0db4eba0bc36 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.embeddableloaded.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ContainerOutput](./kibana-plugin-plugins-embeddable-public.containeroutput.md) > [embeddableLoaded](./kibana-plugin-plugins-embeddable-public.containeroutput.embeddableloaded.md) + +## ContainerOutput.embeddableLoaded property + +Signature: + +```typescript +embeddableLoaded: { + [key: string]: boolean; + }; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.md new file mode 100644 index 00000000000000..f448f0f3ac0595 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.containeroutput.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ContainerOutput](./kibana-plugin-plugins-embeddable-public.containeroutput.md) + +## ContainerOutput interface + +Signature: + +```typescript +export interface ContainerOutput extends EmbeddableOutput +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [embeddableLoaded](./kibana-plugin-plugins-embeddable-public.containeroutput.embeddableloaded.md) | {
[key: string]: boolean;
} | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.context_menu_trigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.context_menu_trigger.md new file mode 100644 index 00000000000000..bcfcf6a3216619 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.context_menu_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [CONTEXT\_MENU\_TRIGGER](./kibana-plugin-plugins-embeddable-public.context_menu_trigger.md) + +## CONTEXT\_MENU\_TRIGGER variable + +Signature: + +```typescript +CONTEXT_MENU_TRIGGER = "CONTEXT_MENU_TRIGGER" +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.contextmenutrigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.contextmenutrigger.md new file mode 100644 index 00000000000000..0a88e1e0a2ea8e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.contextmenutrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [contextMenuTrigger](./kibana-plugin-plugins-embeddable-public.contextmenutrigger.md) + +## contextMenuTrigger variable + +Signature: + +```typescript +contextMenuTrigger: Trigger<'CONTEXT_MENU_TRIGGER'> +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.defaultembeddablefactoryprovider.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.defaultembeddablefactoryprovider.md new file mode 100644 index 00000000000000..08047a7a441b85 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.defaultembeddablefactoryprovider.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [defaultEmbeddableFactoryProvider](./kibana-plugin-plugins-embeddable-public.defaultembeddablefactoryprovider.md) + +## defaultEmbeddableFactoryProvider variable + +Signature: + +```typescript +defaultEmbeddableFactoryProvider: = IEmbeddable, T extends SavedObjectAttributes = SavedObjectAttributes>(def: EmbeddableFactoryDefinition) => EmbeddableFactory +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction._constructor_.md new file mode 100644 index 00000000000000..55bb3d76b99d87 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction._constructor_.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.editpanelaction._constructor_.md) + +## EditPanelAction.(constructor) + +Constructs a new instance of the `EditPanelAction` class + +Signature: + +```typescript +constructor(getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'], application: ApplicationStart, stateTransfer?: EmbeddableStateTransfer | undefined); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| getEmbeddableFactory | EmbeddableStart['getEmbeddableFactory'] | | +| application | ApplicationStart | | +| stateTransfer | EmbeddableStateTransfer | undefined | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.currentappid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.currentappid.md new file mode 100644 index 00000000000000..db94b1482d8b50 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.currentappid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [currentAppId](./kibana-plugin-plugins-embeddable-public.editpanelaction.currentappid.md) + +## EditPanelAction.currentAppId property + +Signature: + +```typescript +currentAppId: string | undefined; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.execute.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.execute.md new file mode 100644 index 00000000000000..6cfd88f17ba85b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.execute.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [execute](./kibana-plugin-plugins-embeddable-public.editpanelaction.execute.md) + +## EditPanelAction.execute() method + +Signature: + +```typescript +execute(context: ActionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionContext | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getapptarget.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getapptarget.md new file mode 100644 index 00000000000000..c9ede0f48b2853 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getapptarget.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [getAppTarget](./kibana-plugin-plugins-embeddable-public.editpanelaction.getapptarget.md) + +## EditPanelAction.getAppTarget() method + +Signature: + +```typescript +getAppTarget({ embeddable }: ActionContext): NavigationContext | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { embeddable } | ActionContext | | + +Returns: + +`NavigationContext | undefined` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getdisplayname.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getdisplayname.md new file mode 100644 index 00000000000000..227fdb8877149b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.getdisplayname.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [getDisplayName](./kibana-plugin-plugins-embeddable-public.editpanelaction.getdisplayname.md) + +## EditPanelAction.getDisplayName() method + +Signature: + +```typescript +getDisplayName({ embeddable }: ActionContext): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { embeddable } | ActionContext | | + +Returns: + +`string` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.gethref.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.gethref.md new file mode 100644 index 00000000000000..1139278ab781fd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.gethref.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [getHref](./kibana-plugin-plugins-embeddable-public.editpanelaction.gethref.md) + +## EditPanelAction.getHref() method + +Signature: + +```typescript +getHref({ embeddable }: ActionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { embeddable } | ActionContext | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.geticontype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.geticontype.md new file mode 100644 index 00000000000000..bc5a1f054ca751 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.geticontype.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [getIconType](./kibana-plugin-plugins-embeddable-public.editpanelaction.geticontype.md) + +## EditPanelAction.getIconType() method + +Signature: + +```typescript +getIconType(): string; +``` +Returns: + +`string` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.id.md new file mode 100644 index 00000000000000..d8b0888b518012 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [id](./kibana-plugin-plugins-embeddable-public.editpanelaction.id.md) + +## EditPanelAction.id property + +Signature: + +```typescript +readonly id = "editPanel"; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.iscompatible.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.iscompatible.md new file mode 100644 index 00000000000000..7f2714f14f0e9b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.iscompatible.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [isCompatible](./kibana-plugin-plugins-embeddable-public.editpanelaction.iscompatible.md) + +## EditPanelAction.isCompatible() method + +Signature: + +```typescript +isCompatible({ embeddable }: ActionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { embeddable } | ActionContext | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.md new file mode 100644 index 00000000000000..a39eae812ebfcd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.md @@ -0,0 +1,38 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) + +## EditPanelAction class + +Signature: + +```typescript +export declare class EditPanelAction implements Action +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(getEmbeddableFactory, application, stateTransfer)](./kibana-plugin-plugins-embeddable-public.editpanelaction._constructor_.md) | | Constructs a new instance of the EditPanelAction class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [currentAppId](./kibana-plugin-plugins-embeddable-public.editpanelaction.currentappid.md) | | string | undefined | | +| [id](./kibana-plugin-plugins-embeddable-public.editpanelaction.id.md) | | | | +| [order](./kibana-plugin-plugins-embeddable-public.editpanelaction.order.md) | | number | | +| [type](./kibana-plugin-plugins-embeddable-public.editpanelaction.type.md) | | | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [execute(context)](./kibana-plugin-plugins-embeddable-public.editpanelaction.execute.md) | | | +| [getAppTarget({ embeddable })](./kibana-plugin-plugins-embeddable-public.editpanelaction.getapptarget.md) | | | +| [getDisplayName({ embeddable })](./kibana-plugin-plugins-embeddable-public.editpanelaction.getdisplayname.md) | | | +| [getHref({ embeddable })](./kibana-plugin-plugins-embeddable-public.editpanelaction.gethref.md) | | | +| [getIconType()](./kibana-plugin-plugins-embeddable-public.editpanelaction.geticontype.md) | | | +| [isCompatible({ embeddable })](./kibana-plugin-plugins-embeddable-public.editpanelaction.iscompatible.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.order.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.order.md new file mode 100644 index 00000000000000..0ec5cde54b279b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.order.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [order](./kibana-plugin-plugins-embeddable-public.editpanelaction.order.md) + +## EditPanelAction.order property + +Signature: + +```typescript +order: number; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.type.md new file mode 100644 index 00000000000000..329f08abaaa3cd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.editpanelaction.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) > [type](./kibana-plugin-plugins-embeddable-public.editpanelaction.type.md) + +## EditPanelAction.type property + +Signature: + +```typescript +readonly type = "editPanel"; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable._constructor_.md new file mode 100644 index 00000000000000..c5e8788bf5d4dd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable._constructor_.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.embeddable._constructor_.md) + +## Embeddable.(constructor) + +Constructs a new instance of the `Embeddable` class + +Signature: + +```typescript +constructor(input: TEmbeddableInput, output: TEmbeddableOutput, parent?: IContainer); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | TEmbeddableInput | | +| output | TEmbeddableOutput | | +| parent | IContainer | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.destroy.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.destroy.md new file mode 100644 index 00000000000000..1ff16eec0b7506 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.destroy.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [destroy](./kibana-plugin-plugins-embeddable-public.embeddable.destroy.md) + +## Embeddable.destroy() method + +Called when this embeddable is no longer used, this should be the place for implementors to add any additional clean up tasks, like unmounting and unsubscribing. + +Signature: + +```typescript +destroy(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput.md new file mode 100644 index 00000000000000..f4a0724d426805 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getInput](./kibana-plugin-plugins-embeddable-public.embeddable.getinput.md) + +## Embeddable.getInput() method + +Signature: + +```typescript +getInput(): Readonly; +``` +Returns: + +`Readonly` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput_.md new file mode 100644 index 00000000000000..e4910d3eb1bf2b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinput_.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getInput$](./kibana-plugin-plugins-embeddable-public.embeddable.getinput_.md) + +## Embeddable.getInput$() method + +Signature: + +```typescript +getInput$(): Readonly>; +``` +Returns: + +`Readonly>` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinspectoradapters.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinspectoradapters.md new file mode 100644 index 00000000000000..490eaca32e685e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getinspectoradapters.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getInspectorAdapters](./kibana-plugin-plugins-embeddable-public.embeddable.getinspectoradapters.md) + +## Embeddable.getInspectorAdapters() method + +An embeddable can return inspector adapters if it want the inspector to be available via the context menu of that panel. Inspector adapters that will be used to open an inspector for. + +Signature: + +```typescript +getInspectorAdapters(): Adapters | undefined; +``` +Returns: + +`Adapters | undefined` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getiscontainer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getiscontainer.md new file mode 100644 index 00000000000000..cb9945ea312933 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getiscontainer.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getIsContainer](./kibana-plugin-plugins-embeddable-public.embeddable.getiscontainer.md) + +## Embeddable.getIsContainer() method + +Signature: + +```typescript +getIsContainer(): this is IContainer; +``` +Returns: + +`this is IContainer` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput.md new file mode 100644 index 00000000000000..b24c5aefddb402 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getOutput](./kibana-plugin-plugins-embeddable-public.embeddable.getoutput.md) + +## Embeddable.getOutput() method + +Signature: + +```typescript +getOutput(): Readonly; +``` +Returns: + +`Readonly` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md new file mode 100644 index 00000000000000..34b5f864dd0c8a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getOutput$](./kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md) + +## Embeddable.getOutput$() method + +Signature: + +```typescript +getOutput$(): Readonly>; +``` +Returns: + +`Readonly>` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getroot.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getroot.md new file mode 100644 index 00000000000000..79397911d5bc72 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.getroot.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getRoot](./kibana-plugin-plugins-embeddable-public.embeddable.getroot.md) + +## Embeddable.getRoot() method + +Returns the top most parent embeddable, or itself if this embeddable is not within a parent. + +Signature: + +```typescript +getRoot(): IEmbeddable | IContainer; +``` +Returns: + +`IEmbeddable | IContainer` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md new file mode 100644 index 00000000000000..4dc1900b4b0110 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [getTitle](./kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md) + +## Embeddable.getTitle() method + +Signature: + +```typescript +getTitle(): string; +``` +Returns: + +`string` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.id.md new file mode 100644 index 00000000000000..348934b9fb65c5 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [id](./kibana-plugin-plugins-embeddable-public.embeddable.id.md) + +## Embeddable.id property + +Signature: + +```typescript +readonly id: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.input.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.input.md new file mode 100644 index 00000000000000..4541aeacd5bc89 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.input.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [input](./kibana-plugin-plugins-embeddable-public.embeddable.input.md) + +## Embeddable.input property + +Signature: + +```typescript +protected input: TEmbeddableInput; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.iscontainer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.iscontainer.md new file mode 100644 index 00000000000000..db15653d40c4c3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.iscontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [isContainer](./kibana-plugin-plugins-embeddable-public.embeddable.iscontainer.md) + +## Embeddable.isContainer property + +Signature: + +```typescript +readonly isContainer: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.md new file mode 100644 index 00000000000000..295cc10b1bb193 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.md @@ -0,0 +1,51 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) + +## Embeddable class + +Signature: + +```typescript +export declare abstract class Embeddable implements IEmbeddable +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(input, output, parent)](./kibana-plugin-plugins-embeddable-public.embeddable._constructor_.md) | | Constructs a new instance of the Embeddable class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [id](./kibana-plugin-plugins-embeddable-public.embeddable.id.md) | | string | | +| [input](./kibana-plugin-plugins-embeddable-public.embeddable.input.md) | | TEmbeddableInput | | +| [isContainer](./kibana-plugin-plugins-embeddable-public.embeddable.iscontainer.md) | | boolean | | +| [output](./kibana-plugin-plugins-embeddable-public.embeddable.output.md) | | TEmbeddableOutput | | +| [parent](./kibana-plugin-plugins-embeddable-public.embeddable.parent.md) | | IContainer | | +| [renderComplete](./kibana-plugin-plugins-embeddable-public.embeddable.rendercomplete.md) | | RenderCompleteDispatcher | | +| [runtimeId](./kibana-plugin-plugins-embeddable-public.embeddable.runtimeid.md) | | number | | +| [runtimeId](./kibana-plugin-plugins-embeddable-public.embeddable.runtimeid.md) | static | number | | +| [type](./kibana-plugin-plugins-embeddable-public.embeddable.type.md) | | string | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [destroy()](./kibana-plugin-plugins-embeddable-public.embeddable.destroy.md) | | Called when this embeddable is no longer used, this should be the place for implementors to add any additional clean up tasks, like unmounting and unsubscribing. | +| [getInput()](./kibana-plugin-plugins-embeddable-public.embeddable.getinput.md) | | | +| [getInput$()](./kibana-plugin-plugins-embeddable-public.embeddable.getinput_.md) | | | +| [getInspectorAdapters()](./kibana-plugin-plugins-embeddable-public.embeddable.getinspectoradapters.md) | | An embeddable can return inspector adapters if it want the inspector to be available via the context menu of that panel. Inspector adapters that will be used to open an inspector for. | +| [getIsContainer()](./kibana-plugin-plugins-embeddable-public.embeddable.getiscontainer.md) | | | +| [getOutput()](./kibana-plugin-plugins-embeddable-public.embeddable.getoutput.md) | | | +| [getOutput$()](./kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md) | | | +| [getRoot()](./kibana-plugin-plugins-embeddable-public.embeddable.getroot.md) | | Returns the top most parent embeddable, or itself if this embeddable is not within a parent. | +| [getTitle()](./kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md) | | | +| [reload()](./kibana-plugin-plugins-embeddable-public.embeddable.reload.md) | | Reload will be called when there is a request to refresh the data or view, even if the input data did not change. | +| [render(el)](./kibana-plugin-plugins-embeddable-public.embeddable.render.md) | | | +| [supportedTriggers()](./kibana-plugin-plugins-embeddable-public.embeddable.supportedtriggers.md) | | | +| [updateInput(changes)](./kibana-plugin-plugins-embeddable-public.embeddable.updateinput.md) | | | +| [updateOutput(outputChanges)](./kibana-plugin-plugins-embeddable-public.embeddable.updateoutput.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.output.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.output.md new file mode 100644 index 00000000000000..db854e2a69cecf --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.output.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [output](./kibana-plugin-plugins-embeddable-public.embeddable.output.md) + +## Embeddable.output property + +Signature: + +```typescript +protected output: TEmbeddableOutput; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.parent.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.parent.md new file mode 100644 index 00000000000000..bfd82f53e96f16 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.parent.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [parent](./kibana-plugin-plugins-embeddable-public.embeddable.parent.md) + +## Embeddable.parent property + +Signature: + +```typescript +readonly parent?: IContainer; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.reload.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.reload.md new file mode 100644 index 00000000000000..e3b06f414cb5b4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.reload.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [reload](./kibana-plugin-plugins-embeddable-public.embeddable.reload.md) + +## Embeddable.reload() method + +Reload will be called when there is a request to refresh the data or view, even if the input data did not change. + +Signature: + +```typescript +abstract reload(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.render.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.render.md new file mode 100644 index 00000000000000..171a3c6a30a855 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.render.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [render](./kibana-plugin-plugins-embeddable-public.embeddable.render.md) + +## Embeddable.render() method + +Signature: + +```typescript +render(el: HTMLElement): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| el | HTMLElement | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.rendercomplete.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.rendercomplete.md new file mode 100644 index 00000000000000..c86bb2e998044c --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.rendercomplete.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [renderComplete](./kibana-plugin-plugins-embeddable-public.embeddable.rendercomplete.md) + +## Embeddable.renderComplete property + +Signature: + +```typescript +protected renderComplete: RenderCompleteDispatcher; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.runtimeid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.runtimeid.md new file mode 100644 index 00000000000000..a5cdd12b6f198b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.runtimeid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [runtimeId](./kibana-plugin-plugins-embeddable-public.embeddable.runtimeid.md) + +## Embeddable.runtimeId property + +Signature: + +```typescript +static runtimeId: number; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.supportedtriggers.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.supportedtriggers.md new file mode 100644 index 00000000000000..16676bc732b1ce --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.supportedtriggers.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [supportedTriggers](./kibana-plugin-plugins-embeddable-public.embeddable.supportedtriggers.md) + +## Embeddable.supportedTriggers() method + +Signature: + +```typescript +supportedTriggers(): Array; +``` +Returns: + +`Array` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.type.md new file mode 100644 index 00000000000000..bb3ae7384686c3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [type](./kibana-plugin-plugins-embeddable-public.embeddable.type.md) + +## Embeddable.type property + +Signature: + +```typescript +abstract readonly type: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateinput.md new file mode 100644 index 00000000000000..36c46bb71c6b62 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateinput.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [updateInput](./kibana-plugin-plugins-embeddable-public.embeddable.updateinput.md) + +## Embeddable.updateInput() method + +Signature: + +```typescript +updateInput(changes: Partial): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| changes | Partial<TEmbeddableInput> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateoutput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateoutput.md new file mode 100644 index 00000000000000..0b0244e7a58531 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.updateoutput.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) > [updateOutput](./kibana-plugin-plugins-embeddable-public.embeddable.updateoutput.md) + +## Embeddable.updateOutput() method + +Signature: + +```typescript +protected updateOutput(outputChanges: Partial): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| outputChanges | Partial<TEmbeddableOutput> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel._constructor_.md new file mode 100644 index 00000000000000..76412de0d54199 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel._constructor_.md) + +## EmbeddableChildPanel.(constructor) + +Constructs a new instance of the `EmbeddableChildPanel` class + +Signature: + +```typescript +constructor(props: EmbeddableChildPanelProps); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| props | EmbeddableChildPanelProps | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentdidmount.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentdidmount.md new file mode 100644 index 00000000000000..5302d3e986d947 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentdidmount.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) > [componentDidMount](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentdidmount.md) + +## EmbeddableChildPanel.componentDidMount() method + +Signature: + +```typescript +componentDidMount(): Promise; +``` +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentwillunmount.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentwillunmount.md new file mode 100644 index 00000000000000..17c23a5ba2fd1e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentwillunmount.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) > [componentWillUnmount](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentwillunmount.md) + +## EmbeddableChildPanel.componentWillUnmount() method + +Signature: + +```typescript +componentWillUnmount(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.embeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.embeddable.md new file mode 100644 index 00000000000000..298697167e1274 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.embeddable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) > [embeddable](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.embeddable.md) + +## EmbeddableChildPanel.embeddable property + +Signature: + +```typescript +embeddable: IEmbeddable | ErrorEmbeddable; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md new file mode 100644 index 00000000000000..d52033b4fd6ad1 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) + +## EmbeddableChildPanel class + +This component can be used by embeddable containers using react to easily render children. It waits for the child to be initialized, showing a loading indicator until that is complete. + +Signature: + +```typescript +export declare class EmbeddableChildPanel extends React.Component +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(props)](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel._constructor_.md) | | Constructs a new instance of the EmbeddableChildPanel class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [embeddable](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.embeddable.md) | | IEmbeddable | ErrorEmbeddable | | +| [mounted](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.mounted.md) | | boolean | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [componentDidMount()](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentdidmount.md) | | | +| [componentWillUnmount()](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.componentwillunmount.md) | | | +| [render()](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.render.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.mounted.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.mounted.md new file mode 100644 index 00000000000000..169f27ea5afa62 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.mounted.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) > [mounted](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.mounted.md) + +## EmbeddableChildPanel.mounted property + +Signature: + +```typescript +mounted: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.render.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.render.md new file mode 100644 index 00000000000000..01d70eb5f628fd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanel.render.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) > [render](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.render.md) + +## EmbeddableChildPanel.render() method + +Signature: + +```typescript +render(): JSX.Element; +``` +Returns: + +`JSX.Element` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.classname.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.classname.md new file mode 100644 index 00000000000000..d18dea31545d9f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.classname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanelProps](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md) > [className](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.classname.md) + +## EmbeddableChildPanelProps.className property + +Signature: + +```typescript +className?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.container.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.container.md new file mode 100644 index 00000000000000..91120f955b15e6 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.container.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanelProps](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md) > [container](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.container.md) + +## EmbeddableChildPanelProps.container property + +Signature: + +```typescript +container: IContainer; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.embeddableid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.embeddableid.md new file mode 100644 index 00000000000000..6765010e1b6967 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.embeddableid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanelProps](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md) > [embeddableId](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.embeddableid.md) + +## EmbeddableChildPanelProps.embeddableId property + +Signature: + +```typescript +embeddableId: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md new file mode 100644 index 00000000000000..7ed3bd1e207686 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanelProps](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md) + +## EmbeddableChildPanelProps interface + +Signature: + +```typescript +export interface EmbeddableChildPanelProps +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [className](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.classname.md) | string | | +| [container](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.container.md) | IContainer | | +| [embeddableId](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.embeddableid.md) | string | | +| [PanelComponent](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.panelcomponent.md) | EmbeddableStart['EmbeddablePanel'] | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.panelcomponent.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.panelcomponent.md new file mode 100644 index 00000000000000..e1bb6b41d38879 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.panelcomponent.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableChildPanelProps](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md) > [PanelComponent](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.panelcomponent.md) + +## EmbeddableChildPanelProps.PanelComponent property + +Signature: + +```typescript +PanelComponent: EmbeddableStart['EmbeddablePanel']; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md new file mode 100644 index 00000000000000..06e51958a2d1e5 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableContext](./kibana-plugin-plugins-embeddable-public.embeddablecontext.md) > [embeddable](./kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md) + +## EmbeddableContext.embeddable property + +Signature: + +```typescript +embeddable: IEmbeddable; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md new file mode 100644 index 00000000000000..a2c2d9245eabe3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableContext](./kibana-plugin-plugins-embeddable-public.embeddablecontext.md) + +## EmbeddableContext interface + +Signature: + +```typescript +export interface EmbeddableContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [embeddable](./kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md) | IEmbeddable | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.embeddableid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.embeddableid.md new file mode 100644 index 00000000000000..d998e982cc9d58 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.embeddableid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableEditorState](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) > [embeddableId](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.embeddableid.md) + +## EmbeddableEditorState.embeddableId property + +Signature: + +```typescript +embeddableId?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md new file mode 100644 index 00000000000000..63302f50204fed --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableEditorState](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) + +## EmbeddableEditorState interface + +A state package that contains information an editor will need to create or edit an embeddable then redirect back. + +Signature: + +```typescript +export interface EmbeddableEditorState +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [embeddableId](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.embeddableid.md) | string | | +| [originatingApp](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.originatingapp.md) | string | | +| [valueInput](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.valueinput.md) | EmbeddableInput | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.originatingapp.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.originatingapp.md new file mode 100644 index 00000000000000..640b0894ef2c7f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.originatingapp.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableEditorState](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) > [originatingApp](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.originatingapp.md) + +## EmbeddableEditorState.originatingApp property + +Signature: + +```typescript +originatingApp: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.valueinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.valueinput.md new file mode 100644 index 00000000000000..61ebfc61634b8b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableeditorstate.valueinput.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableEditorState](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) > [valueInput](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.valueinput.md) + +## EmbeddableEditorState.valueInput property + +Signature: + +```typescript +valueInput?: EmbeddableInput; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.cancreatenew.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.cancreatenew.md new file mode 100644 index 00000000000000..78bcb4f31a5be8 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.cancreatenew.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [canCreateNew](./kibana-plugin-plugins-embeddable-public.embeddablefactory.cancreatenew.md) + +## EmbeddableFactory.canCreateNew() method + +If false, this type of embeddable can't be created with the "createNew" functionality. Instead, use createFromSavedObject, where an existing saved object must first exist. + +Signature: + +```typescript +canCreateNew(): boolean; +``` +Returns: + +`boolean` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.create.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.create.md new file mode 100644 index 00000000000000..130c8cb7606257 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.create.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [create](./kibana-plugin-plugins-embeddable-public.embeddablefactory.create.md) + +## EmbeddableFactory.create() method + +Resolves to undefined if a new Embeddable cannot be directly created and the user will instead be redirected elsewhere. + +This will likely change in future iterations when we improve in place editing capabilities. + +Signature: + +```typescript +create(initialInput: TEmbeddableInput, parent?: IContainer): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initialInput | TEmbeddableInput | | +| parent | IContainer | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.createfromsavedobject.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.createfromsavedobject.md new file mode 100644 index 00000000000000..7a411988ca3b04 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.createfromsavedobject.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [createFromSavedObject](./kibana-plugin-plugins-embeddable-public.embeddablefactory.createfromsavedobject.md) + +## EmbeddableFactory.createFromSavedObject() method + +Creates a new embeddable instance based off the saved object id. + +Signature: + +```typescript +createFromSavedObject(savedObjectId: string, input: Partial, parent?: IContainer): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| savedObjectId | string | | +| input | Partial<TEmbeddableInput> | some input may come from a parent, or user, if it's not stored with the saved object. For example, the time range of the parent container. | +| parent | IContainer | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdefaultinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdefaultinput.md new file mode 100644 index 00000000000000..bf1ca6abd9ba0c --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdefaultinput.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [getDefaultInput](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdefaultinput.md) + +## EmbeddableFactory.getDefaultInput() method + +Can be used to get any default input, to be passed in to during the creation process. Default input will not be stored in a parent container, so any inherited input from a container will trump default input parameters. + +Signature: + +```typescript +getDefaultInput(partial: Partial): Partial; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| partial | Partial<TEmbeddableInput> | | + +Returns: + +`Partial` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdisplayname.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdisplayname.md new file mode 100644 index 00000000000000..5b97645d4947d7 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getdisplayname.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [getDisplayName](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdisplayname.md) + +## EmbeddableFactory.getDisplayName() method + +Returns a display name for this type of embeddable. Used in "Create new... " options in the add panel for containers. + +Signature: + +```typescript +getDisplayName(): string; +``` +Returns: + +`string` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getexplicitinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getexplicitinput.md new file mode 100644 index 00000000000000..3ec05f50005d06 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.getexplicitinput.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [getExplicitInput](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getexplicitinput.md) + +## EmbeddableFactory.getExplicitInput() method + +Can be used to request explicit input from the user, to be passed in to `EmbeddableFactory:create`. Explicit input is stored on the parent container for this embeddable. It overrides any inherited input passed down from the parent container. + +Signature: + +```typescript +getExplicitInput(): Promise>; +``` +Returns: + +`Promise>` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iscontainertype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iscontainertype.md new file mode 100644 index 00000000000000..f3ba375ab575c4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iscontainertype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [isContainerType](./kibana-plugin-plugins-embeddable-public.embeddablefactory.iscontainertype.md) + +## EmbeddableFactory.isContainerType property + +True if is this factory create embeddables that are Containers. Used in the add panel to conditionally show whether these can be added to another container. It's just not supported right now, but once nested containers are officially supported we can probably get rid of this interface. + +Signature: + +```typescript +readonly isContainerType: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iseditable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iseditable.md new file mode 100644 index 00000000000000..f1ad10dfaa1f62 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.iseditable.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [isEditable](./kibana-plugin-plugins-embeddable-public.embeddablefactory.iseditable.md) + +## EmbeddableFactory.isEditable property + +Returns whether the current user should be allowed to edit this type of embeddable. Most of the time this should be based off the capabilities service, hence it's async. + +Signature: + +```typescript +readonly isEditable: () => Promise; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.md new file mode 100644 index 00000000000000..d543cf3d096df3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.md @@ -0,0 +1,34 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) + +## EmbeddableFactory interface + +EmbeddableFactories create and initialize an embeddable instance + +Signature: + +```typescript +export interface EmbeddableFactory = IEmbeddable, TSavedObjectAttributes extends SavedObjectAttributes = SavedObjectAttributes> extends PersistableState +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [isContainerType](./kibana-plugin-plugins-embeddable-public.embeddablefactory.iscontainertype.md) | boolean | True if is this factory create embeddables that are Containers. Used in the add panel to conditionally show whether these can be added to another container. It's just not supported right now, but once nested containers are officially supported we can probably get rid of this interface. | +| [isEditable](./kibana-plugin-plugins-embeddable-public.embeddablefactory.iseditable.md) | () => Promise<boolean> | Returns whether the current user should be allowed to edit this type of embeddable. Most of the time this should be based off the capabilities service, hence it's async. | +| [savedObjectMetaData](./kibana-plugin-plugins-embeddable-public.embeddablefactory.savedobjectmetadata.md) | SavedObjectMetaData<TSavedObjectAttributes> | | +| [type](./kibana-plugin-plugins-embeddable-public.embeddablefactory.type.md) | string | | + +## Methods + +| Method | Description | +| --- | --- | +| [canCreateNew()](./kibana-plugin-plugins-embeddable-public.embeddablefactory.cancreatenew.md) | If false, this type of embeddable can't be created with the "createNew" functionality. Instead, use createFromSavedObject, where an existing saved object must first exist. | +| [create(initialInput, parent)](./kibana-plugin-plugins-embeddable-public.embeddablefactory.create.md) | Resolves to undefined if a new Embeddable cannot be directly created and the user will instead be redirected elsewhere.This will likely change in future iterations when we improve in place editing capabilities. | +| [createFromSavedObject(savedObjectId, input, parent)](./kibana-plugin-plugins-embeddable-public.embeddablefactory.createfromsavedobject.md) | Creates a new embeddable instance based off the saved object id. | +| [getDefaultInput(partial)](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdefaultinput.md) | Can be used to get any default input, to be passed in to during the creation process. Default input will not be stored in a parent container, so any inherited input from a container will trump default input parameters. | +| [getDisplayName()](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdisplayname.md) | Returns a display name for this type of embeddable. Used in "Create new... " options in the add panel for containers. | +| [getExplicitInput()](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getexplicitinput.md) | Can be used to request explicit input from the user, to be passed in to EmbeddableFactory:create. Explicit input is stored on the parent container for this embeddable. It overrides any inherited input passed down from the parent container. | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.savedobjectmetadata.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.savedobjectmetadata.md new file mode 100644 index 00000000000000..ec5bf420aac3e4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.savedobjectmetadata.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [savedObjectMetaData](./kibana-plugin-plugins-embeddable-public.embeddablefactory.savedobjectmetadata.md) + +## EmbeddableFactory.savedObjectMetaData property + +Signature: + +```typescript +readonly savedObjectMetaData?: SavedObjectMetaData; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.type.md new file mode 100644 index 00000000000000..307f808de9bcdc --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactory.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) > [type](./kibana-plugin-plugins-embeddable-public.embeddablefactory.type.md) + +## EmbeddableFactory.type property + +Signature: + +```typescript +readonly type: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorydefinition.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorydefinition.md new file mode 100644 index 00000000000000..4e342d3cf73a19 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorydefinition.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactoryDefinition](./kibana-plugin-plugins-embeddable-public.embeddablefactorydefinition.md) + +## EmbeddableFactoryDefinition type + +Signature: + +```typescript +export declare type EmbeddableFactoryDefinition = IEmbeddable, T extends SavedObjectAttributes = SavedObjectAttributes> = Pick, 'create' | 'type' | 'isEditable' | 'getDisplayName'> & Partial, 'createFromSavedObject' | 'isContainerType' | 'getExplicitInput' | 'savedObjectMetaData' | 'canCreateNew' | 'getDefaultInput' | 'telemetry' | 'extract' | 'inject'>>; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror._constructor_.md new file mode 100644 index 00000000000000..273126936ce91a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactoryNotFoundError](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror._constructor_.md) + +## EmbeddableFactoryNotFoundError.(constructor) + +Constructs a new instance of the `EmbeddableFactoryNotFoundError` class + +Signature: + +```typescript +constructor(type: string); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.code.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.code.md new file mode 100644 index 00000000000000..2ad75d3e68ba46 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.code.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactoryNotFoundError](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md) > [code](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.code.md) + +## EmbeddableFactoryNotFoundError.code property + +Signature: + +```typescript +code: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md new file mode 100644 index 00000000000000..028271d36fee07 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableFactoryNotFoundError](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md) + +## EmbeddableFactoryNotFoundError class + +Signature: + +```typescript +export declare class EmbeddableFactoryNotFoundError extends Error +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(type)](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror._constructor_.md) | | Constructs a new instance of the EmbeddableFactoryNotFoundError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [code](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.code.md) | | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md new file mode 100644 index 00000000000000..d1d97d50f5948a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableInput](./kibana-plugin-plugins-embeddable-public.embeddableinput.md) + +## EmbeddableInput type + +Signature: + +```typescript +export declare type EmbeddableInput = { + viewMode?: ViewMode; + title?: string; + id: string; + lastReloadRequestTime?: number; + hidePanelTitles?: boolean; + enhancements?: SerializableState; + disabledActions?: string[]; + disableTriggers?: boolean; + timeRange?: TimeRange; + query?: Query; + filters?: Filter[]; +}; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.id.md new file mode 100644 index 00000000000000..2298c6fb111a0b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableInstanceConfiguration](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md) > [id](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.id.md) + +## EmbeddableInstanceConfiguration.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md new file mode 100644 index 00000000000000..84f6bcefef4476 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableInstanceConfiguration](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md) + +## EmbeddableInstanceConfiguration interface + +Signature: + +```typescript +export interface EmbeddableInstanceConfiguration +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.id.md) | string | | +| [savedObjectId](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.savedobjectid.md) | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.savedobjectid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.savedobjectid.md new file mode 100644 index 00000000000000..c1584403c5bba9 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.savedobjectid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableInstanceConfiguration](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md) > [savedObjectId](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.savedobjectid.md) + +## EmbeddableInstanceConfiguration.savedObjectId property + +Signature: + +```typescript +savedObjectId?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.defaulttitle.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.defaulttitle.md new file mode 100644 index 00000000000000..c9d616a96e8e20 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.defaulttitle.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [defaultTitle](./kibana-plugin-plugins-embeddable-public.embeddableoutput.defaulttitle.md) + +## EmbeddableOutput.defaultTitle property + +Signature: + +```typescript +defaultTitle?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editable.md new file mode 100644 index 00000000000000..4bf84a8f2abf80 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [editable](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editable.md) + +## EmbeddableOutput.editable property + +Signature: + +```typescript +editable?: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editapp.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editapp.md new file mode 100644 index 00000000000000..5c5acd6288ba43 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editapp.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [editApp](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editapp.md) + +## EmbeddableOutput.editApp property + +Signature: + +```typescript +editApp?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editpath.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editpath.md new file mode 100644 index 00000000000000..da282ece32f208 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editpath.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [editPath](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editpath.md) + +## EmbeddableOutput.editPath property + +Signature: + +```typescript +editPath?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editurl.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editurl.md new file mode 100644 index 00000000000000..a0c4bed4ad8bba --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.editurl.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [editUrl](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editurl.md) + +## EmbeddableOutput.editUrl property + +Signature: + +```typescript +editUrl?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.error.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.error.md new file mode 100644 index 00000000000000..db3f27ecf295b2 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [error](./kibana-plugin-plugins-embeddable-public.embeddableoutput.error.md) + +## EmbeddableOutput.error property + +Signature: + +```typescript +error?: EmbeddableError; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.loading.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.loading.md new file mode 100644 index 00000000000000..a9472b1663f1a3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.loading.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [loading](./kibana-plugin-plugins-embeddable-public.embeddableoutput.loading.md) + +## EmbeddableOutput.loading property + +Signature: + +```typescript +loading?: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.md new file mode 100644 index 00000000000000..92e1560c34e315 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) + +## EmbeddableOutput interface + +Signature: + +```typescript +export interface EmbeddableOutput +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [defaultTitle](./kibana-plugin-plugins-embeddable-public.embeddableoutput.defaulttitle.md) | string | | +| [editable](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editable.md) | boolean | | +| [editApp](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editapp.md) | string | | +| [editPath](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editpath.md) | string | | +| [editUrl](./kibana-plugin-plugins-embeddable-public.embeddableoutput.editurl.md) | string | | +| [error](./kibana-plugin-plugins-embeddable-public.embeddableoutput.error.md) | EmbeddableError | | +| [loading](./kibana-plugin-plugins-embeddable-public.embeddableoutput.loading.md) | boolean | | +| [savedObjectId](./kibana-plugin-plugins-embeddable-public.embeddableoutput.savedobjectid.md) | string | | +| [title](./kibana-plugin-plugins-embeddable-public.embeddableoutput.title.md) | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.savedobjectid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.savedobjectid.md new file mode 100644 index 00000000000000..29aca26621d797 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.savedobjectid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [savedObjectId](./kibana-plugin-plugins-embeddable-public.embeddableoutput.savedobjectid.md) + +## EmbeddableOutput.savedObjectId property + +Signature: + +```typescript +savedObjectId?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.title.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.title.md new file mode 100644 index 00000000000000..0748a60b38e0f1 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableoutput.title.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) > [title](./kibana-plugin-plugins-embeddable-public.embeddableoutput.title.md) + +## EmbeddableOutput.title property + +Signature: + +```typescript +title?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.embeddableid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.embeddableid.md new file mode 100644 index 00000000000000..de1598d92b6de3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.embeddableid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePackageState](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) > [embeddableId](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.embeddableid.md) + +## EmbeddablePackageState.embeddableId property + +Signature: + +```typescript +embeddableId?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.input.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.input.md new file mode 100644 index 00000000000000..2f4b1a1fa4237a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.input.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePackageState](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) > [input](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.input.md) + +## EmbeddablePackageState.input property + +Signature: + +```typescript +input: Optional | Optional; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md new file mode 100644 index 00000000000000..1c0b1b8bf8b462 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePackageState](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) + +## EmbeddablePackageState interface + +A state package that contains all fields necessary to create or update an embeddable by reference or by value in a container. + +Signature: + +```typescript +export interface EmbeddablePackageState +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [embeddableId](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.embeddableid.md) | string | | +| [input](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.input.md) | Optional<EmbeddableInput, 'id'> | Optional<SavedObjectEmbeddableInput, 'id'> | | +| [type](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.type.md) | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.type.md new file mode 100644 index 00000000000000..67ca5b8803dd5b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepackagestate.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePackageState](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) > [type](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.type.md) + +## EmbeddablePackageState.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel._constructor_.md new file mode 100644 index 00000000000000..741e5df8a1590d --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.embeddablepanel._constructor_.md) + +## EmbeddablePanel.(constructor) + +Constructs a new instance of the `EmbeddablePanel` class + +Signature: + +```typescript +constructor(props: Props); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| props | Props | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.closemycontextmenupanel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.closemycontextmenupanel.md new file mode 100644 index 00000000000000..6869257675aa4d --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.closemycontextmenupanel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [closeMyContextMenuPanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.closemycontextmenupanel.md) + +## EmbeddablePanel.closeMyContextMenuPanel property + +Signature: + +```typescript +closeMyContextMenuPanel: () => void; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentdidmount.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentdidmount.md new file mode 100644 index 00000000000000..fb281dcf1107fc --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentdidmount.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [componentDidMount](./kibana-plugin-plugins-embeddable-public.embeddablepanel.componentdidmount.md) + +## EmbeddablePanel.componentDidMount() method + +Signature: + +```typescript +componentDidMount(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentwillunmount.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentwillunmount.md new file mode 100644 index 00000000000000..41050f9c7c82af --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.componentwillunmount.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [componentWillUnmount](./kibana-plugin-plugins-embeddable-public.embeddablepanel.componentwillunmount.md) + +## EmbeddablePanel.componentWillUnmount() method + +Signature: + +```typescript +componentWillUnmount(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.md new file mode 100644 index 00000000000000..643649ede51efd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) + +## EmbeddablePanel class + +Signature: + +```typescript +export declare class EmbeddablePanel extends React.Component +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(props)](./kibana-plugin-plugins-embeddable-public.embeddablepanel._constructor_.md) | | Constructs a new instance of the EmbeddablePanel class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [closeMyContextMenuPanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.closemycontextmenupanel.md) | | () => void | | +| [onBlur](./kibana-plugin-plugins-embeddable-public.embeddablepanel.onblur.md) | | (blurredPanelIndex: string) => void | | +| [onFocus](./kibana-plugin-plugins-embeddable-public.embeddablepanel.onfocus.md) | | (focusedPanelIndex: string) => void | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [componentDidMount()](./kibana-plugin-plugins-embeddable-public.embeddablepanel.componentdidmount.md) | | | +| [componentWillUnmount()](./kibana-plugin-plugins-embeddable-public.embeddablepanel.componentwillunmount.md) | | | +| [render()](./kibana-plugin-plugins-embeddable-public.embeddablepanel.render.md) | | | +| [UNSAFE\_componentWillMount()](./kibana-plugin-plugins-embeddable-public.embeddablepanel.unsafe_componentwillmount.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onblur.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onblur.md new file mode 100644 index 00000000000000..f1db7468018187 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onblur.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [onBlur](./kibana-plugin-plugins-embeddable-public.embeddablepanel.onblur.md) + +## EmbeddablePanel.onBlur property + +Signature: + +```typescript +onBlur: (blurredPanelIndex: string) => void; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onfocus.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onfocus.md new file mode 100644 index 00000000000000..3c9b713eab9501 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.onfocus.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [onFocus](./kibana-plugin-plugins-embeddable-public.embeddablepanel.onfocus.md) + +## EmbeddablePanel.onFocus property + +Signature: + +```typescript +onFocus: (focusedPanelIndex: string) => void; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.render.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.render.md new file mode 100644 index 00000000000000..13e87df47a2427 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.render.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [render](./kibana-plugin-plugins-embeddable-public.embeddablepanel.render.md) + +## EmbeddablePanel.render() method + +Signature: + +```typescript +render(): JSX.Element; +``` +Returns: + +`JSX.Element` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.unsafe_componentwillmount.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.unsafe_componentwillmount.md new file mode 100644 index 00000000000000..286d7e9cee1f30 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanel.unsafe_componentwillmount.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) > [UNSAFE\_componentWillMount](./kibana-plugin-plugins-embeddable-public.embeddablepanel.unsafe_componentwillmount.md) + +## EmbeddablePanel.UNSAFE\_componentWillMount() method + +Signature: + +```typescript +UNSAFE_componentWillMount(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanelhoc.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanelhoc.md new file mode 100644 index 00000000000000..3f57ac562e6d55 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablepanelhoc.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddablePanelHOC](./kibana-plugin-plugins-embeddable-public.embeddablepanelhoc.md) + +## EmbeddablePanelHOC type + +Signature: + +```typescript +export declare type EmbeddablePanelHOC = React.FC<{ + embeddable: IEmbeddable; + hideHeader?: boolean; +}>; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerenderer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerenderer.md new file mode 100644 index 00000000000000..1bc55e60079102 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerenderer.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRenderer](./kibana-plugin-plugins-embeddable-public.embeddablerenderer.md) + +## EmbeddableRenderer variable + +Helper react component to render an embeddable Can be used if you have an embeddable object or an embeddable factory Supports updating input by passing `input` prop + +Signature: + +```typescript +EmbeddableRenderer: (props: EmbeddableRendererProps) => JSX.Element +``` + +## Remarks + +This component shouldn't be used inside an embeddable container to render embeddable children because children may lose inherited input, here is why: + +When passing `input` inside a prop, internally there is a call: + +```ts +embeddable.updateInput(input); + +``` +If you are simply rendering an embeddable, it's no problem. + +However when you are dealing with containers, you want to be sure to only pass into updateInput the actual state that changed. This is because calling child.updateInput({ foo }) will make foo explicit state. It cannot be inherited from it's parent. + +For example, on a dashboard, the time range is inherited by all children, unless they had their time range set explicitly. This is how "per panel time range" works. That action calls embeddable.updateInput({ timeRange }), and the time range will no longer be inherited from the container. + +see: https://github.com/elastic/kibana/pull/67783\#discussion\_r435447657 for more details. refer to: examples/embeddable\_explorer for examples with correct usage of this component. + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerendererprops.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerendererprops.md new file mode 100644 index 00000000000000..c21864b1140e88 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablerendererprops.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRendererProps](./kibana-plugin-plugins-embeddable-public.embeddablerendererprops.md) + +## EmbeddableRendererProps type + +This type is a publicly exposed props of [EmbeddableRenderer](./kibana-plugin-plugins-embeddable-public.embeddablerenderer.md) Union is used to validate that or factory or embeddable is passed in, but it can't be both simultaneously In case when embeddable is passed in, input is optional, because there is already an input inside of embeddable object In case when factory is used, then input is required, because it will be used as initial input to create an embeddable object + +Signature: + +```typescript +export declare type EmbeddableRendererProps = EmbeddableRendererPropsWithEmbeddable | EmbeddableRendererWithFactory; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot._constructor_.md new file mode 100644 index 00000000000000..4e0a2a6880d295 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.embeddableroot._constructor_.md) + +## EmbeddableRoot.(constructor) + +Constructs a new instance of the `EmbeddableRoot` class + +Signature: + +```typescript +constructor(props: Props); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| props | Props | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidmount.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidmount.md new file mode 100644 index 00000000000000..7085339dd88682 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidmount.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) > [componentDidMount](./kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidmount.md) + +## EmbeddableRoot.componentDidMount() method + +Signature: + +```typescript +componentDidMount(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidupdate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidupdate.md new file mode 100644 index 00000000000000..386c8c61681d5b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidupdate.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) > [componentDidUpdate](./kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidupdate.md) + +## EmbeddableRoot.componentDidUpdate() method + +Signature: + +```typescript +componentDidUpdate(prevProps?: Props): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| prevProps | Props | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.md new file mode 100644 index 00000000000000..49d8a184f334c8 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) + +## EmbeddableRoot class + +Signature: + +```typescript +export declare class EmbeddableRoot extends React.Component +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(props)](./kibana-plugin-plugins-embeddable-public.embeddableroot._constructor_.md) | | Constructs a new instance of the EmbeddableRoot class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [componentDidMount()](./kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidmount.md) | | | +| [componentDidUpdate(prevProps)](./kibana-plugin-plugins-embeddable-public.embeddableroot.componentdidupdate.md) | | | +| [render()](./kibana-plugin-plugins-embeddable-public.embeddableroot.render.md) | | | +| [shouldComponentUpdate(newProps)](./kibana-plugin-plugins-embeddable-public.embeddableroot.shouldcomponentupdate.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.render.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.render.md new file mode 100644 index 00000000000000..d9b3820dede151 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.render.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) > [render](./kibana-plugin-plugins-embeddable-public.embeddableroot.render.md) + +## EmbeddableRoot.render() method + +Signature: + +```typescript +render(): JSX.Element; +``` +Returns: + +`JSX.Element` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.shouldcomponentupdate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.shouldcomponentupdate.md new file mode 100644 index 00000000000000..36b08f72c0e401 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableroot.shouldcomponentupdate.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) > [shouldComponentUpdate](./kibana-plugin-plugins-embeddable-public.embeddableroot.shouldcomponentupdate.md) + +## EmbeddableRoot.shouldComponentUpdate() method + +Signature: + +```typescript +shouldComponentUpdate(newProps: Props): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| newProps | Props | | + +Returns: + +`boolean` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.md new file mode 100644 index 00000000000000..97d6eda66bdcd0 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-public.embeddablesetup.md) + +## EmbeddableSetup interface + +Signature: + +```typescript +export interface EmbeddableSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [registerEmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablesetup.registerembeddablefactory.md) | <I extends EmbeddableInput, O extends EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>>(id: string, factory: EmbeddableFactoryDefinition<I, O, E>) => () => EmbeddableFactory<I, O, E> | | +| [registerEnhancement](./kibana-plugin-plugins-embeddable-public.embeddablesetup.registerenhancement.md) | (enhancement: EnhancementRegistryDefinition) => void | | +| [setCustomEmbeddableFactoryProvider](./kibana-plugin-plugins-embeddable-public.embeddablesetup.setcustomembeddablefactoryprovider.md) | (customProvider: EmbeddableFactoryProvider) => void | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerembeddablefactory.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerembeddablefactory.md new file mode 100644 index 00000000000000..d9f63b30dfe6de --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerembeddablefactory.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-public.embeddablesetup.md) > [registerEmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablesetup.registerembeddablefactory.md) + +## EmbeddableSetup.registerEmbeddableFactory property + +Signature: + +```typescript +registerEmbeddableFactory: = IEmbeddable>(id: string, factory: EmbeddableFactoryDefinition) => () => EmbeddableFactory; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerenhancement.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerenhancement.md new file mode 100644 index 00000000000000..46baaf6dbf2681 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.registerenhancement.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-public.embeddablesetup.md) > [registerEnhancement](./kibana-plugin-plugins-embeddable-public.embeddablesetup.registerenhancement.md) + +## EmbeddableSetup.registerEnhancement property + +Signature: + +```typescript +registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.setcustomembeddablefactoryprovider.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.setcustomembeddablefactoryprovider.md new file mode 100644 index 00000000000000..463ff80e5818b7 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetup.setcustomembeddablefactoryprovider.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-public.embeddablesetup.md) > [setCustomEmbeddableFactoryProvider](./kibana-plugin-plugins-embeddable-public.embeddablesetup.setcustomembeddablefactoryprovider.md) + +## EmbeddableSetup.setCustomEmbeddableFactoryProvider property + +Signature: + +```typescript +setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md new file mode 100644 index 00000000000000..d3a62657372ac1 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetupDependencies](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md) > [data](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md) + +## EmbeddableSetupDependencies.data property + +Signature: + +```typescript +data: DataPublicPluginSetup; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md new file mode 100644 index 00000000000000..fdd31ca75be2a7 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetupDependencies](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md) + +## EmbeddableSetupDependencies interface + +Signature: + +```typescript +export interface EmbeddableSetupDependencies +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [data](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md) | DataPublicPluginSetup | | +| [uiActions](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.uiactions.md) | UiActionsSetup | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.uiactions.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.uiactions.md new file mode 100644 index 00000000000000..7eff6e2b0b28be --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.uiactions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetupDependencies](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md) > [uiActions](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.uiactions.md) + +## EmbeddableSetupDependencies.uiActions property + +Signature: + +```typescript +uiActions: UiActionsSetup; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.embeddablepanel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.embeddablepanel.md new file mode 100644 index 00000000000000..b8c10bf0e44733 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.embeddablepanel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) > [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.embeddablepanel.md) + +## EmbeddableStart.EmbeddablePanel property + +Signature: + +```typescript +EmbeddablePanel: EmbeddablePanelHOC; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md new file mode 100644 index 00000000000000..cc6b1187903bf3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) > [getEmbeddableFactories](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md) + +## EmbeddableStart.getEmbeddableFactories property + +Signature: + +```typescript +getEmbeddableFactories: () => IterableIterator; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md new file mode 100644 index 00000000000000..d91878754bd7d0 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) > [getEmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md) + +## EmbeddableStart.getEmbeddableFactory property + +Signature: + +```typescript +getEmbeddableFactory: = IEmbeddable>(embeddableFactoryId: string) => EmbeddableFactory | undefined; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md new file mode 100644 index 00000000000000..7ba24a62a38932 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) > [getEmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md) + +## EmbeddableStart.getEmbeddablePanel property + +Signature: + +```typescript +getEmbeddablePanel: (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md new file mode 100644 index 00000000000000..dafc66b1a6e15c --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) > [getStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md) + +## EmbeddableStart.getStateTransfer property + +Signature: + +```typescript +getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md new file mode 100644 index 00000000000000..f8e0028d8344b2 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) + +## EmbeddableStart interface + +Signature: + +```typescript +export interface EmbeddableStart extends PersistableState +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.embeddablepanel.md) | EmbeddablePanelHOC | | +| [getEmbeddableFactories](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md) | () => IterableIterator<EmbeddableFactory> | | +| [getEmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md) | <I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>>(embeddableFactoryId: string) => EmbeddableFactory<I, O, E> | undefined | | +| [getEmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md) | (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC | | +| [getStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md) | (history?: ScopedHistory) => EmbeddableStateTransfer | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md new file mode 100644 index 00000000000000..0595609b11e498 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStartDependencies](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md) > [data](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md) + +## EmbeddableStartDependencies.data property + +Signature: + +```typescript +data: DataPublicPluginStart; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.inspector.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.inspector.md new file mode 100644 index 00000000000000..299cc945104ab2 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.inspector.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStartDependencies](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md) > [inspector](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.inspector.md) + +## EmbeddableStartDependencies.inspector property + +Signature: + +```typescript +inspector: InspectorStart; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md new file mode 100644 index 00000000000000..5a1b5d1e068610 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStartDependencies](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md) + +## EmbeddableStartDependencies interface + +Signature: + +```typescript +export interface EmbeddableStartDependencies +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [data](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md) | DataPublicPluginStart | | +| [inspector](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.inspector.md) | InspectorStart | | +| [uiActions](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.uiactions.md) | UiActionsStart | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.uiactions.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.uiactions.md new file mode 100644 index 00000000000000..398ee3fbcbc500 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.uiactions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStartDependencies](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md) > [uiActions](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.uiactions.md) + +## EmbeddableStartDependencies.uiActions property + +Signature: + +```typescript +uiActions: UiActionsStart; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md new file mode 100644 index 00000000000000..323ed5e38bde1e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md) + +## EmbeddableStateTransfer.(constructor) + +Constructs a new instance of the `EmbeddableStateTransfer` class + +Signature: + +```typescript +constructor(navigateToApp: ApplicationStart['navigateToApp'], scopedHistory?: ScopedHistory | undefined, appList?: ReadonlyMap | undefined); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| navigateToApp | ApplicationStart['navigateToApp'] | | +| scopedHistory | ScopedHistory<unknown> | undefined | | +| appList | ReadonlyMap<string, PublicAppInfo> | undefined | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getappnamefromid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getappnamefromid.md new file mode 100644 index 00000000000000..f15574593e853e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getappnamefromid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [getAppNameFromId](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getappnamefromid.md) + +## EmbeddableStateTransfer.getAppNameFromId property + +Fetches an internationalized app title when given an appId. + +Signature: + +```typescript +getAppNameFromId: (appId: string) => string | undefined; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md new file mode 100644 index 00000000000000..2a0823a9bf835a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [getIncomingEditorState](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md) + +## EmbeddableStateTransfer.getIncomingEditorState() method + +Fetches an [originating app](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) argument from the scoped history's location state. + +Signature: + +```typescript +getIncomingEditorState(options?: { + keysToRemoveAfterFetch?: string[]; + }): EmbeddableEditorState | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| options | {
keysToRemoveAfterFetch?: string[];
} | | + +Returns: + +`EmbeddableEditorState | undefined` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md new file mode 100644 index 00000000000000..2069f0ce084f9f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [getIncomingEmbeddablePackage](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md) + +## EmbeddableStateTransfer.getIncomingEmbeddablePackage() method + +Fetches an [embeddable package](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) argument from the scoped history's location state. + +Signature: + +```typescript +getIncomingEmbeddablePackage(options?: { + keysToRemoveAfterFetch?: string[]; + }): EmbeddablePackageState | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| options | {
keysToRemoveAfterFetch?: string[];
} | | + +Returns: + +`EmbeddablePackageState | undefined` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md new file mode 100644 index 00000000000000..2b44693e14846f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) + +## EmbeddableStateTransfer class + +A wrapper around the state object in which provides strongly typed helper methods for common incoming and outgoing states used by the embeddable infrastructure. + +Signature: + +```typescript +export declare class EmbeddableStateTransfer +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(navigateToApp, scopedHistory, appList)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md) | | Constructs a new instance of the EmbeddableStateTransfer class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [getAppNameFromId](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getappnamefromid.md) | | (appId: string) => string | undefined | Fetches an internationalized app title when given an appId. | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [getIncomingEditorState(options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md) | | Fetches an [originating app](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) argument from the scoped history's location state. | +| [getIncomingEmbeddablePackage(options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md) | | Fetches an [embeddable package](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) argument from the scoped history's location state. | +| [navigateToEditor(appId, options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md) | | A wrapper around the method which navigates to the specified appId with [embeddable editor state](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) | +| [navigateToWithEmbeddablePackage(appId, options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md) | | A wrapper around the method which navigates to the specified appId with [embeddable package state](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md new file mode 100644 index 00000000000000..fa24784d9aac57 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [navigateToEditor](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md) + +## EmbeddableStateTransfer.navigateToEditor() method + +A wrapper around the method which navigates to the specified appId with [embeddable editor state](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) + +Signature: + +```typescript +navigateToEditor(appId: string, options?: { + path?: string; + state: EmbeddableEditorState; + appendToExistingState?: boolean; + }): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| appId | string | | +| options | {
path?: string;
state: EmbeddableEditorState;
appendToExistingState?: boolean;
} | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md new file mode 100644 index 00000000000000..7173bc8b127cdd --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [navigateToWithEmbeddablePackage](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md) + +## EmbeddableStateTransfer.navigateToWithEmbeddablePackage() method + +A wrapper around the method which navigates to the specified appId with [embeddable package state](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) + +Signature: + +```typescript +navigateToWithEmbeddablePackage(appId: string, options?: { + path?: string; + state: EmbeddablePackageState; + appendToExistingState?: boolean; + }): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| appId | string | | +| options | {
path?: string;
state: EmbeddablePackageState;
appendToExistingState?: boolean;
} | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.id.md new file mode 100644 index 00000000000000..083b3931bcf7d7 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EnhancementRegistryDefinition](./kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.md) > [id](./kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.id.md) + +## EnhancementRegistryDefinition.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.md new file mode 100644 index 00000000000000..c54ebe4b1712d3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EnhancementRegistryDefinition](./kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.md) + +## EnhancementRegistryDefinition interface + +Signature: + +```typescript +export interface EnhancementRegistryDefinition

extends PersistableStateDefinition

+``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.id.md) | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable._constructor_.md new file mode 100644 index 00000000000000..0facb07b416924 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable._constructor_.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.errorembeddable._constructor_.md) + +## ErrorEmbeddable.(constructor) + +Constructs a new instance of the `ErrorEmbeddable` class + +Signature: + +```typescript +constructor(error: Error | string, input: EmbeddableInput, parent?: IContainer); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| error | Error | string | | +| input | EmbeddableInput | | +| parent | IContainer | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.destroy.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.destroy.md new file mode 100644 index 00000000000000..eeb605f2140ece --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.destroy.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) > [destroy](./kibana-plugin-plugins-embeddable-public.errorembeddable.destroy.md) + +## ErrorEmbeddable.destroy() method + +Signature: + +```typescript +destroy(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.error.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.error.md new file mode 100644 index 00000000000000..7e4def3d529230 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) > [error](./kibana-plugin-plugins-embeddable-public.errorembeddable.error.md) + +## ErrorEmbeddable.error property + +Signature: + +```typescript +error: Error | string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.md new file mode 100644 index 00000000000000..75f3fc6d503d54 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.md @@ -0,0 +1,33 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) + +## ErrorEmbeddable class + +Signature: + +```typescript +export declare class ErrorEmbeddable extends Embeddable +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(error, input, parent)](./kibana-plugin-plugins-embeddable-public.errorembeddable._constructor_.md) | | Constructs a new instance of the ErrorEmbeddable class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [error](./kibana-plugin-plugins-embeddable-public.errorembeddable.error.md) | | Error | string | | +| [type](./kibana-plugin-plugins-embeddable-public.errorembeddable.type.md) | | | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [destroy()](./kibana-plugin-plugins-embeddable-public.errorembeddable.destroy.md) | | | +| [reload()](./kibana-plugin-plugins-embeddable-public.errorembeddable.reload.md) | | | +| [render(dom)](./kibana-plugin-plugins-embeddable-public.errorembeddable.render.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.reload.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.reload.md new file mode 100644 index 00000000000000..14d7c9fcf7ee07 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.reload.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) > [reload](./kibana-plugin-plugins-embeddable-public.errorembeddable.reload.md) + +## ErrorEmbeddable.reload() method + +Signature: + +```typescript +reload(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.render.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.render.md new file mode 100644 index 00000000000000..70c9d169f3f7e8 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.render.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) > [render](./kibana-plugin-plugins-embeddable-public.errorembeddable.render.md) + +## ErrorEmbeddable.render() method + +Signature: + +```typescript +render(dom: HTMLElement): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| dom | HTMLElement | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.type.md new file mode 100644 index 00000000000000..d407e743a89af5 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.errorembeddable.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) > [type](./kibana-plugin-plugins-embeddable-public.errorembeddable.type.md) + +## ErrorEmbeddable.type property + +Signature: + +```typescript +readonly type = "error"; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.addnewembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.addnewembeddable.md new file mode 100644 index 00000000000000..ca0095580a0ba0 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.addnewembeddable.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) > [addNewEmbeddable](./kibana-plugin-plugins-embeddable-public.icontainer.addnewembeddable.md) + +## IContainer.addNewEmbeddable() method + +Adds a new embeddable to the container. `explicitInput` may partially specify the required embeddable input, but the remainder must come from inherited container state. + +Signature: + +```typescript +addNewEmbeddable = Embeddable>(type: string, explicitInput: Partial): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | +| explicitInput | Partial<EEI> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getchild.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getchild.md new file mode 100644 index 00000000000000..4355cfb68ad3fa --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getchild.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) > [getChild](./kibana-plugin-plugins-embeddable-public.icontainer.getchild.md) + +## IContainer.getChild() method + +Returns the child embeddable with the given id. + +Signature: + +```typescript +getChild = Embeddable>(id: string): E; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`E` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getinputforchild.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getinputforchild.md new file mode 100644 index 00000000000000..e5afc0eac3ce0f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.getinputforchild.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) > [getInputForChild](./kibana-plugin-plugins-embeddable-public.icontainer.getinputforchild.md) + +## IContainer.getInputForChild() method + +Returns the input for the given child. Uses a combination of explicit input for the child stored on the parent and derived/inherited input taken from the container itself. + +Signature: + +```typescript +getInputForChild(id: string): EEI; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`EEI` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.md new file mode 100644 index 00000000000000..cba430069c7a41 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) + +## IContainer interface + +Signature: + +```typescript +export interface IContainer = ContainerInput, O extends ContainerOutput = ContainerOutput> extends IEmbeddable +``` + +## Methods + +| Method | Description | +| --- | --- | +| [addNewEmbeddable(type, explicitInput)](./kibana-plugin-plugins-embeddable-public.icontainer.addnewembeddable.md) | Adds a new embeddable to the container. explicitInput may partially specify the required embeddable input, but the remainder must come from inherited container state. | +| [getChild(id)](./kibana-plugin-plugins-embeddable-public.icontainer.getchild.md) | Returns the child embeddable with the given id. | +| [getInputForChild(id)](./kibana-plugin-plugins-embeddable-public.icontainer.getinputforchild.md) | Returns the input for the given child. Uses a combination of explicit input for the child stored on the parent and derived/inherited input taken from the container itself. | +| [removeEmbeddable(embeddableId)](./kibana-plugin-plugins-embeddable-public.icontainer.removeembeddable.md) | Removes the embeddable with the given id. | +| [untilEmbeddableLoaded(id)](./kibana-plugin-plugins-embeddable-public.icontainer.untilembeddableloaded.md) | Call if you want to wait until an embeddable with that id has finished loading. | +| [updateInputForChild(id, changes)](./kibana-plugin-plugins-embeddable-public.icontainer.updateinputforchild.md) | Changes the input for a given child. Note, this will override any inherited state taken from the container itself. | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.removeembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.removeembeddable.md new file mode 100644 index 00000000000000..94a991ca20a14e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.removeembeddable.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) > [removeEmbeddable](./kibana-plugin-plugins-embeddable-public.icontainer.removeembeddable.md) + +## IContainer.removeEmbeddable() method + +Removes the embeddable with the given id. + +Signature: + +```typescript +removeEmbeddable(embeddableId: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| embeddableId | string | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.untilembeddableloaded.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.untilembeddableloaded.md new file mode 100644 index 00000000000000..0d6d4a3d8ccf03 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.untilembeddableloaded.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) > [untilEmbeddableLoaded](./kibana-plugin-plugins-embeddable-public.icontainer.untilembeddableloaded.md) + +## IContainer.untilEmbeddableLoaded() method + +Call if you want to wait until an embeddable with that id has finished loading. + +Signature: + +```typescript +untilEmbeddableLoaded(id: string): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.updateinputforchild.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.updateinputforchild.md new file mode 100644 index 00000000000000..04a82b0065516c --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.icontainer.updateinputforchild.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) > [updateInputForChild](./kibana-plugin-plugins-embeddable-public.icontainer.updateinputforchild.md) + +## IContainer.updateInputForChild() method + +Changes the input for a given child. Note, this will override any inherited state taken from the container itself. + +Signature: + +```typescript +updateInputForChild(id: string, changes: Partial): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | +| changes | Partial<EEI> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.destroy.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.destroy.md new file mode 100644 index 00000000000000..7fc636f40f3c21 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.destroy.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [destroy](./kibana-plugin-plugins-embeddable-public.iembeddable.destroy.md) + +## IEmbeddable.destroy() method + +Cleans up subscriptions, destroy nodes mounted from calls to render. + +Signature: + +```typescript +destroy(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.enhancements.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.enhancements.md new file mode 100644 index 00000000000000..9183cd6887872a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.enhancements.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [enhancements](./kibana-plugin-plugins-embeddable-public.iembeddable.enhancements.md) + +## IEmbeddable.enhancements property + +Extra abilities added to Embeddable by `*_enhanced` plugins. + +Signature: + +```typescript +enhancements?: object; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput.md new file mode 100644 index 00000000000000..2fd8db07fa342f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getInput](./kibana-plugin-plugins-embeddable-public.iembeddable.getinput.md) + +## IEmbeddable.getInput() method + +Get the input used to instantiate this embeddable. The input is a serialized representation of this embeddable instance and can be used to clone or re-instantiate it. Input state: + +- Can be updated externally - Can change multiple times for a single embeddable instance. + +Examples: title, pie slice colors, custom search columns and sort order. + +Signature: + +```typescript +getInput(): Readonly; +``` +Returns: + +`Readonly` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput_.md new file mode 100644 index 00000000000000..ad91ad56b3d727 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinput_.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getInput$](./kibana-plugin-plugins-embeddable-public.iembeddable.getinput_.md) + +## IEmbeddable.getInput$() method + +Returns an observable which will be notified when input state changes. + +Signature: + +```typescript +getInput$(): Readonly>; +``` +Returns: + +`Readonly>` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinspectoradapters.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinspectoradapters.md new file mode 100644 index 00000000000000..84b083acac6f41 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getinspectoradapters.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getInspectorAdapters](./kibana-plugin-plugins-embeddable-public.iembeddable.getinspectoradapters.md) + +## IEmbeddable.getInspectorAdapters() method + +An embeddable can return inspector adapters if it wants the inspector to be available via the context menu of that panel. Inspector adapters that will be used to open an inspector for. + +Signature: + +```typescript +getInspectorAdapters(): Adapters | undefined; +``` +Returns: + +`Adapters | undefined` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getiscontainer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getiscontainer.md new file mode 100644 index 00000000000000..f9bfbbc4ca9bd4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getiscontainer.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getIsContainer](./kibana-plugin-plugins-embeddable-public.iembeddable.getiscontainer.md) + +## IEmbeddable.getIsContainer() method + +A functional representation of the isContainer variable, but helpful for typescript to know the shape if this returns true + +Signature: + +```typescript +getIsContainer(): this is IContainer; +``` +Returns: + +`this is IContainer` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput.md new file mode 100644 index 00000000000000..7e4e4fd3d4329e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getOutput](./kibana-plugin-plugins-embeddable-public.iembeddable.getoutput.md) + +## IEmbeddable.getOutput() method + +Output state is: + +- State that should not change once the embeddable is instantiated, or - State that is derived from the input state, or - State that only the embeddable instance itself knows about, or the factory. + +Examples: editUrl, title taken from a saved object, if your input state was first name and last name, your output state could be greeting. + +Signature: + +```typescript +getOutput(): Readonly; +``` +Returns: + +`Readonly` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput_.md new file mode 100644 index 00000000000000..11ec3e0d1c8ead --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getoutput_.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getOutput$](./kibana-plugin-plugins-embeddable-public.iembeddable.getoutput_.md) + +## IEmbeddable.getOutput$() method + +Returns an observable which will be notified when output state changes. + +Signature: + +```typescript +getOutput$(): Readonly>; +``` +Returns: + +`Readonly>` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getroot.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getroot.md new file mode 100644 index 00000000000000..eacec168b4d8ab --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.getroot.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getRoot](./kibana-plugin-plugins-embeddable-public.iembeddable.getroot.md) + +## IEmbeddable.getRoot() method + +Returns the top most parent embeddable, or itself if this embeddable is not within a parent. + +Signature: + +```typescript +getRoot(): IEmbeddable | IContainer; +``` +Returns: + +`IEmbeddable | IContainer` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.gettitle.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.gettitle.md new file mode 100644 index 00000000000000..eed80882f4b932 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.gettitle.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [getTitle](./kibana-plugin-plugins-embeddable-public.iembeddable.gettitle.md) + +## IEmbeddable.getTitle() method + +Returns the title of this embeddable. + +Signature: + +```typescript +getTitle(): string | undefined; +``` +Returns: + +`string | undefined` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.id.md new file mode 100644 index 00000000000000..7d2f5b9c7e71b1 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.id.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [id](./kibana-plugin-plugins-embeddable-public.iembeddable.id.md) + +## IEmbeddable.id property + +A unique identifier for this embeddable. Mainly only used by containers to map their Panel States to a child embeddable instance. + +Signature: + +```typescript +readonly id: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.iscontainer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.iscontainer.md new file mode 100644 index 00000000000000..93b910ee6f6a15 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.iscontainer.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [isContainer](./kibana-plugin-plugins-embeddable-public.iembeddable.iscontainer.md) + +## IEmbeddable.isContainer property + +Is this embeddable an instance of a Container class, can it contain nested embeddables? + +Signature: + +```typescript +readonly isContainer: boolean; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.md new file mode 100644 index 00000000000000..b3b6f961e56d1c --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.md @@ -0,0 +1,41 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) + +## IEmbeddable interface + +Signature: + +```typescript +export interface IEmbeddable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [enhancements](./kibana-plugin-plugins-embeddable-public.iembeddable.enhancements.md) | object | Extra abilities added to Embeddable by *_enhanced plugins. | +| [id](./kibana-plugin-plugins-embeddable-public.iembeddable.id.md) | string | A unique identifier for this embeddable. Mainly only used by containers to map their Panel States to a child embeddable instance. | +| [isContainer](./kibana-plugin-plugins-embeddable-public.iembeddable.iscontainer.md) | boolean | Is this embeddable an instance of a Container class, can it contain nested embeddables? | +| [parent](./kibana-plugin-plugins-embeddable-public.iembeddable.parent.md) | IContainer | If this embeddable is nested inside a container, this will contain a reference to its parent. | +| [runtimeId](./kibana-plugin-plugins-embeddable-public.iembeddable.runtimeid.md) | number | Unique ID an embeddable is assigned each time it is initialized. This ID is different for different instances of the same embeddable. For example, if the same dashboard is rendered twice on the screen, all embeddable instances will have a unique runtimeId. | +| [type](./kibana-plugin-plugins-embeddable-public.iembeddable.type.md) | string | The type of embeddable, this is what will be used to take a serialized embeddable and find the correct factory for which to create an instance of it. | + +## Methods + +| Method | Description | +| --- | --- | +| [destroy()](./kibana-plugin-plugins-embeddable-public.iembeddable.destroy.md) | Cleans up subscriptions, destroy nodes mounted from calls to render. | +| [getInput()](./kibana-plugin-plugins-embeddable-public.iembeddable.getinput.md) | Get the input used to instantiate this embeddable. The input is a serialized representation of this embeddable instance and can be used to clone or re-instantiate it. Input state:- Can be updated externally - Can change multiple times for a single embeddable instance.Examples: title, pie slice colors, custom search columns and sort order. | +| [getInput$()](./kibana-plugin-plugins-embeddable-public.iembeddable.getinput_.md) | Returns an observable which will be notified when input state changes. | +| [getInspectorAdapters()](./kibana-plugin-plugins-embeddable-public.iembeddable.getinspectoradapters.md) | An embeddable can return inspector adapters if it wants the inspector to be available via the context menu of that panel. Inspector adapters that will be used to open an inspector for. | +| [getIsContainer()](./kibana-plugin-plugins-embeddable-public.iembeddable.getiscontainer.md) | A functional representation of the isContainer variable, but helpful for typescript to know the shape if this returns true | +| [getOutput()](./kibana-plugin-plugins-embeddable-public.iembeddable.getoutput.md) | Output state is:- State that should not change once the embeddable is instantiated, or - State that is derived from the input state, or - State that only the embeddable instance itself knows about, or the factory.Examples: editUrl, title taken from a saved object, if your input state was first name and last name, your output state could be greeting. | +| [getOutput$()](./kibana-plugin-plugins-embeddable-public.iembeddable.getoutput_.md) | Returns an observable which will be notified when output state changes. | +| [getRoot()](./kibana-plugin-plugins-embeddable-public.iembeddable.getroot.md) | Returns the top most parent embeddable, or itself if this embeddable is not within a parent. | +| [getTitle()](./kibana-plugin-plugins-embeddable-public.iembeddable.gettitle.md) | Returns the title of this embeddable. | +| [reload()](./kibana-plugin-plugins-embeddable-public.iembeddable.reload.md) | Reload the embeddable so output and rendering is up to date. Especially relevant if the embeddable takes relative time as input (e.g. now to now-15) | +| [render(domNode)](./kibana-plugin-plugins-embeddable-public.iembeddable.render.md) | Renders the embeddable at the given node. | +| [supportedTriggers()](./kibana-plugin-plugins-embeddable-public.iembeddable.supportedtriggers.md) | List of triggers that this embeddable will execute. | +| [updateInput(changes)](./kibana-plugin-plugins-embeddable-public.iembeddable.updateinput.md) | Updates input state with the given changes. | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.parent.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.parent.md new file mode 100644 index 00000000000000..d20102902cdb0e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.parent.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [parent](./kibana-plugin-plugins-embeddable-public.iembeddable.parent.md) + +## IEmbeddable.parent property + +If this embeddable is nested inside a container, this will contain a reference to its parent. + +Signature: + +```typescript +readonly parent?: IContainer; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.reload.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.reload.md new file mode 100644 index 00000000000000..8caea9d8dc5111 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.reload.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [reload](./kibana-plugin-plugins-embeddable-public.iembeddable.reload.md) + +## IEmbeddable.reload() method + +Reload the embeddable so output and rendering is up to date. Especially relevant if the embeddable takes relative time as input (e.g. now to now-15) + +Signature: + +```typescript +reload(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.render.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.render.md new file mode 100644 index 00000000000000..9079227b622dcc --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.render.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [render](./kibana-plugin-plugins-embeddable-public.iembeddable.render.md) + +## IEmbeddable.render() method + +Renders the embeddable at the given node. + +Signature: + +```typescript +render(domNode: HTMLElement | Element): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| domNode | HTMLElement | Element | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.runtimeid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.runtimeid.md new file mode 100644 index 00000000000000..5ddd8ddd0f8dd0 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.runtimeid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [runtimeId](./kibana-plugin-plugins-embeddable-public.iembeddable.runtimeid.md) + +## IEmbeddable.runtimeId property + +Unique ID an embeddable is assigned each time it is initialized. This ID is different for different instances of the same embeddable. For example, if the same dashboard is rendered twice on the screen, all embeddable instances will have a unique `runtimeId`. + +Signature: + +```typescript +readonly runtimeId?: number; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.supportedtriggers.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.supportedtriggers.md new file mode 100644 index 00000000000000..5480f3b246648a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.supportedtriggers.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [supportedTriggers](./kibana-plugin-plugins-embeddable-public.iembeddable.supportedtriggers.md) + +## IEmbeddable.supportedTriggers() method + +List of triggers that this embeddable will execute. + +Signature: + +```typescript +supportedTriggers(): Array; +``` +Returns: + +`Array` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.type.md new file mode 100644 index 00000000000000..46b9d40685dba8 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [type](./kibana-plugin-plugins-embeddable-public.iembeddable.type.md) + +## IEmbeddable.type property + +The type of embeddable, this is what will be used to take a serialized embeddable and find the correct factory for which to create an instance of it. + +Signature: + +```typescript +readonly type: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.updateinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.updateinput.md new file mode 100644 index 00000000000000..523464103bd1a0 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.updateinput.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) > [updateInput](./kibana-plugin-plugins-embeddable-public.iembeddable.updateinput.md) + +## IEmbeddable.updateInput() method + +Updates input state with the given changes. + +Signature: + +```typescript +updateInput(changes: Partial): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| changes | Partial<I> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iserrorembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iserrorembeddable.md new file mode 100644 index 00000000000000..358d085ea9bbab --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iserrorembeddable.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [isErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.iserrorembeddable.md) + +## isErrorEmbeddable() function + +Signature: + +```typescript +export declare function isErrorEmbeddable(embeddable: TEmbeddable | ErrorEmbeddable): embeddable is ErrorEmbeddable; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| embeddable | TEmbeddable | ErrorEmbeddable | | + +Returns: + +`embeddable is ErrorEmbeddable` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.israngeselecttriggercontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.israngeselecttriggercontext.md new file mode 100644 index 00000000000000..cd28494fe3a09d --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.israngeselecttriggercontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [isRangeSelectTriggerContext](./kibana-plugin-plugins-embeddable-public.israngeselecttriggercontext.md) + +## isRangeSelectTriggerContext variable + +Signature: + +```typescript +isRangeSelectTriggerContext: (context: ChartActionContext) => context is RangeSelectContext> +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isreferenceorvalueembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isreferenceorvalueembeddable.md new file mode 100644 index 00000000000000..26a221d929ce6b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isreferenceorvalueembeddable.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [isReferenceOrValueEmbeddable](./kibana-plugin-plugins-embeddable-public.isreferenceorvalueembeddable.md) + +## isReferenceOrValueEmbeddable() function + +Signature: + +```typescript +export declare function isReferenceOrValueEmbeddable(incoming: unknown): incoming is ReferenceOrValueEmbeddable; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| incoming | unknown | | + +Returns: + +`incoming is ReferenceOrValueEmbeddable` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.issavedobjectembeddableinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.issavedobjectembeddableinput.md new file mode 100644 index 00000000000000..663cc41f1bffca --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.issavedobjectembeddableinput.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [isSavedObjectEmbeddableInput](./kibana-plugin-plugins-embeddable-public.issavedobjectembeddableinput.md) + +## isSavedObjectEmbeddableInput() function + +Signature: + +```typescript +export declare function isSavedObjectEmbeddableInput(input: EmbeddableInput | SavedObjectEmbeddableInput): input is SavedObjectEmbeddableInput; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | EmbeddableInput | SavedObjectEmbeddableInput | | + +Returns: + +`input is SavedObjectEmbeddableInput` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isvalueclicktriggercontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isvalueclicktriggercontext.md new file mode 100644 index 00000000000000..4e3c970d9b4374 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.isvalueclicktriggercontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [isValueClickTriggerContext](./kibana-plugin-plugins-embeddable-public.isvalueclicktriggercontext.md) + +## isValueClickTriggerContext variable + +Signature: + +```typescript +isValueClickTriggerContext: (context: ChartActionContext) => context is ValueClickContext> +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md new file mode 100644 index 00000000000000..64dfdd1c6dc226 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md @@ -0,0 +1,95 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) + +## kibana-plugin-plugins-embeddable-public package + +## Classes + +| Class | Description | +| --- | --- | +| [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) | | +| [Container](./kibana-plugin-plugins-embeddable-public.container.md) | | +| [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) | | +| [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) | | +| [EmbeddableChildPanel](./kibana-plugin-plugins-embeddable-public.embeddablechildpanel.md) | This component can be used by embeddable containers using react to easily render children. It waits for the child to be initialized, showing a loading indicator until that is complete. | +| [EmbeddableFactoryNotFoundError](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md) | | +| [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) | | +| [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) | | +| [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) | A wrapper around the state object in which provides strongly typed helper methods for common incoming and outgoing states used by the embeddable infrastructure. | +| [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) | | +| [PanelNotFoundError](./kibana-plugin-plugins-embeddable-public.panelnotfounderror.md) | | + +## Enumerations + +| Enumeration | Description | +| --- | --- | +| [ViewMode](./kibana-plugin-plugins-embeddable-public.viewmode.md) | | + +## Functions + +| Function | Description | +| --- | --- | +| [isErrorEmbeddable(embeddable)](./kibana-plugin-plugins-embeddable-public.iserrorembeddable.md) | | +| [isReferenceOrValueEmbeddable(incoming)](./kibana-plugin-plugins-embeddable-public.isreferenceorvalueembeddable.md) | | +| [isSavedObjectEmbeddableInput(input)](./kibana-plugin-plugins-embeddable-public.issavedobjectembeddableinput.md) | | +| [openAddPanelFlyout(options)](./kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md) | | +| [plugin(initializerContext)](./kibana-plugin-plugins-embeddable-public.plugin.md) | | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [Adapters](./kibana-plugin-plugins-embeddable-public.adapters.md) | The interface that the adapters used to open an inspector have to fullfill. | +| [ContainerInput](./kibana-plugin-plugins-embeddable-public.containerinput.md) | | +| [ContainerOutput](./kibana-plugin-plugins-embeddable-public.containeroutput.md) | | +| [EmbeddableChildPanelProps](./kibana-plugin-plugins-embeddable-public.embeddablechildpanelprops.md) | | +| [EmbeddableContext](./kibana-plugin-plugins-embeddable-public.embeddablecontext.md) | | +| [EmbeddableEditorState](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) | A state package that contains information an editor will need to create or edit an embeddable then redirect back. | +| [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) | EmbeddableFactories create and initialize an embeddable instance | +| [EmbeddableInstanceConfiguration](./kibana-plugin-plugins-embeddable-public.embeddableinstanceconfiguration.md) | | +| [EmbeddableOutput](./kibana-plugin-plugins-embeddable-public.embeddableoutput.md) | | +| [EmbeddablePackageState](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) | A state package that contains all fields necessary to create or update an embeddable by reference or by value in a container. | +| [EmbeddableSetup](./kibana-plugin-plugins-embeddable-public.embeddablesetup.md) | | +| [EmbeddableSetupDependencies](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md) | | +| [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) | | +| [EmbeddableStartDependencies](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md) | | +| [EnhancementRegistryDefinition](./kibana-plugin-plugins-embeddable-public.enhancementregistrydefinition.md) | | +| [IContainer](./kibana-plugin-plugins-embeddable-public.icontainer.md) | | +| [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) | | +| [OutputSpec](./kibana-plugin-plugins-embeddable-public.outputspec.md) | | +| [PanelState](./kibana-plugin-plugins-embeddable-public.panelstate.md) | | +| [PropertySpec](./kibana-plugin-plugins-embeddable-public.propertyspec.md) | | +| [RangeSelectContext](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.md) | | +| [ReferenceOrValueEmbeddable](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md) | Any embeddable that implements this interface will be able to use input that is either by reference (backed by a saved object) OR by value, (provided by the container). | +| [SavedObjectEmbeddableInput](./kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.md) | | +| [ValueClickContext](./kibana-plugin-plugins-embeddable-public.valueclickcontext.md) | | + +## Variables + +| Variable | Description | +| --- | --- | +| [ACTION\_ADD\_PANEL](./kibana-plugin-plugins-embeddable-public.action_add_panel.md) | | +| [ACTION\_EDIT\_PANEL](./kibana-plugin-plugins-embeddable-public.action_edit_panel.md) | | +| [CONTEXT\_MENU\_TRIGGER](./kibana-plugin-plugins-embeddable-public.context_menu_trigger.md) | | +| [contextMenuTrigger](./kibana-plugin-plugins-embeddable-public.contextmenutrigger.md) | | +| [defaultEmbeddableFactoryProvider](./kibana-plugin-plugins-embeddable-public.defaultembeddablefactoryprovider.md) | | +| [EmbeddableRenderer](./kibana-plugin-plugins-embeddable-public.embeddablerenderer.md) | Helper react component to render an embeddable Can be used if you have an embeddable object or an embeddable factory Supports updating input by passing input prop | +| [isRangeSelectTriggerContext](./kibana-plugin-plugins-embeddable-public.israngeselecttriggercontext.md) | | +| [isValueClickTriggerContext](./kibana-plugin-plugins-embeddable-public.isvalueclicktriggercontext.md) | | +| [PANEL\_BADGE\_TRIGGER](./kibana-plugin-plugins-embeddable-public.panel_badge_trigger.md) | | +| [PANEL\_NOTIFICATION\_TRIGGER](./kibana-plugin-plugins-embeddable-public.panel_notification_trigger.md) | | +| [panelBadgeTrigger](./kibana-plugin-plugins-embeddable-public.panelbadgetrigger.md) | | +| [panelNotificationTrigger](./kibana-plugin-plugins-embeddable-public.panelnotificationtrigger.md) | | +| [withEmbeddableSubscription](./kibana-plugin-plugins-embeddable-public.withembeddablesubscription.md) | | + +## Type Aliases + +| Type Alias | Description | +| --- | --- | +| [ChartActionContext](./kibana-plugin-plugins-embeddable-public.chartactioncontext.md) | | +| [EmbeddableFactoryDefinition](./kibana-plugin-plugins-embeddable-public.embeddablefactorydefinition.md) | | +| [EmbeddableInput](./kibana-plugin-plugins-embeddable-public.embeddableinput.md) | | +| [EmbeddablePanelHOC](./kibana-plugin-plugins-embeddable-public.embeddablepanelhoc.md) | | +| [EmbeddableRendererProps](./kibana-plugin-plugins-embeddable-public.embeddablerendererprops.md) | This type is a publicly exposed props of [EmbeddableRenderer](./kibana-plugin-plugins-embeddable-public.embeddablerenderer.md) Union is used to validate that or factory or embeddable is passed in, but it can't be both simultaneously In case when embeddable is passed in, input is optional, because there is already an input inside of embeddable object In case when factory is used, then input is required, because it will be used as initial input to create an embeddable object | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md new file mode 100644 index 00000000000000..ce97f79b4beb97 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [openAddPanelFlyout](./kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md) + +## openAddPanelFlyout() function + +Signature: + +```typescript +export declare function openAddPanelFlyout(options: { + embeddable: IContainer; + getFactory: EmbeddableStart['getEmbeddableFactory']; + getAllFactories: EmbeddableStart['getEmbeddableFactories']; + overlays: OverlayStart; + notifications: NotificationsStart; + SavedObjectFinder: React.ComponentType; +}): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| options | {
embeddable: IContainer;
getFactory: EmbeddableStart['getEmbeddableFactory'];
getAllFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: OverlayStart;
notifications: NotificationsStart;
SavedObjectFinder: React.ComponentType<any>;
} | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.outputspec.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.outputspec.md new file mode 100644 index 00000000000000..eead69b4e487cf --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.outputspec.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [OutputSpec](./kibana-plugin-plugins-embeddable-public.outputspec.md) + +## OutputSpec interface + +Signature: + +```typescript +export interface OutputSpec +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_badge_trigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_badge_trigger.md new file mode 100644 index 00000000000000..d5032d98ef4aaf --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_badge_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PANEL\_BADGE\_TRIGGER](./kibana-plugin-plugins-embeddable-public.panel_badge_trigger.md) + +## PANEL\_BADGE\_TRIGGER variable + +Signature: + +```typescript +PANEL_BADGE_TRIGGER = "PANEL_BADGE_TRIGGER" +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_notification_trigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_notification_trigger.md new file mode 100644 index 00000000000000..cd8a4a1ca45819 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panel_notification_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PANEL\_NOTIFICATION\_TRIGGER](./kibana-plugin-plugins-embeddable-public.panel_notification_trigger.md) + +## PANEL\_NOTIFICATION\_TRIGGER variable + +Signature: + +```typescript +PANEL_NOTIFICATION_TRIGGER = "PANEL_NOTIFICATION_TRIGGER" +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelbadgetrigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelbadgetrigger.md new file mode 100644 index 00000000000000..f6113c93a1c66a --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelbadgetrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [panelBadgeTrigger](./kibana-plugin-plugins-embeddable-public.panelbadgetrigger.md) + +## panelBadgeTrigger variable + +Signature: + +```typescript +panelBadgeTrigger: Trigger<'PANEL_BADGE_TRIGGER'> +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror._constructor_.md new file mode 100644 index 00000000000000..d1704403b2336b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror._constructor_.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PanelNotFoundError](./kibana-plugin-plugins-embeddable-public.panelnotfounderror.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.panelnotfounderror._constructor_.md) + +## PanelNotFoundError.(constructor) + +Constructs a new instance of the `PanelNotFoundError` class + +Signature: + +```typescript +constructor(); +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.code.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.code.md new file mode 100644 index 00000000000000..d169fb97480cc1 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.code.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PanelNotFoundError](./kibana-plugin-plugins-embeddable-public.panelnotfounderror.md) > [code](./kibana-plugin-plugins-embeddable-public.panelnotfounderror.code.md) + +## PanelNotFoundError.code property + +Signature: + +```typescript +code: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.md new file mode 100644 index 00000000000000..2191fdecf1ac5e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotfounderror.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PanelNotFoundError](./kibana-plugin-plugins-embeddable-public.panelnotfounderror.md) + +## PanelNotFoundError class + +Signature: + +```typescript +export declare class PanelNotFoundError extends Error +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)()](./kibana-plugin-plugins-embeddable-public.panelnotfounderror._constructor_.md) | | Constructs a new instance of the PanelNotFoundError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [code](./kibana-plugin-plugins-embeddable-public.panelnotfounderror.code.md) | | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotificationtrigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotificationtrigger.md new file mode 100644 index 00000000000000..df606c11f64ce6 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelnotificationtrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [panelNotificationTrigger](./kibana-plugin-plugins-embeddable-public.panelnotificationtrigger.md) + +## panelNotificationTrigger variable + +Signature: + +```typescript +panelNotificationTrigger: Trigger<'PANEL_NOTIFICATION_TRIGGER'> +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.explicitinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.explicitinput.md new file mode 100644 index 00000000000000..16123958d4db14 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.explicitinput.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PanelState](./kibana-plugin-plugins-embeddable-public.panelstate.md) > [explicitInput](./kibana-plugin-plugins-embeddable-public.panelstate.explicitinput.md) + +## PanelState.explicitInput property + +Signature: + +```typescript +explicitInput: Partial & { + id: string; + }; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.id.md new file mode 100644 index 00000000000000..e6fd4e0264f0d0 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PanelState](./kibana-plugin-plugins-embeddable-public.panelstate.md) > [id](./kibana-plugin-plugins-embeddable-public.panelstate.id.md) + +## PanelState.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.md new file mode 100644 index 00000000000000..b37f652b5021cc --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PanelState](./kibana-plugin-plugins-embeddable-public.panelstate.md) + +## PanelState interface + +Signature: + +```typescript +export interface PanelState +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [explicitInput](./kibana-plugin-plugins-embeddable-public.panelstate.explicitinput.md) | Partial<E> & {
id: string;
} | | +| [id](./kibana-plugin-plugins-embeddable-public.panelstate.id.md) | string | | +| [type](./kibana-plugin-plugins-embeddable-public.panelstate.type.md) | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.type.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.type.md new file mode 100644 index 00000000000000..8be470a77f1c7d --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.panelstate.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PanelState](./kibana-plugin-plugins-embeddable-public.panelstate.md) > [type](./kibana-plugin-plugins-embeddable-public.panelstate.type.md) + +## PanelState.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.plugin.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.plugin.md new file mode 100644 index 00000000000000..4e3ae760153cb1 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.plugin.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [plugin](./kibana-plugin-plugins-embeddable-public.plugin.md) + +## plugin() function + +Signature: + +```typescript +export declare function plugin(initializerContext: PluginInitializerContext): EmbeddablePublicPlugin; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initializerContext | PluginInitializerContext | | + +Returns: + +`EmbeddablePublicPlugin` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.accesspath.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.accesspath.md new file mode 100644 index 00000000000000..2a337e4b0141aa --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.accesspath.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PropertySpec](./kibana-plugin-plugins-embeddable-public.propertyspec.md) > [accessPath](./kibana-plugin-plugins-embeddable-public.propertyspec.accesspath.md) + +## PropertySpec.accessPath property + +Signature: + +```typescript +accessPath: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.description.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.description.md new file mode 100644 index 00000000000000..f36309c657795f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.description.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PropertySpec](./kibana-plugin-plugins-embeddable-public.propertyspec.md) > [description](./kibana-plugin-plugins-embeddable-public.propertyspec.description.md) + +## PropertySpec.description property + +Signature: + +```typescript +description: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.displayname.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.displayname.md new file mode 100644 index 00000000000000..16311493fa5dd5 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.displayname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PropertySpec](./kibana-plugin-plugins-embeddable-public.propertyspec.md) > [displayName](./kibana-plugin-plugins-embeddable-public.propertyspec.displayname.md) + +## PropertySpec.displayName property + +Signature: + +```typescript +displayName: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.id.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.id.md new file mode 100644 index 00000000000000..a37ed9000b67a3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PropertySpec](./kibana-plugin-plugins-embeddable-public.propertyspec.md) > [id](./kibana-plugin-plugins-embeddable-public.propertyspec.id.md) + +## PropertySpec.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.md new file mode 100644 index 00000000000000..02534b5d9d4dae --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PropertySpec](./kibana-plugin-plugins-embeddable-public.propertyspec.md) + +## PropertySpec interface + +Signature: + +```typescript +export interface PropertySpec +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [accessPath](./kibana-plugin-plugins-embeddable-public.propertyspec.accesspath.md) | string | | +| [description](./kibana-plugin-plugins-embeddable-public.propertyspec.description.md) | string | | +| [displayName](./kibana-plugin-plugins-embeddable-public.propertyspec.displayname.md) | string | | +| [id](./kibana-plugin-plugins-embeddable-public.propertyspec.id.md) | string | | +| [value](./kibana-plugin-plugins-embeddable-public.propertyspec.value.md) | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.value.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.value.md new file mode 100644 index 00000000000000..3360a9fff783cc --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.propertyspec.value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [PropertySpec](./kibana-plugin-plugins-embeddable-public.propertyspec.md) > [value](./kibana-plugin-plugins-embeddable-public.propertyspec.value.md) + +## PropertySpec.value property + +Signature: + +```typescript +value?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md new file mode 100644 index 00000000000000..6d2774d86f1090 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [RangeSelectContext](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.md) > [data](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md) + +## RangeSelectContext.data property + +Signature: + +```typescript +data: { + table: KibanaDatatable; + column: number; + range: number[]; + timeFieldName?: string; + }; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md new file mode 100644 index 00000000000000..a6c9f0f7e4253f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [RangeSelectContext](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.md) > [embeddable](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md) + +## RangeSelectContext.embeddable property + +Signature: + +```typescript +embeddable?: T; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md new file mode 100644 index 00000000000000..0f92ed86301da4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [RangeSelectContext](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.md) + +## RangeSelectContext interface + +Signature: + +```typescript +export interface RangeSelectContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [data](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md) | {
table: KibanaDatatable;
column: number;
range: number[];
timeFieldName?: string;
} | | +| [embeddable](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md) | T | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasreftype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasreftype.md new file mode 100644 index 00000000000000..559787c75ab660 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasreftype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ReferenceOrValueEmbeddable](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md) > [getInputAsRefType](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasreftype.md) + +## ReferenceOrValueEmbeddable.getInputAsRefType property + +Gets the embeddable's current input as its Reference type + +Signature: + +```typescript +getInputAsRefType: () => Promise; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasvaluetype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasvaluetype.md new file mode 100644 index 00000000000000..f9cd23b97858ac --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasvaluetype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ReferenceOrValueEmbeddable](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md) > [getInputAsValueType](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasvaluetype.md) + +## ReferenceOrValueEmbeddable.getInputAsValueType property + +Gets the embeddable's current input as its Value type + +Signature: + +```typescript +getInputAsValueType: () => Promise; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.inputisreftype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.inputisreftype.md new file mode 100644 index 00000000000000..9de0447769397e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.inputisreftype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ReferenceOrValueEmbeddable](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md) > [inputIsRefType](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.inputisreftype.md) + +## ReferenceOrValueEmbeddable.inputIsRefType property + +determines whether the input is by value or by reference. + +Signature: + +```typescript +inputIsRefType: (input: ValTypeInput | RefTypeInput) => input is RefTypeInput; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md new file mode 100644 index 00000000000000..47d6d8a0772d86 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ReferenceOrValueEmbeddable](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.md) + +## ReferenceOrValueEmbeddable interface + +Any embeddable that implements this interface will be able to use input that is either by reference (backed by a saved object) OR by value, (provided by the container). + +Signature: + +```typescript +export interface ReferenceOrValueEmbeddable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [getInputAsRefType](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasreftype.md) | () => Promise<RefTypeInput> | Gets the embeddable's current input as its Reference type | +| [getInputAsValueType](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.getinputasvaluetype.md) | () => Promise<ValTypeInput> | Gets the embeddable's current input as its Value type | +| [inputIsRefType](./kibana-plugin-plugins-embeddable-public.referenceorvalueembeddable.inputisreftype.md) | (input: ValTypeInput | RefTypeInput) => input is RefTypeInput | determines whether the input is by value or by reference. | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.md new file mode 100644 index 00000000000000..ae0df9ec01ba1f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [SavedObjectEmbeddableInput](./kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.md) + +## SavedObjectEmbeddableInput interface + +Signature: + +```typescript +export interface SavedObjectEmbeddableInput extends EmbeddableInput +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [savedObjectId](./kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.savedobjectid.md) | string | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.savedobjectid.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.savedobjectid.md new file mode 100644 index 00000000000000..d8cb3bbda9d01f --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.savedobjectid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [SavedObjectEmbeddableInput](./kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.md) > [savedObjectId](./kibana-plugin-plugins-embeddable-public.savedobjectembeddableinput.savedobjectid.md) + +## SavedObjectEmbeddableInput.savedObjectId property + +Signature: + +```typescript +savedObjectId: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md new file mode 100644 index 00000000000000..92c33affc47a95 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ValueClickContext](./kibana-plugin-plugins-embeddable-public.valueclickcontext.md) > [data](./kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md) + +## ValueClickContext.data property + +Signature: + +```typescript +data: { + data: Array<{ + table: Pick; + column: number; + row: number; + value: any; + }>; + timeFieldName?: string; + negate?: boolean; + }; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md new file mode 100644 index 00000000000000..38adee9346945b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ValueClickContext](./kibana-plugin-plugins-embeddable-public.valueclickcontext.md) > [embeddable](./kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md) + +## ValueClickContext.embeddable property + +Signature: + +```typescript +embeddable?: T; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md new file mode 100644 index 00000000000000..13133095956c6c --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ValueClickContext](./kibana-plugin-plugins-embeddable-public.valueclickcontext.md) + +## ValueClickContext interface + +Signature: + +```typescript +export interface ValueClickContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [data](./kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md) | {
data: Array<{
table: Pick<KibanaDatatable, 'rows' | 'columns'>;
column: number;
row: number;
value: any;
}>;
timeFieldName?: string;
negate?: boolean;
} | | +| [embeddable](./kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md) | T | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.viewmode.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.viewmode.md new file mode 100644 index 00000000000000..f47169951018df --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.viewmode.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ViewMode](./kibana-plugin-plugins-embeddable-public.viewmode.md) + +## ViewMode enum + +Signature: + +```typescript +export declare enum ViewMode +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| EDIT | "edit" | | +| VIEW | "view" | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.withembeddablesubscription.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.withembeddablesubscription.md new file mode 100644 index 00000000000000..a815292f3a0c3d --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.withembeddablesubscription.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [withEmbeddableSubscription](./kibana-plugin-plugins-embeddable-public.withembeddablesubscription.md) + +## withEmbeddableSubscription variable + +Signature: + +```typescript +withEmbeddableSubscription: = IEmbeddable, ExtraProps = {}>(WrappedComponent: React.ComponentType<{ + input: I; + output: O; + embeddable: E; +} & ExtraProps>) => React.ComponentType<{ + embeddable: E; +} & ExtraProps> +``` diff --git a/docs/development/plugins/embeddable/server/index.md b/docs/development/plugins/embeddable/server/index.md new file mode 100644 index 00000000000000..3c4d4ce3aed368 --- /dev/null +++ b/docs/development/plugins/embeddable/server/index.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) + +## API Reference + +## Packages + +| Package | Description | +| --- | --- | +| [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) | | + diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.id.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.id.md new file mode 100644 index 00000000000000..ce3e532fcaa3bf --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EmbeddableRegistryDefinition](./kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.md) > [id](./kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.id.md) + +## EmbeddableRegistryDefinition.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.md new file mode 100644 index 00000000000000..de46d91d90c651 --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EmbeddableRegistryDefinition](./kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.md) + +## EmbeddableRegistryDefinition interface + +Signature: + +```typescript +export interface EmbeddableRegistryDefinition

extends PersistableStateDefinition

+``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.id.md) | string | | + diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md new file mode 100644 index 00000000000000..59ca4a2bbca75b --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-server.embeddablesetup.md) + +## EmbeddableSetup interface + +Signature: + +```typescript +export interface EmbeddableSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [registerEmbeddableFactory](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md) | (factory: EmbeddableRegistryDefinition) => void | | +| [registerEnhancement](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md) | (enhancement: EnhancementRegistryDefinition) => void | | + diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md new file mode 100644 index 00000000000000..442941ce869501 --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-server.embeddablesetup.md) > [registerEmbeddableFactory](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md) + +## EmbeddableSetup.registerEmbeddableFactory property + +Signature: + +```typescript +registerEmbeddableFactory: (factory: EmbeddableRegistryDefinition) => void; +``` diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md new file mode 100644 index 00000000000000..9ea2846d0300b1 --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-server.embeddablesetup.md) > [registerEnhancement](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md) + +## EmbeddableSetup.registerEnhancement property + +Signature: + +```typescript +registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; +``` diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.id.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.id.md new file mode 100644 index 00000000000000..a93c6912468722 --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EnhancementRegistryDefinition](./kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.md) > [id](./kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.id.md) + +## EnhancementRegistryDefinition.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.md new file mode 100644 index 00000000000000..09ff48a92158db --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EnhancementRegistryDefinition](./kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.md) + +## EnhancementRegistryDefinition interface + +Signature: + +```typescript +export interface EnhancementRegistryDefinition

extends PersistableStateDefinition

+``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.id.md) | string | | + diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.md new file mode 100644 index 00000000000000..19ee57d677250f --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) + +## kibana-plugin-plugins-embeddable-server package + +## Interfaces + +| Interface | Description | +| --- | --- | +| [EmbeddableRegistryDefinition](./kibana-plugin-plugins-embeddable-server.embeddableregistrydefinition.md) | | +| [EmbeddableSetup](./kibana-plugin-plugins-embeddable-server.embeddablesetup.md) | | +| [EnhancementRegistryDefinition](./kibana-plugin-plugins-embeddable-server.enhancementregistrydefinition.md) | | + +## Variables + +| Variable | Description | +| --- | --- | +| [plugin](./kibana-plugin-plugins-embeddable-server.plugin.md) | | + diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.plugin.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.plugin.md new file mode 100644 index 00000000000000..989f3c3e60963f --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.plugin.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [plugin](./kibana-plugin-plugins-embeddable-server.plugin.md) + +## plugin variable + +Signature: + +```typescript +plugin: () => EmbeddableServerPlugin +``` diff --git a/src/dev/run_check_published_api_changes.ts b/src/dev/run_check_published_api_changes.ts index aaac706f991c23..984e013114c9eb 100644 --- a/src/dev/run_check_published_api_changes.ts +++ b/src/dev/run_check_published_api_changes.ts @@ -233,6 +233,8 @@ async function run(folder: string, { opts }: { opts: Options }): Promise Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { Action } from 'history'; +import { Action as Action_3 } from 'src/plugins/ui_actions/public'; +import { ActionExecutionContext as ActionExecutionContext_2 } from 'src/plugins/ui_actions/public'; +import { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; +import { ApiResponse as ApiResponse_2 } from '@elastic/elasticsearch'; +import { ApplicationStart as ApplicationStart_2 } from 'kibana/public'; +import { Assign } from '@kbn/utility-types'; +import { BehaviorSubject } from 'rxjs'; +import Boom from 'boom'; +import { CoreSetup as CoreSetup_2 } from 'src/core/public'; +import { CoreSetup as CoreSetup_3 } from 'kibana/public'; +import { CoreStart as CoreStart_2 } from 'kibana/public'; +import * as CSS from 'csstype'; +import { EmbeddableStart as EmbeddableStart_2 } from 'src/plugins/embeddable/public/plugin'; +import { Ensure } from '@kbn/utility-types'; +import { EnvironmentMode } from '@kbn/config'; +import { ErrorToastOptions as ErrorToastOptions_2 } from 'src/core/public/notifications'; +import { EuiBreadcrumb } from '@elastic/eui'; +import { EuiButtonEmptyProps } from '@elastic/eui'; +import { EuiComboBoxProps } from '@elastic/eui'; +import { EuiConfirmModalProps } from '@elastic/eui'; +import { EuiContextMenuPanelDescriptor } from '@elastic/eui'; +import { EuiGlobalToastListToast } from '@elastic/eui'; +import { ExclusiveUnion } from '@elastic/eui'; +import { ExpressionAstFunction } from 'src/plugins/expressions/common'; +import { History } from 'history'; +import { Href } from 'history'; +import { IconType } from '@elastic/eui'; +import { ISearchOptions } from 'src/plugins/data/public'; +import { ISearchSource } from 'src/plugins/data/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { IUiSettingsClient as IUiSettingsClient_2 } from 'src/core/public'; +import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; +import { KibanaConfigType } from 'src/core/server/kibana_config'; +import { Location } from 'history'; +import { LocationDescriptorObject } from 'history'; +import { Logger } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; +import { MaybePromise } from '@kbn/utility-types'; +import { Moment } from 'moment'; +import { NameList } from 'elasticsearch'; +import { NotificationsStart as NotificationsStart_2 } from 'src/core/public'; +import { Observable } from 'rxjs'; +import { Optional } from '@kbn/utility-types'; +import { OverlayStart as OverlayStart_2 } from 'src/core/public'; +import { PackageInfo } from '@kbn/config'; +import { Path } from 'history'; +import { PluginInitializerContext } from 'src/core/public'; +import * as PropTypes from 'prop-types'; +import { PublicMethodsOf } from '@kbn/utility-types'; +import { PublicUiSettingsParams } from 'src/core/server/types'; +import React from 'react'; +import { RecursiveReadonly } from '@kbn/utility-types'; +import { RequestAdapter } from 'src/plugins/inspector/common'; +import { Required } from '@kbn/utility-types'; +import * as Rx from 'rxjs'; +import { SavedObject as SavedObject_2 } from 'src/core/server'; +import { SavedObjectAttributes } from 'kibana/server'; +import { SavedObjectAttributes as SavedObjectAttributes_2 } from 'src/core/public'; +import { SavedObjectAttributes as SavedObjectAttributes_3 } from 'kibana/public'; +import { SavedObjectsClientContract as SavedObjectsClientContract_3 } from 'src/core/public'; +import { Search } from '@elastic/elasticsearch/api/requestParams'; +import { SearchResponse } from 'elasticsearch'; +import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; +import { ShallowPromise } from '@kbn/utility-types'; +import { SimpleSavedObject as SimpleSavedObject_2 } from 'src/core/public'; +import { ToastInputFields as ToastInputFields_2 } from 'src/core/public/notifications'; +import { ToastsSetup as ToastsSetup_2 } from 'kibana/public'; +import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; +import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; +import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import { TypeOf } from '@kbn/config-schema'; +import { UiComponent } from 'src/plugins/kibana_utils/public'; +import { UnregisterCallback } from 'history'; +import { UserProvidedValues } from 'src/core/server/types'; + +// Warning: (ae-missing-release-tag) "ACTION_ADD_PANEL" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ACTION_ADD_PANEL = "ACTION_ADD_PANEL"; + +// Warning: (ae-missing-release-tag) "ACTION_EDIT_PANEL" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ACTION_EDIT_PANEL = "editPanel"; + +// Warning: (ae-missing-release-tag) "Adapters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface Adapters { + // (undocumented) + [key: string]: any; +} + +// Warning: (ae-forgotten-export) The symbol "ActionContext" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AddPanelAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class AddPanelAction implements Action_3 { + // Warning: (ae-forgotten-export) The symbol "React" needs to be exported by the entry point index.d.ts + constructor(getFactory: EmbeddableStart_2['getEmbeddableFactory'], getAllFactories: EmbeddableStart_2['getEmbeddableFactories'], overlays: OverlayStart_2, notifications: NotificationsStart_2, SavedObjectFinder: React_2.ComponentType); + // (undocumented) + execute(context: ActionExecutionContext_2): Promise; + // (undocumented) + getDisplayName(): string; + // (undocumented) + getIconType(): string; + // (undocumented) + readonly id = "ACTION_ADD_PANEL"; + // (undocumented) + isCompatible(context: ActionExecutionContext_2): Promise; + // (undocumented) + readonly type = "ACTION_ADD_PANEL"; +} + +// Warning: (ae-missing-release-tag) "ChartActionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ChartActionContext = ValueClickContext | RangeSelectContext; + +// Warning: (ae-missing-release-tag) "Container" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export abstract class Container = {}, TContainerInput extends ContainerInput = ContainerInput, TContainerOutput extends ContainerOutput = ContainerOutput> extends Embeddable implements IContainer { + constructor(input: TContainerInput, output: TContainerOutput, getFactory: EmbeddableStart['getEmbeddableFactory'], parent?: Container); + // (undocumented) + addNewEmbeddable = IEmbeddable>(type: string, explicitInput: Partial): Promise; + // (undocumented) + protected readonly children: { + [key: string]: IEmbeddable | ErrorEmbeddable; + }; + // (undocumented) + protected createNewPanelState>(factory: EmbeddableFactory, partial?: Partial): PanelState; + // (undocumented) + destroy(): void; + // (undocumented) + getChild(id: string): E; + // (undocumented) + getChildIds(): string[]; + // (undocumented) + protected readonly getFactory: EmbeddableStart['getEmbeddableFactory']; + protected abstract getInheritedInput(id: string): TChildInput; + // (undocumented) + getInputForChild(embeddableId: string): TEmbeddableInput; + // (undocumented) + protected getPanelState(embeddableId: string): PanelState; + // (undocumented) + readonly isContainer: boolean; + // (undocumented) + reload(): void; + // (undocumented) + removeEmbeddable(embeddableId: string): void; + // (undocumented) + untilEmbeddableLoaded(id: string): Promise; + // (undocumented) + updateInputForChild(id: string, changes: Partial): void; +} + +// Warning: (ae-missing-release-tag) "ContainerInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ContainerInput extends EmbeddableInput { + // (undocumented) + hidePanelTitles?: boolean; + // (undocumented) + panels: { + [key: string]: PanelState; + }; +} + +// Warning: (ae-missing-release-tag) "ContainerOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ContainerOutput extends EmbeddableOutput { + // (undocumented) + embeddableLoaded: { + [key: string]: boolean; + }; +} + +// Warning: (ae-missing-release-tag) "CONTEXT_MENU_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const CONTEXT_MENU_TRIGGER = "CONTEXT_MENU_TRIGGER"; + +// Warning: (ae-forgotten-export) The symbol "Trigger" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "contextMenuTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const contextMenuTrigger: Trigger<'CONTEXT_MENU_TRIGGER'>; + +// Warning: (ae-missing-release-tag) "defaultEmbeddableFactoryProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const defaultEmbeddableFactoryProvider: = IEmbeddable, T extends SavedObjectAttributes_3 = SavedObjectAttributes_3>(def: EmbeddableFactoryDefinition) => EmbeddableFactory; + +// Warning: (ae-forgotten-export) The symbol "ActionContext" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EditPanelAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class EditPanelAction implements Action_3 { + constructor(getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'], application: ApplicationStart_2, stateTransfer?: EmbeddableStateTransfer | undefined); + // (undocumented) + currentAppId: string | undefined; + // (undocumented) + execute(context: ActionContext_3): Promise; + // Warning: (ae-forgotten-export) The symbol "NavigationContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getAppTarget({ embeddable }: ActionContext_3): NavigationContext | undefined; + // (undocumented) + getDisplayName({ embeddable }: ActionContext_3): string; + // (undocumented) + getHref({ embeddable }: ActionContext_3): Promise; + // (undocumented) + getIconType(): string; + // (undocumented) + readonly id = "editPanel"; + // (undocumented) + isCompatible({ embeddable }: ActionContext_3): Promise; + // (undocumented) + order: number; + // (undocumented) + readonly type = "editPanel"; +} + +// Warning: (ae-missing-release-tag) "Embeddable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export abstract class Embeddable implements IEmbeddable { + constructor(input: TEmbeddableInput, output: TEmbeddableOutput, parent?: IContainer); + destroy(): void; + // (undocumented) + getInput$(): Readonly>; + // (undocumented) + getInput(): Readonly; + getInspectorAdapters(): Adapters | undefined; + // (undocumented) + getIsContainer(): this is IContainer; + // (undocumented) + getOutput$(): Readonly>; + // (undocumented) + getOutput(): Readonly; + getRoot(): IEmbeddable | IContainer; + // (undocumented) + getTitle(): string; + // (undocumented) + readonly id: string; + // (undocumented) + protected input: TEmbeddableInput; + // (undocumented) + readonly isContainer: boolean; + // (undocumented) + protected output: TEmbeddableOutput; + // (undocumented) + readonly parent?: IContainer; + abstract reload(): void; + // (undocumented) + render(el: HTMLElement): void; + // Warning: (ae-forgotten-export) The symbol "RenderCompleteDispatcher" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected renderComplete: RenderCompleteDispatcher; + // (undocumented) + static runtimeId: number; + // (undocumented) + readonly runtimeId: number; + // Warning: (ae-forgotten-export) The symbol "TriggerContextMapping" needs to be exported by the entry point index.d.ts + // + // (undocumented) + supportedTriggers(): Array; + // (undocumented) + abstract readonly type: string; + // (undocumented) + updateInput(changes: Partial): void; + // (undocumented) + protected updateOutput(outputChanges: Partial): void; +} + +// Warning: (ae-forgotten-export) The symbol "State" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddableChildPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class EmbeddableChildPanel extends React.Component { + constructor(props: EmbeddableChildPanelProps); + // (undocumented) + [panel: string]: any; + // (undocumented) + componentDidMount(): Promise; + // (undocumented) + componentWillUnmount(): void; + // (undocumented) + embeddable: IEmbeddable | ErrorEmbeddable; + // (undocumented) + mounted: boolean; + // (undocumented) + render(): JSX.Element; + } + +// Warning: (ae-missing-release-tag) "EmbeddableChildPanelProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableChildPanelProps { + // (undocumented) + className?: string; + // (undocumented) + container: IContainer; + // (undocumented) + embeddableId: string; + // (undocumented) + PanelComponent: EmbeddableStart['EmbeddablePanel']; +} + +// Warning: (ae-missing-release-tag) "EmbeddableContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableContext { + // (undocumented) + embeddable: IEmbeddable; +} + +// @public +export interface EmbeddableEditorState { + // (undocumented) + embeddableId?: string; + // (undocumented) + originatingApp: string; + // (undocumented) + valueInput?: EmbeddableInput; +} + +// Warning: (ae-forgotten-export) The symbol "PersistableState" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddableFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface EmbeddableFactory = IEmbeddable, TSavedObjectAttributes extends SavedObjectAttributes_2 = SavedObjectAttributes_2> extends PersistableState { + canCreateNew(): boolean; + create(initialInput: TEmbeddableInput, parent?: IContainer): Promise; + createFromSavedObject(savedObjectId: string, input: Partial, parent?: IContainer): Promise; + getDefaultInput(partial: Partial): Partial; + getDisplayName(): string; + getExplicitInput(): Promise>; + readonly isContainerType: boolean; + readonly isEditable: () => Promise; + // Warning: (ae-forgotten-export) The symbol "SavedObjectMetaData" needs to be exported by the entry point index.d.ts + // + // (undocumented) + readonly savedObjectMetaData?: SavedObjectMetaData; + // (undocumented) + readonly type: string; +} + +// Warning: (ae-missing-release-tag) "EmbeddableFactoryDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type EmbeddableFactoryDefinition = IEmbeddable, T extends SavedObjectAttributes = SavedObjectAttributes> = Pick, 'create' | 'type' | 'isEditable' | 'getDisplayName'> & Partial, 'createFromSavedObject' | 'isContainerType' | 'getExplicitInput' | 'savedObjectMetaData' | 'canCreateNew' | 'getDefaultInput' | 'telemetry' | 'extract' | 'inject'>>; + +// Warning: (ae-missing-release-tag) "EmbeddableFactoryNotFoundError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class EmbeddableFactoryNotFoundError extends Error { + constructor(type: string); + // (undocumented) + code: string; +} + +// Warning: (ae-missing-release-tag) "EmbeddableInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type EmbeddableInput = { + viewMode?: ViewMode; + title?: string; + id: string; + lastReloadRequestTime?: number; + hidePanelTitles?: boolean; + enhancements?: SerializableState; + disabledActions?: string[]; + disableTriggers?: boolean; + timeRange?: TimeRange; + query?: Query; + filters?: Filter[]; +}; + +// Warning: (ae-missing-release-tag) "EmbeddableInstanceConfiguration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableInstanceConfiguration { + // (undocumented) + id: string; + // (undocumented) + savedObjectId?: string; +} + +// Warning: (ae-missing-release-tag) "EmbeddableOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableOutput { + // (undocumented) + defaultTitle?: string; + // (undocumented) + editable?: boolean; + // (undocumented) + editApp?: string; + // (undocumented) + editPath?: string; + // (undocumented) + editUrl?: string; + // Warning: (ae-forgotten-export) The symbol "EmbeddableError" needs to be exported by the entry point index.d.ts + // + // (undocumented) + error?: EmbeddableError; + // (undocumented) + loading?: boolean; + // (undocumented) + savedObjectId?: string; + // (undocumented) + title?: string; +} + +// @public +export interface EmbeddablePackageState { + // (undocumented) + embeddableId?: string; + // (undocumented) + input: Optional | Optional; + // (undocumented) + type: string; +} + +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "State" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddablePanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class EmbeddablePanel extends React.Component { + constructor(props: Props); + // (undocumented) + closeMyContextMenuPanel: () => void; + // (undocumented) + componentDidMount(): void; + // (undocumented) + componentWillUnmount(): void; + // (undocumented) + onBlur: (blurredPanelIndex: string) => void; + // (undocumented) + onFocus: (focusedPanelIndex: string) => void; + // (undocumented) + render(): JSX.Element; + // (undocumented) + UNSAFE_componentWillMount(): void; +} + +// Warning: (ae-missing-release-tag) "EmbeddablePanelHOC" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type EmbeddablePanelHOC = React.FC<{ + embeddable: IEmbeddable; + hideHeader?: boolean; +}>; + +// @public +export const EmbeddableRenderer: (props: EmbeddableRendererProps) => JSX.Element; + +// Warning: (ae-forgotten-export) The symbol "EmbeddableRendererPropsWithEmbeddable" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "EmbeddableRendererWithFactory" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddableRendererProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type EmbeddableRendererProps = EmbeddableRendererPropsWithEmbeddable | EmbeddableRendererWithFactory; + +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddableRoot" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class EmbeddableRoot extends React.Component { + constructor(props: Props_2); + // (undocumented) + componentDidMount(): void; + // (undocumented) + componentDidUpdate(prevProps?: Props_2): void; + // (undocumented) + render(): JSX.Element; + // (undocumented) + shouldComponentUpdate(newProps: Props_2): boolean; +} + +// Warning: (ae-missing-release-tag) "EmbeddableSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableSetup { + // (undocumented) + registerEmbeddableFactory: = IEmbeddable>(id: string, factory: EmbeddableFactoryDefinition) => () => EmbeddableFactory; + // (undocumented) + registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; + // Warning: (ae-forgotten-export) The symbol "EmbeddableFactoryProvider" needs to be exported by the entry point index.d.ts + // + // (undocumented) + setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void; +} + +// Warning: (ae-missing-release-tag) "EmbeddableSetupDependencies" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableSetupDependencies { + // Warning: (ae-forgotten-export) The symbol "DataPublicPluginSetup" needs to be exported by the entry point index.d.ts + // + // (undocumented) + data: DataPublicPluginSetup; + // Warning: (ae-forgotten-export) The symbol "UiActionsSetup" needs to be exported by the entry point index.d.ts + // + // (undocumented) + uiActions: UiActionsSetup; +} + +// Warning: (ae-missing-release-tag) "EmbeddableStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableStart extends PersistableState { + // (undocumented) + EmbeddablePanel: EmbeddablePanelHOC; + // (undocumented) + getEmbeddableFactories: () => IterableIterator; + // (undocumented) + getEmbeddableFactory: = IEmbeddable>(embeddableFactoryId: string) => EmbeddableFactory | undefined; + // (undocumented) + getEmbeddablePanel: (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC; + // Warning: (ae-forgotten-export) The symbol "ScopedHistory" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer; +} + +// Warning: (ae-missing-release-tag) "EmbeddableStartDependencies" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableStartDependencies { + // Warning: (ae-forgotten-export) The symbol "DataPublicPluginStart" needs to be exported by the entry point index.d.ts + // + // (undocumented) + data: DataPublicPluginStart; + // Warning: (ae-forgotten-export) The symbol "Start" needs to be exported by the entry point index.d.ts + // + // (undocumented) + inspector: Start; + // Warning: (ae-forgotten-export) The symbol "UiActionsStart" needs to be exported by the entry point index.d.ts + // + // (undocumented) + uiActions: UiActionsStart; +} + +// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ScopedHistory" +// +// @public +export class EmbeddableStateTransfer { + // Warning: (ae-forgotten-export) The symbol "ApplicationStart" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "PublicAppInfo" needs to be exported by the entry point index.d.ts + constructor(navigateToApp: ApplicationStart['navigateToApp'], scopedHistory?: ScopedHistory | undefined, appList?: ReadonlyMap | undefined); + getAppNameFromId: (appId: string) => string | undefined; + getIncomingEditorState(options?: { + keysToRemoveAfterFetch?: string[]; + }): EmbeddableEditorState | undefined; + getIncomingEmbeddablePackage(options?: { + keysToRemoveAfterFetch?: string[]; + }): EmbeddablePackageState | undefined; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ApplicationStart" + navigateToEditor(appId: string, options?: { + path?: string; + state: EmbeddableEditorState; + appendToExistingState?: boolean; + }): Promise; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ApplicationStart" + navigateToWithEmbeddablePackage(appId: string, options?: { + path?: string; + state: EmbeddablePackageState; + appendToExistingState?: boolean; + }): Promise; + } + +// Warning: (ae-forgotten-export) The symbol "PersistableStateDefinition" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EnhancementRegistryDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EnhancementRegistryDefinition

extends PersistableStateDefinition

{ + // (undocumented) + id: string; +} + +// Warning: (ae-missing-release-tag) "ErrorEmbeddable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ErrorEmbeddable extends Embeddable { + constructor(error: Error | string, input: EmbeddableInput, parent?: IContainer); + // (undocumented) + destroy(): void; + // (undocumented) + error: Error | string; + // (undocumented) + reload(): void; + // (undocumented) + render(dom: HTMLElement): void; + // (undocumented) + readonly type = "error"; +} + +// Warning: (ae-missing-release-tag) "IContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IContainer = ContainerInput, O extends ContainerOutput = ContainerOutput> extends IEmbeddable { + addNewEmbeddable = Embeddable>(type: string, explicitInput: Partial): Promise; + getChild = Embeddable>(id: string): E; + getInputForChild(id: string): EEI; + removeEmbeddable(embeddableId: string): void; + untilEmbeddableLoaded(id: string): Promise; + updateInputForChild(id: string, changes: Partial): void; +} + +// Warning: (ae-missing-release-tag) "IEmbeddable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IEmbeddable { + destroy(): void; + enhancements?: object; + getInput$(): Readonly>; + getInput(): Readonly; + getInspectorAdapters(): Adapters | undefined; + getIsContainer(): this is IContainer; + getOutput$(): Readonly>; + getOutput(): Readonly; + getRoot(): IEmbeddable | IContainer; + getTitle(): string | undefined; + readonly id: string; + readonly isContainer: boolean; + readonly parent?: IContainer; + reload(): void; + render(domNode: HTMLElement | Element): void; + readonly runtimeId?: number; + supportedTriggers(): Array; + readonly type: string; + updateInput(changes: Partial): void; +} + +// Warning: (ae-missing-release-tag) "isErrorEmbeddable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function isErrorEmbeddable(embeddable: TEmbeddable | ErrorEmbeddable): embeddable is ErrorEmbeddable; + +// Warning: (ae-missing-release-tag) "isRangeSelectTriggerContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const isRangeSelectTriggerContext: (context: ChartActionContext) => context is RangeSelectContext>; + +// Warning: (ae-missing-release-tag) "isReferenceOrValueEmbeddable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function isReferenceOrValueEmbeddable(incoming: unknown): incoming is ReferenceOrValueEmbeddable; + +// Warning: (ae-missing-release-tag) "isSavedObjectEmbeddableInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function isSavedObjectEmbeddableInput(input: EmbeddableInput | SavedObjectEmbeddableInput): input is SavedObjectEmbeddableInput; + +// Warning: (ae-missing-release-tag) "isValueClickTriggerContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const isValueClickTriggerContext: (context: ChartActionContext) => context is ValueClickContext>; + +// Warning: (ae-missing-release-tag) "openAddPanelFlyout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function openAddPanelFlyout(options: { + embeddable: IContainer; + getFactory: EmbeddableStart['getEmbeddableFactory']; + getAllFactories: EmbeddableStart['getEmbeddableFactories']; + overlays: OverlayStart_2; + notifications: NotificationsStart_2; + SavedObjectFinder: React.ComponentType; +}): Promise; + +// Warning: (ae-missing-release-tag) "OutputSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface OutputSpec { + // (undocumented) + [key: string]: PropertySpec; +} + +// Warning: (ae-missing-release-tag) "PANEL_BADGE_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const PANEL_BADGE_TRIGGER = "PANEL_BADGE_TRIGGER"; + +// Warning: (ae-missing-release-tag) "PANEL_NOTIFICATION_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const PANEL_NOTIFICATION_TRIGGER = "PANEL_NOTIFICATION_TRIGGER"; + +// Warning: (ae-missing-release-tag) "panelBadgeTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const panelBadgeTrigger: Trigger<'PANEL_BADGE_TRIGGER'>; + +// Warning: (ae-missing-release-tag) "PanelNotFoundError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class PanelNotFoundError extends Error { + constructor(); + // (undocumented) + code: string; +} + +// Warning: (ae-missing-release-tag) "panelNotificationTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const panelNotificationTrigger: Trigger<'PANEL_NOTIFICATION_TRIGGER'>; + +// Warning: (ae-missing-release-tag) "PanelState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface PanelState { + // (undocumented) + explicitInput: Partial & { + id: string; + }; + // (undocumented) + type: string; +} + +// Warning: (ae-forgotten-export) The symbol "EmbeddablePublicPlugin" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "plugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function plugin(initializerContext: PluginInitializerContext): EmbeddablePublicPlugin; + +// Warning: (ae-missing-release-tag) "PropertySpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface PropertySpec { + // (undocumented) + accessPath: string; + // (undocumented) + description: string; + // (undocumented) + displayName: string; + // (undocumented) + id: string; + // (undocumented) + value?: string; +} + +// Warning: (ae-missing-release-tag) "RangeSelectContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface RangeSelectContext { + // (undocumented) + data: { + table: KibanaDatatable; + column: number; + range: number[]; + timeFieldName?: string; + }; + // (undocumented) + embeddable?: T; +} + +// @public +export interface ReferenceOrValueEmbeddable { + getInputAsRefType: () => Promise; + getInputAsValueType: () => Promise; + inputIsRefType: (input: ValTypeInput | RefTypeInput) => input is RefTypeInput; +} + +// Warning: (ae-missing-release-tag) "SavedObjectEmbeddableInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SavedObjectEmbeddableInput extends EmbeddableInput { + // (undocumented) + savedObjectId: string; +} + +// Warning: (ae-missing-release-tag) "ValueClickContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ValueClickContext { + // (undocumented) + data: { + data: Array<{ + table: Pick; + column: number; + row: number; + value: any; + }>; + timeFieldName?: string; + negate?: boolean; + }; + // (undocumented) + embeddable?: T; +} + +// Warning: (ae-missing-release-tag) "ViewMode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum ViewMode { + // (undocumented) + EDIT = "edit", + // (undocumented) + VIEW = "view" +} + +// Warning: (ae-missing-release-tag) "withEmbeddableSubscription" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const withEmbeddableSubscription: = IEmbeddable, ExtraProps = {}>(WrappedComponent: React.ComponentType<{ + input: I; + output: O; + embeddable: E; +} & ExtraProps>) => React.ComponentType<{ + embeddable: E; +} & ExtraProps>; + + +// Warnings were encountered during analysis: +// +// src/plugins/embeddable/common/types.ts:44:3 - (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts +// src/plugins/embeddable/common/types.ts:59:3 - (ae-forgotten-export) The symbol "TimeRange" needs to be exported by the entry point index.d.ts +// src/plugins/embeddable/common/types.ts:64:3 - (ae-forgotten-export) The symbol "Query" needs to be exported by the entry point index.d.ts +// src/plugins/embeddable/common/types.ts:69:3 - (ae-forgotten-export) The symbol "Filter" needs to be exported by the entry point index.d.ts +// src/plugins/embeddable/public/lib/triggers/triggers.ts:45:5 - (ae-forgotten-export) The symbol "KibanaDatatable" needs to be exported by the entry point index.d.ts + +// (No @packageDocumentation comment for this package) + +``` diff --git a/src/plugins/embeddable/server/server.api.md b/src/plugins/embeddable/server/server.api.md new file mode 100644 index 00000000000000..c4fad2917343ba --- /dev/null +++ b/src/plugins/embeddable/server/server.api.md @@ -0,0 +1,50 @@ +## API Report File for "kibana" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { CoreSetup } from 'kibana/server'; +import { CoreStart } from 'kibana/server'; +import { Plugin } from 'kibana/server'; +import { SavedObjectReference as SavedObjectReference_2 } from 'kibana/server'; + +// Warning: (ae-forgotten-export) The symbol "EmbeddableInput" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "PersistableStateDefinition" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddableRegistryDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableRegistryDefinition

extends PersistableStateDefinition

{ + // (undocumented) + id: string; +} + +// Warning: (ae-missing-release-tag) "EmbeddableSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EmbeddableSetup { + // (undocumented) + registerEmbeddableFactory: (factory: EmbeddableRegistryDefinition) => void; + // (undocumented) + registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; +} + +// Warning: (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EnhancementRegistryDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface EnhancementRegistryDefinition

extends PersistableStateDefinition

{ + // (undocumented) + id: string; +} + +// Warning: (ae-forgotten-export) The symbol "EmbeddableServerPlugin" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "plugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const plugin: () => EmbeddableServerPlugin; + + +// (No @packageDocumentation comment for this package) + +``` From 6f1a15840322750543c7c0f13f47be37f40d9ab0 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 30 Sep 2020 15:22:55 -0400 Subject: [PATCH 04/36] [Security Solution][Resolver] New mock with cursor (#78863) * New mock with cursor * include next cursor Co-authored-by: Elastic Machine --- ...ith_related_events_and_cursor_on_origin.ts | 147 ++++++++++++++++++ .../public/resolver/mocks/resolver_tree.ts | 13 +- 2 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_and_cursor_on_origin.ts diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_and_cursor_on_origin.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_and_cursor_on_origin.ts new file mode 100644 index 00000000000000..7682165ac5e94f --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_and_cursor_on_origin.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DataAccessLayer } from '../../types'; +import { + mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin, + firstRelatedEventID, + secondRelatedEventID, +} from '../../mocks/resolver_tree'; +import { + ResolverRelatedEvents, + ResolverTree, + ResolverEntityIndex, + SafeResolverEvent, +} from '../../../../common/endpoint/types'; +import * as eventModel from '../../../../common/endpoint/models/event'; + +interface Metadata { + /** + * The `_id` of the document being analyzed. + */ + databaseDocumentID: string; + /** + * A record of entityIDs to be used in tests assertions. + */ + entityIDs: { + /** + * The entityID of the node related to the document being analyzed. + */ + origin: 'origin'; + /** + * The entityID of the first child of the origin. + */ + firstChild: 'firstChild'; + /** + * The entityID of the second child of the origin. + */ + secondChild: 'secondChild'; + }; +} + +/** + * See the other mock `noAncestorsTwoChildrenWithRelatedEventsOnOrigin` but this one + * has one of the related events "after" the first (i.e. you have to call with `after` to + * get the second one). + */ +export function noAncestorsTwoChildrenWithRelatedEventsOnOriginWithOneAfterCursor(): { + dataAccessLayer: DataAccessLayer; + metadata: Metadata; +} { + const metadata: Metadata = { + databaseDocumentID: '_id', + entityIDs: { origin: 'origin', firstChild: 'firstChild', secondChild: 'secondChild' }, + }; + const tree = mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin({ + originID: metadata.entityIDs.origin, + firstChildID: metadata.entityIDs.firstChild, + secondChildID: metadata.entityIDs.secondChild, + }); + + return { + metadata, + dataAccessLayer: { + /** + * Fetch related events for an entity ID + */ + async relatedEvents(entityID: string): Promise { + /** + * Respond with the mocked related events when the origin's related events are fetched. + **/ + const events = entityID === metadata.entityIDs.origin ? tree.relatedEvents.events : []; + + return { + entityID, + events, + nextEvent: null, + }; + }, + + /** + * Any of the origin's related events by category. + * `entityID` must match the origin node's `process.entity_id`. + * These are split by the `after` cursor: Calling without the cursor will + * return the first event, calling with the cursor set to the id of the first event + * will return the second. + */ + async eventsWithEntityIDAndCategory( + entityID: string, + category: string, + after?: string + ): Promise<{ events: SafeResolverEvent[]; nextEvent: string | null }> { + /** + * For testing: This 'fakes' the behavior of one related event being `after` + * a cursor for an earlier event. + * @param event A `SafeResolverEvent` to filter + */ + function splitOnCursor(event: SafeResolverEvent) { + if (typeof after === 'undefined') { + return eventModel.eventID(event) === firstRelatedEventID; + } + if (after === firstRelatedEventID) { + return eventModel.eventID(event) === secondRelatedEventID; + } + return false; + } + + const events = + entityID === metadata.entityIDs.origin + ? tree.relatedEvents.events.filter( + (event) => + eventModel.eventCategory(event).includes(category) && splitOnCursor(event) + ) + : []; + return { + events, + nextEvent: typeof after === 'undefined' ? firstRelatedEventID : null, + }; + }, + + /** + * Any of the origin's related events by event.id + */ + async event(eventID: string): Promise { + return ( + tree.relatedEvents.events.find((event) => eventModel.eventID(event) === eventID) ?? null + ); + }, + + /** + * Fetch a ResolverTree for a entityID + */ + async resolverTree(): Promise { + return tree; + }, + + /** + * Get entities matching a document. + */ + async entities(): Promise { + return [{ entity_id: metadata.entityIDs.origin }]; + }, + }, + }; +} diff --git a/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts index 3f7c58efc762b9..5b851d588543df 100644 --- a/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts +++ b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts @@ -307,6 +307,15 @@ export function mockTreeWithNoProcessEvents(): ResolverTree { }; } +/** + * first ID (to check in the mock data access layer) + */ +export const firstRelatedEventID = 'id of first related event'; +/** + * second ID (to check in the mock data access layer) + */ +export const secondRelatedEventID = 'id of second related event'; + export function mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin({ originID, firstChildID, @@ -326,14 +335,14 @@ export function mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin({ mockEndpointEvent({ entityID: originID, parentEntityID, - eventID: 'first related event', + eventID: firstRelatedEventID, eventType: 'access', eventCategory: 'registry', }), mockEndpointEvent({ entityID: originID, parentEntityID, - eventID: 'second related event', + eventID: secondRelatedEventID, eventType: 'access', eventCategory: 'registry', }), From 28e138281321d11b2de9a7dd5d66981d557d79ed Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Wed, 30 Sep 2020 12:32:09 -0700 Subject: [PATCH 05/36] Fix KQL autocomplete value suggestions (#78676) --- .../suggestion_component.test.tsx.snap | 2 ++ .../ui/typeahead/suggestion_component.tsx | 4 ++- test/functional/services/query_bar.ts | 5 +++ .../providers/kql_query_suggestion/value.ts | 26 +++++++-------- x-pack/test/functional/apps/discover/index.ts | 1 + .../apps/discover/value_suggestions.ts | 33 +++++++++++++++++++ 6 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 x-pack/test/functional/apps/discover/value_suggestions.ts diff --git a/src/plugins/data/public/ui/typeahead/__snapshots__/suggestion_component.test.tsx.snap b/src/plugins/data/public/ui/typeahead/__snapshots__/suggestion_component.test.tsx.snap index 38b570c86c6c5f..19ce92178d5e40 100644 --- a/src/plugins/data/public/ui/typeahead/__snapshots__/suggestion_component.test.tsx.snap +++ b/src/plugins/data/public/ui/typeahead/__snapshots__/suggestion_component.test.tsx.snap @@ -22,6 +22,7 @@ exports[`SuggestionComponent Should display the suggestion and use the provided

as promised, not helpful
@@ -56,6 +57,7 @@ exports[`SuggestionComponent Should make the element active if the selected prop
as promised, not helpful
diff --git a/src/plugins/data/public/ui/typeahead/suggestion_component.tsx b/src/plugins/data/public/ui/typeahead/suggestion_component.tsx index 724287b874bf70..20cb60ddab9e15 100644 --- a/src/plugins/data/public/ui/typeahead/suggestion_component.tsx +++ b/src/plugins/data/public/ui/typeahead/suggestion_component.tsx @@ -72,7 +72,9 @@ export function SuggestionComponent(props: Props) {
-
{props.suggestion.text}
+
+ {props.suggestion.text} +
{props.shouldDisplayDescription && (
{props.suggestion.description}
)} diff --git a/test/functional/services/query_bar.ts b/test/functional/services/query_bar.ts index 8cd63fb2f4a510..3b4645bc428215 100644 --- a/test/functional/services/query_bar.ts +++ b/test/functional/services/query_bar.ts @@ -87,6 +87,11 @@ export function QueryBarProvider({ getService, getPageObjects }: FtrProviderCont const queryLanguageButton = await testSubjects.find('switchQueryLanguageButton'); expect((await queryLanguageButton.getVisibleText()).toLowerCase()).to.eql(lang); } + + public async getSuggestions() { + const suggestions = await testSubjects.findAll('autoCompleteSuggestionText'); + return Promise.all(suggestions.map((suggestion) => suggestion.getVisibleText())); + } } return new QueryBar(); diff --git a/x-pack/plugins/data_enhanced/public/autocomplete/providers/kql_query_suggestion/value.ts b/x-pack/plugins/data_enhanced/public/autocomplete/providers/kql_query_suggestion/value.ts index 6f9ba4d00109dd..441e5a6f775dd5 100644 --- a/x-pack/plugins/data_enhanced/public/autocomplete/providers/kql_query_suggestion/value.ts +++ b/x-pack/plugins/data_enhanced/public/autocomplete/providers/kql_query_suggestion/value.ts @@ -9,6 +9,8 @@ import { escapeQuotes } from './lib/escape_kuery'; import { KqlQuerySuggestionProvider } from './types'; import { getAutocompleteService } from '../../../services'; import { + IFieldType, + IIndexPattern, QuerySuggestion, QuerySuggestionTypes, } from '../../../../../../../src/plugins/data/public'; @@ -23,29 +25,27 @@ const wrapAsSuggestions = (start: number, end: number, query: string, values: st end, })); -export const setupGetValueSuggestions: KqlQuerySuggestionProvider = (core) => { +export const setupGetValueSuggestions: KqlQuerySuggestionProvider = () => { return async ( { indexPatterns, boolFilter, signal }, { start, end, prefix, suffix, fieldName, nestedPath } ): Promise => { - const allFields = flatten( - indexPatterns.map((indexPattern) => - indexPattern.fields.map((field) => ({ - ...field, - indexPattern, - })) - ) - ); - const fullFieldName = nestedPath ? `${nestedPath}.${fieldName}` : fieldName; - const fields = allFields.filter((field) => field.name === fullFieldName); + + const indexPatternFieldEntries: Array<[IIndexPattern, IFieldType]> = []; + indexPatterns.forEach((indexPattern) => { + indexPattern.fields + .filter((field) => field.name === fullFieldName) + .forEach((field) => indexPatternFieldEntries.push([indexPattern, field])); + }); + const query = `${prefix}${suffix}`.trim(); const { getValueSuggestions } = getAutocompleteService(); const data = await Promise.all( - fields.map((field) => + indexPatternFieldEntries.map(([indexPattern, field]) => getValueSuggestions({ - indexPattern: field.indexPattern, + indexPattern, field, query, boolFilter, diff --git a/x-pack/test/functional/apps/discover/index.ts b/x-pack/test/functional/apps/discover/index.ts index 759225d80fa206..816428f7b3cc30 100644 --- a/x-pack/test/functional/apps/discover/index.ts +++ b/x-pack/test/functional/apps/discover/index.ts @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./async_scripted_fields')); loadTestFile(require.resolve('./reporting')); loadTestFile(require.resolve('./error_handling')); + loadTestFile(require.resolve('./value_suggestions')); }); } diff --git a/x-pack/test/functional/apps/discover/value_suggestions.ts b/x-pack/test/functional/apps/discover/value_suggestions.ts new file mode 100644 index 00000000000000..54720b94172f61 --- /dev/null +++ b/x-pack/test/functional/apps/discover/value_suggestions.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const queryBar = getService('queryBar'); + const PageObjects = getPageObjects(['common']); + + describe('value suggestions', function describeIndexTests() { + before(async function () { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.load('dashboard/drilldowns'); + await PageObjects.common.navigateToApp('discover'); + }); + + after(async () => { + await esArchiver.unload('dashboard/drilldowns'); + }); + + it('show up', async () => { + await queryBar.setQuery('extension.raw : '); + const suggestions = await queryBar.getSuggestions(); + expect(suggestions.length).to.be(5); + expect(suggestions).to.contain('"jpg"'); + }); + }); +} From 7c217a72f70ee9846db876f9ad2b844c1c7214b9 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Wed, 30 Sep 2020 15:32:59 -0400 Subject: [PATCH 06/36] [Design] A couple fixes for 7.10 (#78801) * A few header and KQL bar fixes * More fixes (alerts, modals) * Fixing bottom padding for advanced settings bottom bar * One more alerts responsive fix * Snaps --- .../header/__snapshots__/header.test.tsx.snap | 280 ++++++++++++------ src/core/public/chrome/ui/header/header.tsx | 13 +- .../management_app/_advanced_settings.scss | 4 + .../management_app/components/form/form.tsx | 9 +- .../ui/query_string_input/no_data_popover.tsx | 1 + .../ui/typeahead/suggestions_component.tsx | 2 +- .../saved_object_save_modal.test.tsx.snap | 1 + .../save_modal/saved_object_save_modal.tsx | 9 +- .../public/components/search_bar.tsx | 3 + .../action_wizard/action_wizard.tsx | 4 +- .../drilldown_hello_bar.tsx | 6 +- .../components/flyout_frame/flyout_frame.tsx | 2 +- .../threshold_watch_action_dropdown.tsx | 2 +- 13 files changed, 226 insertions(+), 110 deletions(-) diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 8937ecb4d9b4e3..2db3eede16e253 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -1907,62 +1907,133 @@ exports[`Header renders 1`] = ` Object { "borders": "none", "items": Array [ - + , + /> + , ], }, Object { "borders": "none", "items": Array [ + + + , - + + /> + @@ -3117,6 +3198,24 @@ exports[`Header renders 1`] = ` +
+ +
+
+
- -
- ], + items: [ + + + , + ], }), borders: 'none', }, { items: [ + + + , - - diff --git a/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss b/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss index 66ae9cca3f83b7..fd26677e938942 100644 --- a/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss +++ b/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss @@ -64,3 +64,7 @@ .mgtAdvancedSettingsForm__button { width: 100%; } + +.kbnBody--mgtAdvancedSettingsHasBottomBar .mgtPage__body { + padding-bottom: $euiSizeXL * 2; +} 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 497252b75fa582..d243d85e12a665 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 @@ -388,6 +388,13 @@ export class Form extends PureComponent { const { unsavedChanges } = this.state; const { visibleSettings, categories, categoryCounts, clearQuery } = this.props; const currentCategories: Category[] = []; + const hasUnsavedChanges = !isEmpty(unsavedChanges); + + if (hasUnsavedChanges) { + document.body.classList.add('kbnBody--mgtAdvancedSettingsHasBottomBar'); + } else { + document.body.classList.remove('kbnBody--mgtAdvancedSettingsHasBottomBar'); + } categories.forEach((category) => { if (visibleSettings[category] && visibleSettings[category].length) { @@ -408,7 +415,7 @@ export class Form extends PureComponent { }) : this.maybeRenderNoSettings(clearQuery)}
- {!isEmpty(unsavedChanges) && this.renderBottomBar()} + {hasUnsavedChanges && this.renderBottomBar()} ); } diff --git a/src/plugins/data/public/ui/query_string_input/no_data_popover.tsx b/src/plugins/data/public/ui/query_string_input/no_data_popover.tsx index 561c33519f96fc..7d5282a0545bc5 100644 --- a/src/plugins/data/public/ui/query_string_input/no_data_popover.tsx +++ b/src/plugins/data/public/ui/query_string_input/no_data_popover.tsx @@ -63,6 +63,7 @@ export function NoDataPopover({ } minWidth={300} anchorPosition="downCenter" + anchorClassName="eui-displayBlock" step={1} stepsTotal={1} isStepOpen={noDataPopoverVisible} diff --git a/src/plugins/data/public/ui/typeahead/suggestions_component.tsx b/src/plugins/data/public/ui/typeahead/suggestions_component.tsx index 50ed9e9542d369..054c6318b9772c 100644 --- a/src/plugins/data/public/ui/typeahead/suggestions_component.tsx +++ b/src/plugins/data/public/ui/typeahead/suggestions_component.tsx @@ -154,7 +154,7 @@ export class SuggestionsComponent extends Component { const StyledSuggestionsListDiv = styled.div` ${(props: { queryBarRect: DOMRect; verticalListPosition: string }) => ` position: absolute; - z-index: 4001; + z-index: 999; left: ${props.queryBarRect.left}px; width: ${props.queryBarRect.width}px; ${props.verticalListPosition}`} diff --git a/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap b/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap index eff5ab4f1e2c7a..c923c5c2aed900 100644 --- a/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap +++ b/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap @@ -25,6 +25,7 @@ exports[`SavedObjectSaveModal should render matching snapshot 1`] = ` + {!this.props.showDescription && this.props.description && ( - - {this.props.description} - + + {this.props.description} + )} + + + {this.renderCopyOnSave()} = ({ data-test-subj={`${TEST_SUBJ_SELECTED_ACTION_FACTORY}-${actionFactory.id}`} >
- + {actionFactory.getIconType(context) && ( @@ -342,7 +342,7 @@ const ActionFactorySelector: React.FC = ({ }; return ( - + {ensureOrder(actionFactories).map((actionFactory) => ( = ({ }) => { return ( - + -
- -
+
diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.tsx index b55cbd88d0dc06..e518209746b605 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.tsx @@ -64,7 +64,7 @@ export const FlyoutFrame: React.FC = ({ const footerFragment = (onClose || footer) && ( - + {onClose && ( = ({ settings, setIsPopOverOpen(false); }} > - + From 5ed8fef08b2d1fbaae23dac7afa65dab824164e2 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 30 Sep 2020 15:33:51 -0400 Subject: [PATCH 07/36] [ML] DF Analytics creation wizard: replace select input with job type cards with icons (#78872) * replace select input with job type cards with icons * update functional tests * remove unusued translations * ensure jobType cards remain same size and add translations --- .../configuration_step/job_type.tsx | 128 ++++++++++-------- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../ml/data_frame_analytics_creation.ts | 20 +-- 4 files changed, 80 insertions(+), 72 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/job_type.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/job_type.tsx index 1e6a616fedd64d..31c7672ed4fcc7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/job_type.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/job_type.tsx @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC } from 'react'; +import React, { FC, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiSelect } from '@elastic/eui'; +import { EuiCard, EuiIcon, EuiFlexItem, EuiFlexGroup, EuiSpacer } from '@elastic/eui'; import { ANALYSIS_CONFIG_TYPE } from '../../../../../../../common/constants/data_frame_analytics'; +import { DataFrameAnalysisConfigType } from '../../../../../../../common/types/data_frame_analytics'; import { AnalyticsJobType } from '../../../analytics_management/hooks/use_create_analytics_form/state'; @@ -17,64 +18,81 @@ interface Props { setFormState: React.Dispatch>; } -export const JobType: FC = ({ type, setFormState }) => { - const outlierHelpText = i18n.translate( - 'xpack.ml.dataframe.analytics.create.outlierDetectionHelpText', - { - defaultMessage: 'Outlier detection identifies unusual data points in the data set.', - } - ); +interface Details { + helpText: string; + icon: string; + title: string; +} - const regressionHelpText = i18n.translate( - 'xpack.ml.dataframe.analytics.create.outlierRegressionHelpText', - { - defaultMessage: 'Regression predicts numerical values in the data set.', - } - ); +type JobDetails = Record; - const classificationHelpText = i18n.translate( - 'xpack.ml.dataframe.analytics.create.classificationHelpText', - { +const jobDetails: JobDetails = { + [ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION]: { + helpText: i18n.translate('xpack.ml.dataframe.analytics.create.outlierDetectionHelpText', { + defaultMessage: 'Outlier detection identifies unusual data points in the data set.', + }), + icon: 'outlierDetectionJob', + title: i18n.translate('xpack.ml.dataframe.analytics.create.outlierDetectionTitle', { + defaultMessage: 'Outlier detection', + }), + }, + [ANALYSIS_CONFIG_TYPE.REGRESSION]: { + helpText: i18n.translate('xpack.ml.dataframe.analytics.create.regressionHelpText', { + defaultMessage: 'Regression predicts numerical values in the data set.', + }), + icon: 'regressionJob', + title: i18n.translate('xpack.ml.dataframe.analytics.create.regressionTitle', { + defaultMessage: 'Regression', + }), + }, + [ANALYSIS_CONFIG_TYPE.CLASSIFICATION]: { + helpText: i18n.translate('xpack.ml.dataframe.analytics.create.classificationHelpText', { defaultMessage: 'Classification predicts labels of data points in the data set.', - } - ); + }), + icon: 'classificationJob', + title: i18n.translate('xpack.ml.dataframe.analytics.create.classificationTitle', { + defaultMessage: 'Classification', + }), + }, +}; - const helpText = { - [ANALYSIS_CONFIG_TYPE.REGRESSION]: regressionHelpText, - [ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION]: outlierHelpText, - [ANALYSIS_CONFIG_TYPE.CLASSIFICATION]: classificationHelpText, - }; +export const JobType: FC = ({ type, setFormState }) => { + const [selectedCard, setSelectedCard] = useState({}); return ( - - - ({ - value: jobType, - text: jobType.replace(/_/g, ' '), - 'data-test-subj': `mlAnalyticsCreation-${jobType}-option`, - }))} - value={type} - hasNoInitialSelection={true} - onChange={(e) => { - const value = e.target.value as AnalyticsJobType; - setFormState({ - previousJobType: type, - jobType: value, - includes: [], - requiredFieldsError: undefined, - }); - }} - data-test-subj="mlAnalyticsCreateJobWizardJobTypeSelect" - /> - - + <> + + {(Object.keys(jobDetails) as Array).map((jobType) => ( + + } + title={jobDetails[jobType].title} + description={jobDetails[jobType].helpText} + data-test-subj={`mlAnalyticsCreation-${jobType}-option${ + type === jobType ? ' selectedJobType' : '' + }`} + selectable={{ + onClick: () => { + // Only allow one job selected at a time and don't allow deselection + if (selectedCard[jobType] === true) { + return; + } + + setFormState({ + previousJobType: type, + jobType, + includes: [], + requiredFieldsError: undefined, + }); + setSelectedCard({ [jobType]: !selectedCard[jobType] }); + }, + isSelected: selectedCard[jobType] === true || type === jobType, + }} + /> + + ))} + + + ); }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0b2c3b1231e4b6..34ff32244035a0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10678,7 +10678,6 @@ "xpack.ml.dataframe.analytics.create.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, one {# 文字} other {# 文字}} 以内でなければなりません。", "xpack.ml.dataframe.analytics.create.jobIdLabel": "ジョブID", "xpack.ml.dataframe.analytics.create.jobIdPlaceholder": "ジョブID", - "xpack.ml.dataframe.analytics.create.jobTypeLabel": "ジョブタイプ", "xpack.ml.dataframe.analytics.create.lambdaHelpText": "学習データセットの過剰適合を防止するための正則化パラメーター。非負の値でなければなりません。", "xpack.ml.dataframe.analytics.create.lambdaInputAriaLabel": "学習データセットの過剰適合を防止するための正則化パラメーター。", "xpack.ml.dataframe.analytics.create.lambdaLabel": "ラムダ", @@ -10711,7 +10710,6 @@ "xpack.ml.dataframe.analytics.create.outlierFractionHelpText": "異常値検出の前に異常であると想定されるデータセットの比率を設定します。", "xpack.ml.dataframe.analytics.create.outlierFractionInputAriaLabel": "異常値検出の前に異常であると想定されるデータセットの比率を設定します。", "xpack.ml.dataframe.analytics.create.outlierFractionLabel": "異常値割合", - "xpack.ml.dataframe.analytics.create.outlierRegressionHelpText": "回帰はデータセットにおける数値を予測します。", "xpack.ml.dataframe.analytics.create.predictionFieldNameHelpText": "結果で予測フィールドの名前を定義します。デフォルトは_predictionです。", "xpack.ml.dataframe.analytics.create.predictionFieldNameLabel": "予測フィールド名", "xpack.ml.dataframe.analytics.create.randomizeSeedInputAriaLabel": "学習で使用されるドキュメントを選択するために使用される乱数生成器のシード", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4d753b26fd7e3d..db59493002987a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10684,7 +10684,6 @@ "xpack.ml.dataframe.analytics.create.jobIdInvalidMaxLengthErrorMessage": "作业 ID 的长度不得超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", "xpack.ml.dataframe.analytics.create.jobIdLabel": "作业 ID", "xpack.ml.dataframe.analytics.create.jobIdPlaceholder": "作业 ID", - "xpack.ml.dataframe.analytics.create.jobTypeLabel": "作业类型", "xpack.ml.dataframe.analytics.create.lambdaHelpText": "在训练数据集上防止过度拟合的正则化参数。必须为非负值。", "xpack.ml.dataframe.analytics.create.lambdaInputAriaLabel": "在训练数据集上防止过度拟合的正则化参数。", "xpack.ml.dataframe.analytics.create.lambdaLabel": "Lambda", @@ -10717,7 +10716,6 @@ "xpack.ml.dataframe.analytics.create.outlierFractionHelpText": "设置在离群值检测之前被假设为离群的数据集比例。", "xpack.ml.dataframe.analytics.create.outlierFractionInputAriaLabel": "设置在离群值检测之前被假设为离群的数据集比例。", "xpack.ml.dataframe.analytics.create.outlierFractionLabel": "离群值比例", - "xpack.ml.dataframe.analytics.create.outlierRegressionHelpText": "回归用于预测数据集中的数值。", "xpack.ml.dataframe.analytics.create.predictionFieldNameHelpText": "定义结果中预测字段的名称。默认为 _prediction。", "xpack.ml.dataframe.analytics.create.predictionFieldNameLabel": "预测字段名称", "xpack.ml.dataframe.analytics.create.randomizeSeedInputAriaLabel": "用于选取哪个文档用于训练的随机生成器种子", diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index e01e065867ac79..71d68b16c4037b 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -29,23 +29,16 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( await testSubjects.existOrFail('mlAnalyticsCreateJobWizardJobTypeSelect'); }, - async assertJobTypeSelection(expectedSelection: string) { + async assertJobTypeSelection(jobTypeAttribute: string) { await retry.tryForTime(5000, async () => { - const actualSelection = await testSubjects.getAttribute( - 'mlAnalyticsCreateJobWizardJobTypeSelect', - 'value' - ); - expect(actualSelection).to.eql( - expectedSelection, - `Job type selection should be '${expectedSelection}' (got '${actualSelection}')` - ); + await testSubjects.existOrFail(`${jobTypeAttribute} selectedJobType`); }); }, async selectJobType(jobType: string) { - await testSubjects.click('mlAnalyticsCreateJobWizardJobTypeSelect'); - await testSubjects.click(`mlAnalyticsCreation-${jobType}-option`); - await this.assertJobTypeSelection(jobType); + const jobTypeAttribute = `mlAnalyticsCreation-${jobType}-option`; + await testSubjects.click(jobTypeAttribute); + await this.assertJobTypeSelection(jobTypeAttribute); }, async assertAdvancedEditorSwitchExists() { @@ -505,7 +498,8 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async assertInitialCloneJobConfigStep(job: DataFrameAnalyticsConfig) { const jobType = Object.keys(job.analysis)[0]; - await this.assertJobTypeSelection(jobType); + const jobTypeAttribute = `mlAnalyticsCreation-${jobType}-option`; + await this.assertJobTypeSelection(jobTypeAttribute); if (isClassificationAnalysis(job.analysis) || isRegressionAnalysis(job.analysis)) { await this.assertDependentVariableSelection([job.analysis[jobType].dependent_variable]); await this.assertTrainingPercentValue(String(job.analysis[jobType].training_percent)); From df86dcb215b4f5e5ead657ea15b1e836549d1c0c Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Wed, 30 Sep 2020 15:36:01 -0400 Subject: [PATCH 08/36] remove file accidentally checked in (#79005) --- .../architecture/code-exploration.asciidoc | 598 ------------------ 1 file changed, 598 deletions(-) delete mode 100644 docs/developer/architecture/code-exploration.asciidoc diff --git a/docs/developer/architecture/code-exploration.asciidoc b/docs/developer/architecture/code-exploration.asciidoc deleted file mode 100644 index 4a390336da34fc..00000000000000 --- a/docs/developer/architecture/code-exploration.asciidoc +++ /dev/null @@ -1,598 +0,0 @@ -//// - -NOTE: - This is an automatically generated file. Please do not edit directly. Instead, run the - following from within the kibana repository: - - node scripts/build_plugin_list_docs - - You can update the template within packages/kbn-dev-utils/target/plugin_list/generate_plugin_list.js - -//// - -[[code-exploration]] -== Exploring Kibana code - -The goals of our folder heirarchy are: - -- Easy for developers to know where to add new services, plugins and applications. -- Easy for developers to know where to find the code from services, plugins and applications. -- Easy to browse and understand our folder structure. - -To that aim, we strive to: - -- Avoid too many files in any given folder. -- Choose clear, unambigious folder names. -- Organize by domain. -- Every folder should contain a README that describes the contents of that folder. - -[discrete] -[[kibana-services-applications]] -=== Services and Applications - -[discrete] -==== src/plugins - -- {kib-repo}blob/{branch}/src/plugins/advanced_settings[advancedSettings] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/apm_oss[apmOss] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/bfetch/README.md[bfetch] - -bfetch allows to batch HTTP requests and streams responses back. - - -- {kib-repo}blob/{branch}/src/plugins/charts/README.md[charts] - -The Charts plugin is a way to create easier integration of shared colors, themes, types and other utilities across all Kibana charts and visualizations. - - -- {kib-repo}blob/{branch}/src/plugins/console[console] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/dashboard/README.md[dashboard] - -Contains the dashboard application. - - -- {kib-repo}blob/{branch}/src/plugins/data/README.md[data] - -data plugin provides common data access services. - - -- {kib-repo}blob/{branch}/src/plugins/dev_tools/README.md[devTools] - -The ui/registry/dev_tools is removed in favor of the devTools plugin which exposes a register method in the setup contract. -Registering app works mostly the same as registering apps in core.application.register. -Routing will be handled by the id of the dev tool - your dev tool will be mounted when the URL matches /app/dev_tools#/. -This API doesn't support angular, for registering angular dev tools, bootstrap a local module on mount into the given HTML element. - - -- {kib-repo}blob/{branch}/src/plugins/discover/README.md[discover] - -Contains the Discover application and the saved search embeddable. - - -- {kib-repo}blob/{branch}/src/plugins/embeddable/README.md[embeddable] - -Embeddables are re-usable widgets that can be rendered in any environment or plugin. Developers can embed them directly in their plugin. End users can dynamically add them to any embeddable containers. - - -- {kib-repo}blob/{branch}/src/plugins/es_ui_shared/README.md[esUiShared] - -This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module. - - -- {kib-repo}blob/{branch}/src/plugins/expressions/README.md[expressions] - -This plugin provides methods which will parse & execute an expression pipeline -string for you, as well as a series of registries for advanced users who might -want to incorporate their own functions, types, and renderers into the service -for use in their own application. - - -- {kib-repo}blob/{branch}/src/plugins/home/README.md[home] - -Moves the legacy ui/registry/feature_catalogue module for registering "features" that should be shown in the home page's feature catalogue to a service within a "home" plugin. The feature catalogue refered to here should not be confused with the "feature" plugin for registering features used to derive UI capabilities for feature controls. - - -- {kib-repo}blob/{branch}/src/plugins/index_pattern_management[indexPatternManagement] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/input_control_vis/README.md[inputControlVis] - -Contains the input control visualization allowing to place custom filter controls on a dashboard. - - -- {kib-repo}blob/{branch}/src/plugins/inspector/README.md[inspector] - -The inspector is a contextual tool to gain insights into different elements -in Kibana, e.g. visualizations. It has the form of a flyout panel. - - -- {kib-repo}blob/{branch}/src/plugins/kibana_legacy/README.md[kibanaLegacy] - -This plugin will contain several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform. - - -- {kib-repo}blob/{branch}/src/plugins/kibana_react/README.md[kibanaReact] - -Tools for building React applications in Kibana. - - -- {kib-repo}blob/{branch}/src/plugins/kibana_usage_collection/README.md[kibanaUsageCollection] - -This plugin registers the basic usage collectors from Kibana: - - -- {kib-repo}blob/{branch}/src/plugins/kibana_utils/README.md[kibanaUtils] - -Utilities for building Kibana plugins. - - -- {kib-repo}blob/{branch}/src/plugins/legacy_export[legacyExport] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/management[management] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/maps_legacy[mapsLegacy] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/navigation/README.md[navigation] - -The navigation plugins exports the TopNavMenu component. -It also provides a stateful version of it on the start contract. - - -- {kib-repo}blob/{branch}/src/plugins/newsfeed[newsfeed] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/region_map[regionMap] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/saved_objects[savedObjects] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/saved_objects_management[savedObjectsManagement] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/share/README.md[share] - -Replaces the legacy ui/share module for registering share context menus. - - -- {kib-repo}blob/{branch}/src/plugins/telemetry/README.md[telemetry] - -Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things: - - -- {kib-repo}blob/{branch}/src/plugins/telemetry_collection_manager/README.md[telemetryCollectionManager] - -Telemetry's collection manager to go through all the telemetry sources when fetching it before reporting. - - -- {kib-repo}blob/{branch}/src/plugins/telemetry_management_section/README.md[telemetryManagementSection] - -This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry). - - -- {kib-repo}blob/{branch}/src/plugins/tile_map[tileMap] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/src/plugins/timelion/README.md[timelion] - -Contains the deprecated timelion application. For the timelion visualization, -which also contains the timelion APIs and backend, look at the vis_type_timelion plugin. - - -- {kib-repo}blob/{branch}/src/plugins/ui_actions/README.md[uiActions] - -An API for: - - -- {kib-repo}blob/{branch}/src/plugins/usage_collection/README.md[usageCollection] - -Usage Collection allows collecting usage data for other services to consume (telemetry and monitoring). -To integrate with the telemetry services for usage collection of your feature, there are 2 steps: - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_markdown/README.md[visTypeMarkdown] - -The markdown visualization that can be used to place text panels on dashboards. - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_metric/README.md[visTypeMetric] - -Contains the metric visualization. - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_table/README.md[visTypeTable] - -Contains the data table visualization, that allows presenting data in a simple table format. - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_tagcloud/README.md[visTypeTagcloud] - -Contains the tagcloud visualization. - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_timelion/README.md[visTypeTimelion] - -Contains the timelion visualization and the timelion backend. - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_timeseries/README.md[visTypeTimeseries] - -Contains everything around TSVB (the editor, visualizatin implementations and backends). - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_vega/README.md[visTypeVega] - -Contains the Vega visualization. - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_vislib/README.md[visTypeVislib] - -Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and -heatmap charts. - - -- {kib-repo}blob/{branch}/src/plugins/vis_type_xy/README.md[visTypeXy] - -Contains the new xy-axis chart using the elastic-charts library, which will eventually -replace the vislib xy-axis (bar, area, line) charts. - - -- {kib-repo}blob/{branch}/src/plugins/visualizations/README.md[visualizations] - -Contains most of the visualization infrastructure, e.g. the visualization type registry or the -visualization embeddable. - - -- {kib-repo}blob/{branch}/src/plugins/visualize/README.md[visualize] - -Contains the visualize application which includes the listing page and the app frame, -which will load the visualization's editor. - - -[discrete] -==== x-pack/plugins - -- {kib-repo}blob/{branch}/x-pack/plugins/actions/README.md[actions] - -The Kibana actions plugin provides a framework to create executable actions. You can: - - -- {kib-repo}blob/{branch}/x-pack/plugins/alerting_builtins/README.md[alertingBuiltins] - -This plugin provides alertTypes shipped with Kibana for use with the -the alerts plugin. When enabled, it will register -the built-in alertTypes with the alerting plugin, register associated HTTP -routes, etc. - - -- {kib-repo}blob/{branch}/x-pack/plugins/alerts/README.md[alerts] - -The Kibana alerting plugin provides a common place to set up alerts. You can: - - -- {kib-repo}blob/{branch}/x-pack/plugins/apm/readme.md[apm] - -To access an elasticsearch instance that has live data you have two options: - - -- {kib-repo}blob/{branch}/x-pack/plugins/audit_trail[auditTrail] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/beats_management/readme.md[beatsManagement] - -Notes: -Failure to have auth enabled in Kibana will make for a broken UI. UI-based errors not yet in place - - -- {kib-repo}blob/{branch}/x-pack/plugins/canvas/README.md[canvas] - -"Never look back. The past is done. The future is a blank canvas." ― Suzy Kassem, Rise Up and Salute the Sun - - -- {kib-repo}blob/{branch}/x-pack/plugins/case/README.md[case] - -Experimental Feature - - -- {kib-repo}blob/{branch}/x-pack/plugins/cloud[cloud] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/code[code] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/console_extensions[consoleExtensions] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/cross_cluster_replication/README.md[crossClusterReplication] - -You can run a local cluster and simulate a remote cluster within a single Kibana directory. - - -- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_enhanced/README.md[dashboardEnhanced] - -Contains the enhancements to the OSS dashboard app. - - -- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_mode/README.md[dashboardMode] - -The deprecated dashboard only mode. - - -- {kib-repo}blob/{branch}/x-pack/plugins/data_enhanced[dataEnhanced] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/discover_enhanced/README.md[discoverEnhanced] - -Contains the enhancements to the OSS discover app. - - -- {kib-repo}blob/{branch}/x-pack/plugins/embeddable_enhanced[embeddableEnhanced] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/encrypted_saved_objects/README.md[encryptedSavedObjects] - -The purpose of this plugin is to provide a way to encrypt/decrypt attributes on the custom Saved Objects that works with -security and spaces filtering as well as performing audit logging. - - -- {kib-repo}blob/{branch}/x-pack/plugins/enterprise_search/README.md[enterpriseSearch] - -This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In it's current MVP state, the plugin provides the following with the goal of gathering user feedback and raising product awareness: - - -- {kib-repo}blob/{branch}/x-pack/plugins/event_log/README.md[eventLog] - -The purpose of this plugin is to provide a way to persist a history of events -occuring in Kibana, initially just for the Make It Action project - alerts -and actions. - - -- {kib-repo}blob/{branch}/x-pack/plugins/features[features] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/file_upload[fileUpload] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/global_search/README.md[globalSearch] - -The GlobalSearch plugin provides an easy way to search for various objects, such as applications -or dashboards from the Kibana instance, from both server and client-side plugins - - -- {kib-repo}blob/{branch}/x-pack/plugins/global_search_bar/README.md[globalSearchBar] - -The GlobalSearchBar plugin provides a search interface for navigating Kibana. (It is the UI to the GlobalSearch plugin.) - - -- {kib-repo}blob/{branch}/x-pack/plugins/global_search_providers[globalSearchProviders] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/graph/README.md[graph] - -This is the main source folder of the Graph plugin. It contains all of the Kibana server and client source code. x-pack/test/functional/apps/graph contains additional functional tests. - - -- {kib-repo}blob/{branch}/x-pack/plugins/grokdebugger/README.md[grokdebugger] - -- {kib-repo}blob/{branch}/x-pack/plugins/index_lifecycle_management/README.md[indexLifecycleManagement] - -You can test that the Frozen badge, phase filtering, and lifecycle information is surfaced in -Index Management by running this series of requests in Console: - - -- {kib-repo}blob/{branch}/x-pack/plugins/index_management[indexManagement] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/infra/README.md[infra] - -This is the home of the infra plugin, which aims to provide a solution for -the infrastructure monitoring use-case within Kibana. - - -- {kib-repo}blob/{branch}/x-pack/plugins/ingest_manager/README.md[ingestManager] - -Fleet needs to have Elasticsearch API keys enabled, and also to have TLS enabled on kibana, (if you want to run Kibana without TLS you can provide the following config flag --xpack.ingestManager.fleet.tlsCheckDisabled=false) - - -- {kib-repo}blob/{branch}/x-pack/plugins/ingest_pipelines/README.md[ingestPipelines] - -The ingest_pipelines plugin provides Kibana support for Elasticsearch's ingest nodes. Please refer to the Elasticsearch documentation for more details. - - -- {kib-repo}blob/{branch}/x-pack/plugins/lens/readme.md[lens] - -Run all tests from the x-pack root directory - - -- {kib-repo}blob/{branch}/x-pack/plugins/license_management[licenseManagement] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/licensing/README.md[licensing] - -The licensing plugin retrieves license data from Elasticsearch at regular configurable intervals. - - -- {kib-repo}blob/{branch}/x-pack/plugins/lists/README.md[lists] - -README.md for developers working on the backend lists on how to get started -using the CURL scripts in the scripts folder. - - -- {kib-repo}blob/{branch}/x-pack/plugins/logstash[logstash] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/maps/README.md[maps] - -Visualize geo data from Elasticsearch or 3rd party geo-services. - - -- {kib-repo}blob/{branch}/x-pack/plugins/maps_legacy_licensing/README.md[mapsLegacyLicensing] - -This plugin provides access to the detailed tile map services from Elastic. - - -- {kib-repo}blob/{branch}/x-pack/plugins/ml[ml] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/monitoring[monitoring] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/observability/README.md[observability] - -This plugin provides shared components and services for use across observability solutions, as well as the observability landing page UI. - - -- {kib-repo}blob/{branch}/x-pack/plugins/oss_telemetry[ossTelemetry] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/painless_lab[painlessLab] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/remote_clusters[remoteClusters] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/reporting/README.md[reporting] - -An awesome Kibana reporting plugin - - -- {kib-repo}blob/{branch}/x-pack/plugins/rollup/README.md[rollup] - -Welcome to the Kibana rollup plugin! This plugin provides Kibana support for Elasticsearch's rollup feature. Please refer to the Elasticsearch documentation to understand rollup indices and how to create rollup jobs. - - -- {kib-repo}blob/{branch}/x-pack/plugins/searchprofiler[searchprofiler] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/security/README.md[security] - -See Configuring security in Kibana. - - -- {kib-repo}blob/{branch}/x-pack/plugins/security_solution/README.md[securitySolution] - -Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing. - - -- {kib-repo}blob/{branch}/x-pack/plugins/snapshot_restore/README.md[snapshotRestore] - -or - - -- {kib-repo}blob/{branch}/x-pack/plugins/spaces[spaces] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/task_manager[taskManager] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/telemetry_collection_xpack/README.md[telemetryCollectionXpack] - -Gathers all usage collection, retrieving them from both: OSS and X-Pack plugins. - - -- {kib-repo}blob/{branch}/x-pack/plugins/transform[transform] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/translations[translations] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/triggers_actions_ui/README.md[triggers_actions_ui] - -The Kibana alerts and actions UI plugin provides a user interface for managing alerts and actions. -As a developer you can reuse and extend built-in alerts and actions UI functionality: - - -- {kib-repo}blob/{branch}/x-pack/plugins/ui_actions_enhanced/README.md[uiActionsEnhanced] - -- {kib-repo}blob/{branch}/x-pack/plugins/upgrade_assistant[upgradeAssistant] - -WARNING: Missing README. - - -- {kib-repo}blob/{branch}/x-pack/plugins/uptime/README.md[uptime] - -The purpose of this plugin is to provide users of Heartbeat more visibility of what's happening -in their infrastructure. - - -- {kib-repo}blob/{branch}/x-pack/plugins/watcher/README.md[watcher] - -This plugins adopts some conventions in addition to or in place of conventions in Kibana (at the time of the plugin's creation): - From 15e7623ecfced651200c0c412fbdb347e5f2982e Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Wed, 30 Sep 2020 16:07:58 -0400 Subject: [PATCH 09/36] [SECURITY_SOLUTION][ENDPOINT] Improve Endpoint Host data generator to also integrate with Ingest (#74305) * Endpoint generator connects host with a real policy and enrolls agent Co-authored-by: Paul Tavares Co-authored-by: kevinlog Co-authored-by: Candace Park --- .../src/kbn_client/kbn_client.ts | 4 +- .../common/endpoint/generate_data.ts | 24 +- .../common/endpoint/index_data.ts | 393 +++++++++++++++++- .../kbn_client_with_api_key_support.ts | 32 ++ .../endpoint/resolver_generator_script.ts | 91 ++-- 5 files changed, 489 insertions(+), 55 deletions(-) create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts diff --git a/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts b/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts index 861ea0988692c5..7184727fc53dec 100644 --- a/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts +++ b/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts @@ -54,8 +54,8 @@ export class KbnClient { /** * Make a direct request to the Kibana server */ - async request(options: ReqOptions) { - return await this.requester.request(options); + async request(options: ReqOptions) { + return await this.requester.request(options); } resolveUrl(relativeUrl: string) { diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index abdbbf1986bcd2..ec7a49da469fe3 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -110,6 +110,12 @@ const Mac: OSFields[] = []; const OS: OSFields[] = [...Windows, ...Mac, ...Linux]; +const POLICY_RESPONSE_STATUSES: HostPolicyResponseActionStatus[] = [ + HostPolicyResponseActionStatus.success, + HostPolicyResponseActionStatus.failure, + HostPolicyResponseActionStatus.warning, +]; + const APPLIED_POLICIES: Array<{ name: string; id: string; @@ -125,6 +131,11 @@ const APPLIED_POLICIES: Array<{ id: 'C2A9093E-E289-4C0A-AA44-8C32A414FA7A', status: HostPolicyResponseActionStatus.success, }, + { + name: 'Detect Malware Only', + id: '47d7965d-6869-478b-bd9c-fb0d2bb3959f', + status: HostPolicyResponseActionStatus.success, + }, ]; const FILE_OPERATIONS: string[] = ['creation', 'open', 'rename', 'execution', 'deletion']; @@ -364,15 +375,12 @@ export class EndpointDocGenerator { } /** - * Creates new random policy id for the host to simulate new policy application + * Updates the current Host common record applied Policy to a different one from the list + * of random choices and gives it a random policy response status. */ - public updatePolicyId() { - this.commonInfo.Endpoint.policy.applied.id = this.randomChoice(APPLIED_POLICIES).id; - this.commonInfo.Endpoint.policy.applied.status = this.randomChoice([ - HostPolicyResponseActionStatus.success, - HostPolicyResponseActionStatus.failure, - HostPolicyResponseActionStatus.warning, - ]); + public updateHostPolicyData() { + this.commonInfo.Endpoint.policy.applied = this.randomChoice(APPLIED_POLICIES); + this.commonInfo.Endpoint.policy.applied.status = this.randomChoice(POLICY_RESPONSE_STATUSES); } private createHostData(): HostInfo { diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index b8c2fdbe65f1e4..bf3d12f231c867 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -6,25 +6,66 @@ import { Client } from '@elastic/elasticsearch'; import seedrandom from 'seedrandom'; +import { KbnClient } from '@kbn/dev-utils'; +import { AxiosResponse } from 'axios'; import { EndpointDocGenerator, TreeOptions, Event } from './generate_data'; import { firstNonNullValue } from './models/ecs_safety_helpers'; +import { + CreateAgentPolicyRequest, + CreateAgentPolicyResponse, + CreatePackagePolicyRequest, + CreatePackagePolicyResponse, + GetPackagesResponse, + PostAgentEnrollRequest, + AGENT_API_ROUTES, + AGENT_POLICY_API_ROUTES, + EPM_API_ROUTES, + PACKAGE_POLICY_API_ROUTES, + ENROLLMENT_API_KEY_ROUTES, + GetEnrollmentAPIKeysResponse, + GetOneEnrollmentAPIKeyResponse, + PostAgentEnrollResponse, + PostAgentCheckinRequest, + PostAgentCheckinResponse, + PostAgentAcksResponse, + PostAgentAcksRequest, +} from '../../../ingest_manager/common'; +import { factory as policyConfigFactory } from './models/policy_config'; +import { HostMetadata } from './types'; +import { KbnClientWithApiKeySupport } from '../../scripts/endpoint/kbn_client_with_api_key_support'; export async function indexHostsAndAlerts( client: Client, + kbnClient: KbnClientWithApiKeySupport, seed: string, numHosts: number, numDocs: number, metadataIndex: string, - policyIndex: string, + policyResponseIndex: string, eventIndex: string, alertIndex: string, alertsPerHost: number, + fleet: boolean, options: TreeOptions = {} ) { const random = seedrandom(seed); + const epmEndpointPackage = await getEndpointPackageInfo(kbnClient); + // Keep a map of host applied policy ids (fake) to real ingest package configs (policy record) + const realPolicies: Record = {}; + for (let i = 0; i < numHosts; i++) { const generator = new EndpointDocGenerator(random); - await indexHostDocs(numDocs, client, metadataIndex, policyIndex, generator); + await indexHostDocs( + numDocs, + client, + kbnClient, + realPolicies, + epmEndpointPackage, + metadataIndex, + policyResponseIndex, + fleet, + generator + ); await indexAlerts(client, eventIndex, alertIndex, generator, alertsPerHost, options); } await client.indices.refresh({ @@ -43,22 +84,78 @@ function delay(ms: number) { async function indexHostDocs( numDocs: number, client: Client, + kbnClient: KbnClientWithApiKeySupport, + realPolicies: Record, + epmEndpointPackage: GetPackagesResponse['response'][0], metadataIndex: string, - policyIndex: string, + policyResponseIndex: string, + enrollFleet: boolean, generator: EndpointDocGenerator ) { const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents const timestamp = new Date().getTime(); + let hostMetadata: HostMetadata; + let wasAgentEnrolled = false; + let enrolledAgent: undefined | PostAgentEnrollResponse['item']; + for (let j = 0; j < numDocs; j++) { generator.updateHostData(); - generator.updatePolicyId(); + generator.updateHostPolicyData(); + + hostMetadata = generator.generateHostMetadata(timestamp - timeBetweenDocs * (numDocs - j - 1)); + + if (enrollFleet) { + const { id: appliedPolicyId, name: appliedPolicyName } = hostMetadata.Endpoint.policy.applied; + + // If we don't yet have a "real" policy record, then create it now in ingest (package config) + if (!realPolicies[appliedPolicyId]) { + // eslint-disable-next-line require-atomic-updates + realPolicies[appliedPolicyId] = await createPolicy( + kbnClient, + appliedPolicyName, + epmEndpointPackage.version + ); + } + + // If we did not yet enroll an agent for this Host, do it now that we have good policy id + if (!wasAgentEnrolled) { + wasAgentEnrolled = true; + enrolledAgent = await fleetEnrollAgentForHost( + kbnClient, + hostMetadata!, + realPolicies[appliedPolicyId].policy_id + ); + } + // Update the Host metadata record with the ID of the "real" policy along with the enrolled agent id + hostMetadata = { + ...hostMetadata, + elastic: { + ...hostMetadata.elastic, + agent: { + ...hostMetadata.elastic.agent, + id: enrolledAgent?.id ?? hostMetadata.elastic.agent.id, + }, + }, + Endpoint: { + ...hostMetadata.Endpoint, + policy: { + ...hostMetadata.Endpoint.policy, + applied: { + ...hostMetadata.Endpoint.policy.applied, + id: realPolicies[appliedPolicyId].id, + }, + }, + }, + }; + } + await client.index({ index: metadataIndex, - body: generator.generateHostMetadata(timestamp - timeBetweenDocs * (numDocs - j - 1)), + body: hostMetadata, op_type: 'create', }); await client.index({ - index: policyIndex, + index: policyResponseIndex, body: generator.generatePolicyResponse(timestamp - timeBetweenDocs * (numDocs - j - 1)), op_type: 'create', }); @@ -98,3 +195,287 @@ async function indexAlerts( await client.bulk({ body, refresh: true }); } } + +const createPolicy = async ( + kbnClient: KbnClient, + policyName: string, + endpointPackageVersion: string +): Promise => { + // Create Agent Policy first + const newAgentPolicyData: CreateAgentPolicyRequest['body'] = { + name: `Policy for ${policyName}`, + description: '', + namespace: 'default', + }; + let agentPolicy; + try { + agentPolicy = (await kbnClient.request({ + path: AGENT_POLICY_API_ROUTES.CREATE_PATTERN, + method: 'POST', + body: newAgentPolicyData, + })) as AxiosResponse; + } catch (error) { + throw new Error(`create policy ${error}`); + } + + // Create Package Configuration + const newPackagePolicyData: CreatePackagePolicyRequest['body'] = { + name: policyName, + description: 'Protect the worlds data', + policy_id: agentPolicy.data.item.id, + enabled: true, + output_id: '', + inputs: [ + { + type: 'endpoint', + enabled: true, + streams: [], + config: { + policy: { + value: policyConfigFactory(), + }, + }, + }, + ], + namespace: 'default', + package: { + name: 'endpoint', + title: 'endpoint', + version: endpointPackageVersion, + }, + }; + const packagePolicy = (await kbnClient.request({ + path: PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN, + method: 'POST', + body: newPackagePolicyData, + })) as AxiosResponse; + return packagePolicy.data.item; +}; + +const getEndpointPackageInfo = async ( + kbnClient: KbnClient +): Promise => { + const endpointPackage = ((await kbnClient.request({ + path: `${EPM_API_ROUTES.LIST_PATTERN}?category=security`, + method: 'GET', + })) as AxiosResponse).data.response.find( + (epmPackage) => epmPackage.name === 'endpoint' + ); + + if (!endpointPackage) { + throw new Error('EPM Endpoint package was not found!'); + } + + return endpointPackage; +}; + +const fleetEnrollAgentForHost = async ( + kbnClient: KbnClientWithApiKeySupport, + endpointHost: HostMetadata, + agentPolicyId: string +): Promise => { + // Get Enrollement key for host's applied policy + const enrollmentApiKey = await kbnClient + .request({ + path: ENROLLMENT_API_KEY_ROUTES.LIST_PATTERN, + method: 'GET', + query: { + kuery: `fleet-enrollment-api-keys.policy_id:"${agentPolicyId}"`, + }, + }) + .then((apiKeysResponse) => { + const apiKey = apiKeysResponse.data.list[0]; + + if (!apiKey) { + return Promise.reject( + new Error(`no API enrollment key found for agent policy id ${agentPolicyId}`) + ); + } + + return kbnClient + .request({ + path: ENROLLMENT_API_KEY_ROUTES.INFO_PATTERN.replace('{keyId}', apiKey.id), + method: 'GET', + }) + .catch((error) => { + // eslint-disable-next-line no-console + console.log('unable to retrieve enrollment api key for policy'); + return Promise.reject(error); + }); + }) + .then((apiKeyDetailsResponse) => { + return apiKeyDetailsResponse.data.item.api_key; + }) + .catch((error) => { + // eslint-disable-next-line no-console + console.error(error); + return ''; + }); + + if (enrollmentApiKey.length === 0) { + return; + } + + const fetchKibanaVersion = async () => { + const version = ((await kbnClient.request({ + path: '/api/status', + method: 'GET', + })) as AxiosResponse).data.version.number; + if (!version) { + // eslint-disable-next-line no-console + console.log('failed to retrieve kibana version'); + } + return version; + }; + + // Enroll an agent for the Host + const body: PostAgentEnrollRequest['body'] = { + type: 'PERMANENT', + metadata: { + local: { + elastic: { + agent: { + version: await fetchKibanaVersion(), + }, + }, + host: { + architecture: 'x86_64', + hostname: endpointHost.host, + name: endpointHost.host, + id: '1c032ec0-3a94-4d54-9ad2-c5610c0eaba4', + ip: ['fe80::703b:b9e6:887d:7f5/64', '10.0.2.15/24', '::1/128', '127.0.0.1/8'], + mac: ['08:00:27:d8:c5:c0'], + }, + os: { + family: 'windows', + kernel: '10.0.19041.388 (WinBuild.160101.0800)', + platform: 'windows', + version: '10.0', + name: 'Windows 10 Pro', + full: 'Windows 10 Pro(10.0)', + }, + }, + user_provided: { + dev_agent_version: '0.0.1', + region: 'us-east', + }, + }, + }; + + try { + // First enroll the agent + const res = await kbnClient.requestWithApiKey(AGENT_API_ROUTES.ENROLL_PATTERN, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'kbn-xsrf': 'xxx', + Authorization: `ApiKey ${enrollmentApiKey}`, + 'Content-Type': 'application/json', + }, + }); + + if (res) { + const enrollObj: PostAgentEnrollResponse = await res.json(); + if (!res.ok) { + // eslint-disable-next-line no-console + console.error('unable to enroll agent', enrollObj); + return; + } + // ------------------------------------------------ + // now check the agent in so that it can complete enrollment + const checkinBody: PostAgentCheckinRequest['body'] = { + events: [ + { + type: 'STATE', + subtype: 'RUNNING', + message: 'state changed from STOPPED to RUNNING', + timestamp: new Date().toISOString(), + payload: { + random: 'data', + state: 'RUNNING', + previous_state: 'STOPPED', + }, + agent_id: enrollObj.item.id, + }, + ], + }; + const checkinRes = await kbnClient + .requestWithApiKey( + AGENT_API_ROUTES.CHECKIN_PATTERN.replace('{agentId}', enrollObj.item.id), + { + method: 'POST', + body: JSON.stringify(checkinBody), + headers: { + 'kbn-xsrf': 'xxx', + Authorization: `ApiKey ${enrollObj.item.access_api_key}`, + 'Content-Type': 'application/json', + }, + } + ) + .catch((error) => { + return Promise.reject(error); + }); + + // Agent unenrolling? + if (checkinRes.status === 403) { + return; + } + + const checkinObj: PostAgentCheckinResponse = await checkinRes.json(); + if (!checkinRes.ok) { + // eslint-disable-next-line no-console + console.error( + `failed to checkin agent [${enrollObj.item.id}] for endpoint [${endpointHost.host.id}]` + ); + return enrollObj.item; + } + + // ------------------------------------------------ + // If we have an action to ack(), then do it now + if (checkinObj.actions.length) { + const ackActionBody: PostAgentAcksRequest['body'] = { + // @ts-ignore + events: checkinObj.actions.map((action) => { + return { + action_id: action.id, + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: new Date().toISOString(), + agent_id: action.agent_id, + policy_id: agentPolicyId, + message: `endpoint generator: Endpoint Started`, + }; + }), + }; + const ackActionResp = await kbnClient.requestWithApiKey( + AGENT_API_ROUTES.ACKS_PATTERN.replace('{agentId}', enrollObj.item.id), + { + method: 'POST', + body: JSON.stringify(ackActionBody), + headers: { + 'kbn-xsrf': 'xxx', + Authorization: `ApiKey ${enrollObj.item.access_api_key}`, + 'Content-Type': 'application/json', + }, + } + ); + + const ackActionObj: PostAgentAcksResponse = await ackActionResp.json(); + if (!ackActionResp.ok) { + // eslint-disable-next-line no-console + console.error( + `failed to ACK Actions provided to agent [${enrollObj.item.id}] for endpoint [${endpointHost.host.id}]` + ); + // eslint-disable-next-line no-console + console.error(JSON.stringify(ackActionObj, null, 2)); + return enrollObj.item; + } + } + + return enrollObj.item; + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts b/x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts new file mode 100644 index 00000000000000..526c0eb4a90559 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { KbnClient, ToolingLog } from '@kbn/dev-utils'; +import { KibanaConfig } from '@kbn/dev-utils/target/kbn_client/kbn_client_requester'; +import fetch, { RequestInit as FetchRequestInit } from 'node-fetch'; + +export class KbnClientWithApiKeySupport extends KbnClient { + private kibanaUrlNoAuth: string; + constructor(log: ToolingLog, kibanaConfig: KibanaConfig) { + super(log, kibanaConfig); + const kibanaUrl = this.resolveUrl(kibanaConfig.url); + const matches = kibanaUrl.match(/(https?:\/\/)(.*\:.*\@)(.*)/); + // strip auth from url + this.kibanaUrlNoAuth = + matches && matches.length >= 3 + ? matches[1] + matches[3].replace('/', '') + : kibanaUrl.replace('/', ''); + } + /** + * The fleet api to enroll and agent requires an api key when you mke the request, however KbnClient currently does not support sending an api key with the request. This function allows you to send an api key with a request. + */ + requestWithApiKey(path: string, init?: RequestInit | undefined): Promise { + return (fetch( + `${this.kibanaUrlNoAuth}${path}`, + init as FetchRequestInit + ) as unknown) as Promise; + } +} diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts index cfe1c741ef3f1c..1c2c4a857451b4 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts @@ -4,14 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ /* eslint-disable no-console */ -import * as path from 'path'; import yargs from 'yargs'; -import * as url from 'url'; -import fetch from 'node-fetch'; import { Client, ClientOptions } from '@elastic/elasticsearch'; import { ResponseError } from '@elastic/elasticsearch/lib/errors'; +import { KbnClient, ToolingLog } from '@kbn/dev-utils'; +import { AxiosResponse } from 'axios'; import { indexHostsAndAlerts } from '../../common/endpoint/index_data'; import { ANCESTRY_LIMIT } from '../../common/endpoint/generate_data'; +import { FLEET_SETUP_API_ROUTES, SETUP_API_ROUTE } from '../../../ingest_manager/common/constants'; +import { + CreateFleetSetupResponse, + PostIngestSetupResponse, +} from '../../../ingest_manager/common/types/rest_spec'; +import { KbnClientWithApiKeySupport } from './kbn_client_with_api_key_support'; main(); @@ -35,42 +40,37 @@ async function deleteIndices(indices: string[], client: Client) { } } -async function doIngestSetup(kibanaURL: string) { +async function doIngestSetup(kbnClient: KbnClient) { + // Setup Ingest try { - const kbURL = new url.URL(kibanaURL); - // this includes the base path that is randomly generated by Kibana - const pathname = path.posix.join(path.posix.sep, kbURL.pathname, 'api/ingest_manager/setup'); - const connectURL = new url.URL(pathname, kbURL); - console.log('Calling ingest manager setup at ', connectURL.toString()); - const response = await fetch( - // wrap base url in URL class because the kibana basepath will get removed otherwise - connectURL.toString(), - { - method: 'POST', - headers: { - 'kbn-xsrf': 'blah', - }, - } - ); - if (response.status !== 200) { - console.log('POST response ', response); - console.log( - 'Request failed please check that you have the correct base path and credentials for the kibana URL' - ); - // eslint-disable-next-line no-process-exit - process.exit(1); + const setupResponse = (await kbnClient.request({ + path: SETUP_API_ROUTE, + method: 'POST', + })) as AxiosResponse; + + if (!setupResponse.data.isInitialized) { + console.error(setupResponse.data); + throw new Error('Initializing the ingest manager failed, existing'); } - const setupResponse = await response.json(); - console.log('Ingest setup response ', setupResponse); - if (!setupResponse?.isInitialized) { - console.log('Initializing the ingest manager failed, existing'); - // eslint-disable-next-line no-process-exit - process.exit(1); + } catch (error) { + console.error(error); + throw error; + } + + // Setup Fleet + try { + const setupResponse = (await kbnClient.request({ + path: FLEET_SETUP_API_ROUTES.CREATE_PATTERN, + method: 'POST', + })) as AxiosResponse; + + if (!setupResponse.data.isInitialized) { + console.error(setupResponse.data); + throw new Error('Initializing Fleet failed, existing'); } } catch (error) { - console.log(JSON.stringify(error, null, 2)); - // eslint-disable-next-line no-process-exit - process.exit(1); + console.error(error); + throw error; } } @@ -196,14 +196,25 @@ async function main() { type: 'boolean', default: false, }, + fleet: { + alias: 'f', + describe: 'enroll fleet agents for hosts', + type: 'boolean', + default: false, + }, }).argv; - await doIngestSetup(argv.kibana); + const kbnClient = new KbnClientWithApiKeySupport(new ToolingLog(), { url: argv.kibana }); - const clientOptions: ClientOptions = { - node: argv.node, - }; + try { + await doIngestSetup(kbnClient); + } catch (error) { + // eslint-disable-next-line no-process-exit + process.exit(1); + } + const clientOptions: ClientOptions = { node: argv.node }; const client = new Client(clientOptions); + if (argv.delete) { await deleteIndices( [argv.eventIndex, argv.metadataIndex, argv.policyIndex, argv.alertIndex], @@ -219,6 +230,7 @@ async function main() { const startTime = new Date().getTime(); await indexHostsAndAlerts( client, + kbnClient, seed, argv.numHosts, argv.numDocs, @@ -227,6 +239,7 @@ async function main() { argv.eventIndex, argv.alertIndex, argv.alertsPerHost, + argv.fleet, { ancestors: argv.ancestors, generations: argv.generations, From e41f4368f27ea2f3baa6fc3af89eca4a3c2aa606 Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Wed, 30 Sep 2020 16:17:16 -0400 Subject: [PATCH 10/36] [Security Solution][Detections] Add rule overrides for single event EQL rules (#78876) * Add buildRuleWithOverrides function for single event EQL queries * Disable rule overrides for all sequence signals Co-authored-by: Elastic Machine --- .../scripts/rules/queries/query_eql.json | 1 + .../signals/__mocks__/es_results.ts | 41 ++++ .../signals/build_bulk_body.test.ts | 2 +- .../signals/build_bulk_body.ts | 11 +- .../signals/build_rule.test.ts | 189 +++++++++++------- .../detection_engine/signals/build_rule.ts | 57 +++++- .../build_risk_score_from_mapping.test.ts | 2 +- .../mappings/build_risk_score_from_mapping.ts | 8 +- .../build_rule_name_from_mapping.test.ts | 2 +- .../mappings/build_rule_name_from_mapping.ts | 8 +- .../build_severity_from_mapping.test.ts | 6 +- .../mappings/build_severity_from_mapping.ts | 8 +- .../signals/signal_rule_alert_type.ts | 2 +- 13 files changed, 232 insertions(+), 105 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_eql.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_eql.json index 598f2182002c1d..a63444012addd6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_eql.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_eql.json @@ -25,6 +25,7 @@ "from": "now-300m", "severity": "high", "type": "eql", + "language": "eql", "threat": [ { "framework": "MITRE ATT&CK", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index b37bc7d0fab69c..501cd1fa6ecfb2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -22,6 +22,7 @@ import { RuleTypeParams } from '../../types'; import { IRuleStatusAttributes } from '../../rules/types'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; +import { RulesSchema } from '../../../../../common/detection_engine/schemas/response'; export const sampleRuleAlertParams = ( maxSignals?: number | undefined, @@ -92,6 +93,46 @@ export const sampleRuleSO = (): SavedObject => { }; }; +export const expectedRule = (): RulesSchema => { + return { + actions: [], + author: ['Elastic'], + building_block_type: 'default', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + rule_id: 'rule-1', + false_positives: [], + max_signals: 10000, + risk_score: 50, + risk_score_mapping: [], + output_index: '.siem-signals', + description: 'Detecting root and admin users', + from: 'now-6m', + immutable: false, + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + language: 'kuery', + license: 'Elastic License', + name: 'rule-name', + query: 'user.name: root or user.name: admin', + references: ['http://google.com'], + severity: 'high', + severity_mapping: [], + tags: ['some fake tag 1', 'some fake tag 2'], + threat: [], + type: 'query', + to: 'now', + note: '', + enabled: true, + created_by: 'sample user', + updated_by: 'sample user', + version: 1, + updated_at: '2020-03-27T22:55:59.577Z', + created_at: '2020-03-27T22:55:59.577Z', + throttle: 'no_actions', + exceptions_list: getListArrayMock(), + }; +}; + export const sampleDocNoSortIdNoVersion = (someUuid: string = sampleIdGuid): SignalSourceHit => ({ _index: 'myFakeSignalIndex', _type: 'doc', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts index f45a408cd32b8e..2f7dd22c0c78e7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts @@ -546,7 +546,7 @@ describe('buildSignalFromEvent', () => { const ancestor = sampleDocWithAncestors().hits.hits[0]; delete ancestor._source.source; const ruleSO = sampleRuleSO(); - const signal = buildSignalFromEvent(ancestor, ruleSO); + const signal = buildSignalFromEvent(ancestor, ruleSO, true); // Timestamp will potentially always be different so remove it for the test // @ts-expect-error delete signal['@timestamp']; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts index 01a6b0e7aefadb..f8632a85c77e9e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts @@ -13,7 +13,7 @@ import { BaseSignalHit, SignalSource, } from './types'; -import { buildRule, buildRuleWithoutOverrides } from './build_rule'; +import { buildRule, buildRuleWithoutOverrides, buildRuleWithOverrides } from './build_rule'; import { additionalSignalFields, buildSignal } from './build_signal'; import { buildEventTypeSignal } from './build_event_type_signal'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; @@ -97,7 +97,7 @@ export const buildSignalGroupFromSequence = ( ): BaseSignalHit[] => { const wrappedBuildingBlocks = wrapBuildingBlocks( sequence.events.map((event) => { - const signal = buildSignalFromEvent(event, ruleSO); + const signal = buildSignalFromEvent(event, ruleSO, false); signal.signal.rule.building_block_type = 'default'; return signal; }), @@ -147,9 +147,12 @@ export const buildSignalFromSequence = ( export const buildSignalFromEvent = ( event: BaseSignalHit, - ruleSO: SavedObject + ruleSO: SavedObject, + applyOverrides: boolean ): SignalHit => { - const rule = buildRuleWithoutOverrides(ruleSO); + const rule = applyOverrides + ? buildRuleWithOverrides(ruleSO, event._source) + : buildRuleWithoutOverrides(ruleSO); const signal = { ...buildSignal([event], rule), ...additionalSignalFields(event), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts index 62e5854037d9e1..6bc9489f658707 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts @@ -4,12 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildRule, removeInternalTagsFromRule, buildRuleWithoutOverrides } from './build_rule'; +import { + buildRule, + removeInternalTagsFromRule, + buildRuleWithOverrides, + buildRuleWithoutOverrides, +} from './build_rule'; import { sampleDocNoSortId, sampleRuleAlertParams, sampleRuleGuid, sampleRuleSO, + expectedRule, + sampleDocSeverity, } from './__mocks__/es_results'; import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; @@ -312,43 +319,7 @@ describe('buildRuleWithoutOverrides', () => { test('builds a rule using rule SO', () => { const ruleSO = sampleRuleSO(); const rule = buildRuleWithoutOverrides(ruleSO); - expect(rule).toEqual({ - actions: [], - author: ['Elastic'], - building_block_type: 'default', - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - rule_id: 'rule-1', - false_positives: [], - max_signals: 10000, - risk_score: 50, - risk_score_mapping: [], - output_index: '.siem-signals', - description: 'Detecting root and admin users', - from: 'now-6m', - immutable: false, - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - interval: '5m', - language: 'kuery', - license: 'Elastic License', - name: 'rule-name', - query: 'user.name: root or user.name: admin', - references: ['http://google.com'], - severity: 'high', - severity_mapping: [], - tags: ['some fake tag 1', 'some fake tag 2'], - threat: [], - type: 'query', - to: 'now', - note: '', - enabled: true, - created_by: 'sample user', - updated_by: 'sample user', - version: 1, - updated_at: ruleSO.updated_at ?? '', - created_at: ruleSO.attributes.createdAt, - throttle: 'no_actions', - exceptions_list: getListArrayMock(), - }); + expect(rule).toEqual(expectedRule()); }); test('builds a rule using rule SO and removes internal tags', () => { @@ -360,42 +331,110 @@ describe('buildRuleWithoutOverrides', () => { `${INTERNAL_IMMUTABLE_KEY}:true`, ]; const rule = buildRuleWithoutOverrides(ruleSO); - expect(rule).toEqual({ - actions: [], - author: ['Elastic'], - building_block_type: 'default', - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - rule_id: 'rule-1', - false_positives: [], - max_signals: 10000, - risk_score: 50, - risk_score_mapping: [], - output_index: '.siem-signals', - description: 'Detecting root and admin users', - from: 'now-6m', - immutable: false, - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - interval: '5m', - language: 'kuery', - license: 'Elastic License', - name: 'rule-name', - query: 'user.name: root or user.name: admin', - references: ['http://google.com'], - severity: 'high', - severity_mapping: [], - tags: ['some fake tag 1', 'some fake tag 2'], - threat: [], - type: 'query', - to: 'now', - note: '', - enabled: true, - created_by: 'sample user', - updated_by: 'sample user', - version: 1, - updated_at: ruleSO.updated_at ?? '', - created_at: ruleSO.attributes.createdAt, - throttle: 'no_actions', - exceptions_list: getListArrayMock(), - }); + expect(rule).toEqual(expectedRule()); + }); +}); + +describe('buildRuleWithOverrides', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('it builds a rule as expected with filters present', () => { + const ruleSO = sampleRuleSO(); + ruleSO.attributes.params.filters = [ + { + query: 'host.name: Rebecca', + }, + { + query: 'host.name: Evan', + }, + { + query: 'host.name: Braden', + }, + ]; + const rule = buildRuleWithOverrides(ruleSO, sampleDocNoSortId()._source); + const expected: RulesSchema = { + ...expectedRule(), + filters: ruleSO.attributes.params.filters, + }; + expect(rule).toEqual(expected); + }); + + test('it builds a rule and removes internal tags', () => { + const ruleSO = sampleRuleSO(); + ruleSO.attributes.tags = [ + 'some fake tag 1', + 'some fake tag 2', + `${INTERNAL_RULE_ID_KEY}:rule-1`, + `${INTERNAL_IMMUTABLE_KEY}:true`, + ]; + const rule = buildRuleWithOverrides(ruleSO, sampleDocNoSortId()._source); + expect(rule).toEqual(expectedRule()); + }); + + test('it applies rule name override in buildRule', () => { + const ruleSO = sampleRuleSO(); + ruleSO.attributes.params.ruleNameOverride = 'someKey'; + const rule = buildRuleWithOverrides(ruleSO, sampleDocNoSortId()._source); + const expected = { + ...expectedRule(), + name: 'someValue', + rule_name_override: 'someKey', + meta: { + ruleNameOverridden: true, + }, + }; + expect(rule).toEqual(expected); + }); + + test('it applies risk score override in buildRule', () => { + const newRiskScore = 79; + const ruleSO = sampleRuleSO(); + ruleSO.attributes.params.riskScoreMapping = [ + { + field: 'new_risk_score', + // value and risk_score aren't used for anything but are required in the schema + value: '', + operator: 'equals', + risk_score: undefined, + }, + ]; + const doc = sampleDocNoSortId(); + doc._source.new_risk_score = newRiskScore; + const rule = buildRuleWithOverrides(ruleSO, doc._source); + const expected = { + ...expectedRule(), + risk_score: newRiskScore, + risk_score_mapping: ruleSO.attributes.params.riskScoreMapping, + meta: { + riskScoreOverridden: true, + }, + }; + expect(rule).toEqual(expected); + }); + + test('it applies severity override in buildRule', () => { + const eventSeverity = '42'; + const ruleSO = sampleRuleSO(); + ruleSO.attributes.params.severityMapping = [ + { + field: 'event.severity', + value: eventSeverity, + operator: 'equals', + severity: 'critical', + }, + ]; + const doc = sampleDocSeverity(Number(eventSeverity)); + const rule = buildRuleWithOverrides(ruleSO, doc._source); + const expected = { + ...expectedRule(), + severity: 'critical', + severity_mapping: ruleSO.attributes.params.severityMapping, + meta: { + severityOverrideField: 'event.severity', + }, + }; + expect(rule).toEqual(expected); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts index e5370735333bc7..344f705c4af24e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts @@ -9,7 +9,7 @@ import { RulesSchema } from '../../../../common/detection_engine/schemas/respons import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleTypeParams } from '../types'; import { buildRiskScoreFromMapping } from './mappings/build_risk_score_from_mapping'; -import { SignalSourceHit, RuleAlertAttributes } from './types'; +import { SignalSourceHit, RuleAlertAttributes, SignalSource } from './types'; import { buildSeverityFromMapping } from './mappings/build_severity_from_mapping'; import { buildRuleNameFromMapping } from './mappings/build_rule_name_from_mapping'; import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; @@ -46,19 +46,19 @@ export const buildRule = ({ throttle, }: BuildRuleParams): RulesSchema => { const { riskScore, riskScoreMeta } = buildRiskScoreFromMapping({ - doc, + eventSource: doc._source, riskScore: ruleParams.riskScore, riskScoreMapping: ruleParams.riskScoreMapping, }); const { severity, severityMeta } = buildSeverityFromMapping({ - doc, + eventSource: doc._source, severity: ruleParams.severity, severityMapping: ruleParams.severityMapping, }); const { ruleName, ruleNameMeta } = buildRuleNameFromMapping({ - doc, + eventSource: doc._source, ruleName: name, ruleNameMapping: ruleParams.ruleNameOverride, }); @@ -132,7 +132,7 @@ export const buildRuleWithoutOverrides = ( meta: ruleParams.meta, max_signals: ruleParams.maxSignals, risk_score: ruleParams.riskScore, - risk_score_mapping: ruleParams.riskScoreMapping ?? [], + risk_score_mapping: [], output_index: ruleParams.outputIndex, description: ruleParams.description, note: ruleParams.note, @@ -145,9 +145,8 @@ export const buildRuleWithoutOverrides = ( name: ruleSO.attributes.name, query: ruleParams.query, references: ruleParams.references, - rule_name_override: ruleParams.ruleNameOverride, severity: ruleParams.severity, - severity_mapping: ruleParams.severityMapping ?? [], + severity_mapping: [], tags: ruleSO.attributes.tags, type: ruleParams.type, to: ruleParams.to, @@ -184,3 +183,47 @@ export const removeInternalTagsFromRule = (rule: RulesSchema): RulesSchema => { return ruleWithoutInternalTags; } }; + +export const buildRuleWithOverrides = ( + ruleSO: SavedObject, + eventSource: SignalSource +): RulesSchema => { + const ruleWithoutOverrides = buildRuleWithoutOverrides(ruleSO); + return applyRuleOverrides(ruleWithoutOverrides, eventSource, ruleSO.attributes.params); +}; + +export const applyRuleOverrides = ( + rule: RulesSchema, + eventSource: SignalSource, + ruleParams: RuleTypeParams +): RulesSchema => { + const { riskScore, riskScoreMeta } = buildRiskScoreFromMapping({ + eventSource, + riskScore: ruleParams.riskScore, + riskScoreMapping: ruleParams.riskScoreMapping, + }); + + const { severity, severityMeta } = buildSeverityFromMapping({ + eventSource, + severity: ruleParams.severity, + severityMapping: ruleParams.severityMapping, + }); + + const { ruleName, ruleNameMeta } = buildRuleNameFromMapping({ + eventSource, + ruleName: rule.name, + ruleNameMapping: ruleParams.ruleNameOverride, + }); + + const meta = { ...ruleParams.meta, ...riskScoreMeta, ...severityMeta, ...ruleNameMeta }; + return { + ...rule, + risk_score: riskScore, + risk_score_mapping: ruleParams.riskScoreMapping ?? [], + severity, + severity_mapping: ruleParams.severityMapping ?? [], + name: ruleName, + rule_name_override: ruleParams.ruleNameOverride, + meta: Object.keys(meta).length > 0 ? meta : undefined, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.test.ts index e1d9c7f7c8a5ca..ff50c2634dfd16 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.test.ts @@ -14,7 +14,7 @@ describe('buildRiskScoreFromMapping', () => { test('risk score defaults to provided if mapping is incomplete', () => { const riskScore = buildRiskScoreFromMapping({ - doc: sampleDocNoSortId(), + eventSource: sampleDocNoSortId()._source, riskScore: 57, riskScoreMapping: undefined, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts index 888642f77af60c..c358339e66cd92 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts @@ -9,10 +9,10 @@ import { RiskScore, RiskScoreMappingOrUndefined, } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { SignalSourceHit } from '../types'; +import { SignalSource } from '../types'; interface BuildRiskScoreFromMappingProps { - doc: SignalSourceHit; + eventSource: SignalSource; riskScore: RiskScore; riskScoreMapping: RiskScoreMappingOrUndefined; } @@ -23,7 +23,7 @@ interface BuildRiskScoreFromMappingReturn { } export const buildRiskScoreFromMapping = ({ - doc, + eventSource, riskScore, riskScoreMapping, }: BuildRiskScoreFromMappingProps): BuildRiskScoreFromMappingReturn => { @@ -31,7 +31,7 @@ export const buildRiskScoreFromMapping = ({ if (riskScoreMapping != null && riskScoreMapping.length > 0) { const mappedField = riskScoreMapping[0].field; // TODO: Expand by verifying fieldType from index via doc._index - const mappedValue = get(mappedField, doc._source); + const mappedValue = get(mappedField, eventSource); if ( typeof mappedValue === 'number' && Number.isSafeInteger(mappedValue) && diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.test.ts index b509020646d1b5..b88a628b9735c2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.test.ts @@ -14,7 +14,7 @@ describe('buildRuleNameFromMapping', () => { test('rule name defaults to provided if mapping is incomplete', () => { const ruleName = buildRuleNameFromMapping({ - doc: sampleDocNoSortId(), + eventSource: sampleDocNoSortId()._source, ruleName: 'rule-name', ruleNameMapping: 'message', }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.ts index af540ed1454adf..c12baa556a7ff3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.ts @@ -10,10 +10,10 @@ import { Name, RuleNameOverrideOrUndefined, } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { SignalSourceHit } from '../types'; +import { SignalSource } from '../types'; interface BuildRuleNameFromMappingProps { - doc: SignalSourceHit; + eventSource: SignalSource; ruleName: Name; ruleNameMapping: RuleNameOverrideOrUndefined; } @@ -24,13 +24,13 @@ interface BuildRuleNameFromMappingReturn { } export const buildRuleNameFromMapping = ({ - doc, + eventSource, ruleName, ruleNameMapping, }: BuildRuleNameFromMappingProps): BuildRuleNameFromMappingReturn => { if (ruleNameMapping != null) { // TODO: Expand by verifying fieldType from index via doc._index - const mappedValue = get(ruleNameMapping, doc._source); + const mappedValue = get(ruleNameMapping, eventSource); if (t.string.is(mappedValue)) { return { ruleName: mappedValue, ruleNameMeta: { ruleNameOverridden: true } }; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.test.ts index fb1d51364ab393..430564cd985c2e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.test.ts @@ -14,7 +14,7 @@ describe('buildSeverityFromMapping', () => { test('severity defaults to provided if mapping is undefined', () => { const severity = buildSeverityFromMapping({ - doc: sampleDocNoSortId(), + eventSource: sampleDocNoSortId()._source, severity: 'low', severityMapping: undefined, }); @@ -24,7 +24,7 @@ describe('buildSeverityFromMapping', () => { test('severity is overridden to highest matched mapping', () => { const severity = buildSeverityFromMapping({ - doc: sampleDocSeverity(23), + eventSource: sampleDocSeverity(23)._source, severity: 'low', severityMapping: [ { field: 'event.severity', operator: 'equals', value: '23', severity: 'critical' }, @@ -44,7 +44,7 @@ describe('buildSeverityFromMapping', () => { test('severity is overridden when field is event.severity and source value is number', () => { const severity = buildSeverityFromMapping({ - doc: sampleDocSeverity(23), + eventSource: sampleDocSeverity(23)._source, severity: 'low', severityMapping: [ { field: 'event.severity', operator: 'equals', value: '13', severity: 'low' }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.ts index c0a62a2cc887d2..52ebd67f257af6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.ts @@ -11,10 +11,10 @@ import { severity as SeverityIOTS, SeverityMappingOrUndefined, } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { SignalSourceHit } from '../types'; +import { SignalSource } from '../types'; interface BuildSeverityFromMappingProps { - doc: SignalSourceHit; + eventSource: SignalSource; severity: Severity; severityMapping: SeverityMappingOrUndefined; } @@ -32,7 +32,7 @@ const severitySortMapping = { }; export const buildSeverityFromMapping = ({ - doc, + eventSource, severity, severityMapping, }: BuildSeverityFromMappingProps): BuildSeverityFromMappingReturn => { @@ -45,7 +45,7 @@ export const buildSeverityFromMapping = ({ ); severityMappingSorted.forEach((mapping) => { - const docValue = get(mapping.field, doc._source); + const docValue = get(mapping.field, eventSource); // TODO: Expand by verifying fieldType from index via doc._index // Till then, explicit parsing of event.severity (long) to number. If not ECS, this could be // another datatype, but until we can lookup datatype we must assume number for the Elastic diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index a3b37270e50b1a..2eee1c18cd4c40 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -455,7 +455,7 @@ export const signalRulesAlertType = ({ ); } else if (response.hits.events !== undefined) { newSignals = response.hits.events.map((event) => - wrapSignal(buildSignalFromEvent(event, savedObject), outputIndex) + wrapSignal(buildSignalFromEvent(event, savedObject, true), outputIndex) ); } else { throw new Error( From 935f6349a1d96abaca602652e6d212cba0064ab9 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Wed, 30 Sep 2020 16:22:09 -0400 Subject: [PATCH 11/36] [Search bar] Remove duplicate `popoverProps` (#79025) --- .../plugins/global_search_bar/public/components/search_bar.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index 40c34053ab949f..0dde28db0436d4 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -141,9 +141,6 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) { onChange={onChange} options={options} popoverButtonBreakpoints={['xs', 's']} - popoverProps={{ - repositionOnScroll: true, - }} popoverButton={ Date: Wed, 30 Sep 2020 22:28:05 +0200 Subject: [PATCH 12/36] [Security] Alert Telemetry for the Security app (#77200) This adds a `TelemetryEventsSender` component that can be used to publish Endpoint alerts to our Telemetry service. The alerts are filtered by a set of allowed fields (for PII) and batched in a queue to be sent once per minute. There is a cap of 100 alerts per minute to be sent. The component respects the telemetry opt-in status and enriches the alerts with the cluster ID and name. The Detection Engine is slightly modified to send endpoint telemetry events via the `TelemetryEventsSender`. Only the "custom query" rule type is modified because that's the only one that can create Endpoint Alerts. Co-authored-by: Elastic Machine Co-authored-by: Garrett Spong --- x-pack/plugins/security_solution/kibana.json | 3 +- .../signals/search_after_bulk_create.test.ts | 11 + .../signals/search_after_bulk_create.ts | 10 + .../signals/send_telemetry_events.test.ts | 78 ++++ .../signals/send_telemetry_events.ts | 46 +++ .../signals/signal_rule_alert_type.test.ts | 2 + .../signals/signal_rule_alert_type.ts | 5 + .../threat_mapping/create_threat_signal.ts | 2 + .../threat_mapping/create_threat_signals.ts | 2 + .../signals/threat_mapping/types.ts | 3 + .../lib/detection_engine/signals/types.ts | 2 + .../server/lib/telemetry/sender.test.ts | 241 ++++++++++++ .../server/lib/telemetry/sender.ts | 368 ++++++++++++++++++ .../security_solution/server/plugin.ts | 15 + 14 files changed, 787 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts create mode 100644 x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/telemetry/sender.ts diff --git a/x-pack/plugins/security_solution/kibana.json b/x-pack/plugins/security_solution/kibana.json index 7b5c3b5337c02d..7bd76838c7559b 100644 --- a/x-pack/plugins/security_solution/kibana.json +++ b/x-pack/plugins/security_solution/kibana.json @@ -27,7 +27,8 @@ "spaces", "usageCollection", "lists", - "home" + "home", + "telemetry" ], "server": true, "ui": true, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index 68c6a51b4e6f66..c82c1fe969ee3c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -130,6 +130,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [exceptionItem], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -255,6 +256,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [exceptionItem], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -334,6 +336,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [exceptionItem], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -394,6 +397,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [exceptionItem], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -452,6 +456,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [exceptionItem], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -535,6 +540,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [exceptionItem], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -615,6 +621,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -664,6 +671,7 @@ describe('searchAfterAndBulkCreate', () => { ruleParams: sampleParams, services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -719,6 +727,7 @@ describe('searchAfterAndBulkCreate', () => { ruleParams: sampleParams, services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -790,6 +799,7 @@ describe('searchAfterAndBulkCreate', () => { ruleParams: sampleParams, services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, @@ -898,6 +908,7 @@ describe('searchAfterAndBulkCreate', () => { exceptionsList: [], services: mockService, logger: mockLogger, + eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, signalsIndex: DEFAULT_SIGNALS_INDEX, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts index 2df180582a0ace..8fe55d97b569c2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -7,6 +7,7 @@ import { singleSearchAfter } from './single_search_after'; import { singleBulkCreate } from './single_bulk_create'; import { filterEventsAgainstList } from './filter_events_with_list'; +import { sendAlertTelemetryEvents } from './send_telemetry_events'; import { createSearchAfterReturnType, createSearchAfterReturnTypeFromResponse, @@ -25,6 +26,7 @@ export const searchAfterAndBulkCreate = async ({ services, listClient, logger, + eventsTelemetry, id, inputIndexPattern, signalsIndex, @@ -188,6 +190,14 @@ export const searchAfterAndBulkCreate = async ({ logger.debug( buildRuleMessage(`filteredEvents.hits.hits: ${filteredEvents.hits.hits.length}`) ); + + sendAlertTelemetryEvents( + logger, + eventsTelemetry, + filteredEvents, + ruleParams, + buildRuleMessage + ); } // we are guaranteed to have searchResult hits at this point diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts new file mode 100644 index 00000000000000..2a531998ff8a63 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { selectEvents } from './send_telemetry_events'; + +describe('sendAlertTelemetry', () => { + it('selectEvents', () => { + const filteredEvents = { + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + failed: 0, + skipped: 0, + }, + hits: { + total: 2, + max_score: 0, + hits: [ + { + _index: 'x', + _type: 'x', + _id: 'x', + _score: 0, + _source: { + '@timestamp': 'x', + key1: 'hello', + data_stream: { + dataset: 'endpoint.events', + }, + }, + }, + { + _index: 'x', + _type: 'x', + _id: 'x', + _score: 0, + _source: { + '@timestamp': 'x', + key2: 'hello', + data_stream: { + dataset: 'endpoint.alerts', + other: 'x', + }, + }, + }, + { + _index: 'x', + _type: 'x', + _id: 'x', + _score: 0, + _source: { + '@timestamp': 'x', + key3: 'hello', + data_stream: {}, + }, + }, + ], + }, + }; + + const sources = selectEvents(filteredEvents); + expect(sources).toStrictEqual([ + { + '@timestamp': 'x', + key2: 'hello', + data_stream: { + dataset: 'endpoint.alerts', + other: 'x', + }, + }, + ]); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts new file mode 100644 index 00000000000000..5963d31bda8a6b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TelemetryEventsSender, TelemetryEvent } from '../../telemetry/sender'; +import { RuleTypeParams } from '../types'; +import { BuildRuleMessage } from './rule_messages'; +import { SignalSearchResponse, SignalSource } from './types'; +import { Logger } from '../../../../../../../src/core/server'; + +export interface SearchResultWithSource { + _source: SignalSource; +} + +export function selectEvents(filteredEvents: SignalSearchResponse): TelemetryEvent[] { + const sources = filteredEvents.hits.hits.map(function ( + obj: SearchResultWithSource + ): TelemetryEvent { + return obj._source; + }); + + // Filter out non-endpoint alerts + return sources.filter((obj: TelemetryEvent) => obj.data_stream?.dataset === 'endpoint.alerts'); +} + +export function sendAlertTelemetryEvents( + logger: Logger, + eventsTelemetry: TelemetryEventsSender | undefined, + filteredEvents: SignalSearchResponse, + ruleParams: RuleTypeParams, + buildRuleMessage: BuildRuleMessage +) { + if (eventsTelemetry === undefined) { + return; + } + + const sources = selectEvents(filteredEvents); + + try { + eventsTelemetry.queueTelemetryEvents(sources); + } catch (exc) { + logger.error(buildRuleMessage(`[-] queing telemetry events failed ${exc}`)); + } +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 382acf2f382454..415abc9d995fba 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -138,6 +138,7 @@ describe('rules_notification_alert_type', () => { alert = signalRulesAlertType({ logger, + eventsTelemetry: undefined, version, ml: mlMock, lists: listMock.createSetup(), @@ -344,6 +345,7 @@ describe('rules_notification_alert_type', () => { payload = getPayload(ruleAlert, alertServices) as jest.Mocked; alert = signalRulesAlertType({ logger, + eventsTelemetry: undefined, version, ml: undefined, lists: undefined, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 2eee1c18cd4c40..95348808bb58fd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -56,6 +56,7 @@ import { ruleStatusServiceFactory } from './rule_status_service'; import { buildRuleMessageFactory } from './rule_messages'; import { ruleStatusSavedObjectsClientFactory } from './rule_status_saved_objects_client'; import { getNotificationResultsLink } from '../notifications/utils'; +import { TelemetryEventsSender } from '../../telemetry/sender'; import { buildEqlSearchRequest } from '../../../../common/detection_engine/get_query_filter'; import { bulkInsertSignals } from './single_bulk_create'; import { buildSignalFromEvent, buildSignalGroupFromSequence } from './build_bulk_body'; @@ -63,11 +64,13 @@ import { createThreatSignals } from './threat_mapping/create_threat_signals'; export const signalRulesAlertType = ({ logger, + eventsTelemetry, version, ml, lists, }: { logger: Logger; + eventsTelemetry: TelemetryEventsSender | undefined; version: string; ml: SetupPlugins['ml']; lists: SetupPlugins['lists'] | undefined; @@ -369,6 +372,7 @@ export const signalRulesAlertType = ({ previousStartedAt, listClient, logger, + eventsTelemetry, alertId, outputIndex, params, @@ -409,6 +413,7 @@ export const signalRulesAlertType = ({ ruleParams: params, services, logger, + eventsTelemetry, id: alertId, inputIndexPattern: inputIndex, signalsIndex: outputIndex, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts index a6d4a2ba58ddd0..560e7ad7fe2cb3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts @@ -28,6 +28,7 @@ export const createThreatSignal = async ({ previousStartedAt, listClient, logger, + eventsTelemetry, alertId, outputIndex, params, @@ -77,6 +78,7 @@ export const createThreatSignal = async ({ ruleParams: params, services, logger, + eventsTelemetry, id: alertId, inputIndexPattern: inputIndex, signalsIndex: outputIndex, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts index f416ae6703b662..f44c7a96844578 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts @@ -24,6 +24,7 @@ export const createThreatSignals = async ({ previousStartedAt, listClient, logger, + eventsTelemetry, alertId, outputIndex, params, @@ -79,6 +80,7 @@ export const createThreatSignals = async ({ previousStartedAt, listClient, logger, + eventsTelemetry, alertId, outputIndex, params, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts index d63f2d2b3b6aa6..7cd6e5196ea681 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts @@ -21,6 +21,7 @@ import { AlertServices } from '../../../../../../alerts/server'; import { ExceptionListItemSchema } from '../../../../../../lists/common/schemas'; import { ILegacyScopedClusterClient, Logger } from '../../../../../../../../src/core/server'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; +import { TelemetryEventsSender } from '../../../telemetry/sender'; import { BuildRuleMessage } from '../rule_messages'; import { SearchAfterAndBulkCreateReturnType } from '../types'; @@ -38,6 +39,7 @@ export interface CreateThreatSignalsOptions { previousStartedAt: Date | null; listClient: ListClient; logger: Logger; + eventsTelemetry: TelemetryEventsSender | undefined; alertId: string; outputIndex: string; params: RuleTypeParams; @@ -73,6 +75,7 @@ export interface CreateThreatSignalOptions { previousStartedAt: Date | null; listClient: ListClient; logger: Logger; + eventsTelemetry: TelemetryEventsSender | undefined; alertId: string; outputIndex: string; params: RuleTypeParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 2f6ed0c1e3a8e9..d9ca3dce54af3d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -21,6 +21,7 @@ import { ListClient } from '../../../../../lists/server'; import { Logger } from '../../../../../../../src/core/server'; import { ExceptionListItemSchema } from '../../../../../lists/common/schemas'; import { BuildRuleMessage } from './rule_messages'; +import { TelemetryEventsSender } from '../../telemetry/sender'; // used for gap detection code // eslint-disable-next-line @typescript-eslint/naming-convention @@ -211,6 +212,7 @@ export interface SearchAfterAndBulkCreateParams { listClient: ListClient; exceptionsList: ExceptionListItemSchema[]; logger: Logger; + eventsTelemetry: TelemetryEventsSender | undefined; id: string; inputIndexPattern: string[]; signalsIndex: string; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts new file mode 100644 index 00000000000000..1ebdcb6b9d3f4e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts @@ -0,0 +1,241 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable dot-notation */ +import { TelemetryEventsSender, copyAllowlistedFields, getV3UrlFromV2 } from './sender'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { URL } from 'url'; + +describe('TelemetryEventsSender', () => { + let logger: ReturnType; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + }); + + describe('processEvents', () => { + it('returns empty array when empty array is passed', () => { + const sender = new TelemetryEventsSender(logger); + const result = sender.processEvents([]); + expect(result).toStrictEqual([]); + }); + + it('applies the allowlist', () => { + const sender = new TelemetryEventsSender(logger); + const input = [ + { + event: { + kind: 'alert', + }, + agent: { + name: 'test', + }, + file: { + size: 3, + path: 'X', + test: 'me', + another: 'nope', + Ext: { + code_signature: { + key1: 'X', + key2: 'Y', + }, + malware_classification: { + key1: 'X', + }, + something_else: 'nope', + }, + }, + host: { + os: { + name: 'windows', + }, + something_else: 'nope', + }, + }, + ]; + + const result = sender.processEvents(input); + expect(result).toStrictEqual([ + { + event: { + kind: 'alert', + }, + agent: { + name: 'test', + }, + file: { + size: 3, + path: 'X', + Ext: { + code_signature: { + key1: 'X', + key2: 'Y', + }, + malware_classification: { + key1: 'X', + }, + }, + }, + host: { + os: { + name: 'windows', + }, + }, + }, + ]); + }); + }); + + describe('queueTelemetryEvents', () => { + it('queues two events', () => { + const sender = new TelemetryEventsSender(logger); + sender.queueTelemetryEvents([{ 'event.kind': '1' }, { 'event.kind': '2' }]); + expect(sender['queue'].length).toBe(2); + }); + + it('queues more than maxQueueSize events', () => { + const sender = new TelemetryEventsSender(logger); + sender['maxQueueSize'] = 5; + sender.queueTelemetryEvents([{ 'event.kind': '1' }, { 'event.kind': '2' }]); + sender.queueTelemetryEvents([{ 'event.kind': '3' }, { 'event.kind': '4' }]); + sender.queueTelemetryEvents([{ 'event.kind': '5' }, { 'event.kind': '6' }]); + sender.queueTelemetryEvents([{ 'event.kind': '7' }, { 'event.kind': '8' }]); + expect(sender['queue'].length).toBe(5); + }); + + it('empties the queue when sending', async () => { + const sender = new TelemetryEventsSender(logger); + sender['sendEvents'] = jest.fn(); + sender['telemetryStart'] = { + getIsOptedIn: jest.fn(async () => true), + }; + sender['telemetrySetup'] = { + getTelemetryUrl: jest.fn(async () => new URL('https://telemetry.elastic.co')), + }; + sender['fetchClusterInfo'] = jest.fn(async () => { + return { + cluster_name: 'test', + cluster_uuid: 'test-uuid', + }; + }); + + sender.queueTelemetryEvents([{ 'event.kind': '1' }, { 'event.kind': '2' }]); + expect(sender['queue'].length).toBe(2); + await sender['sendIfDue'](); + expect(sender['queue'].length).toBe(0); + expect(sender['sendEvents']).toBeCalledTimes(1); + sender.queueTelemetryEvents([{ 'event.kind': '3' }, { 'event.kind': '4' }]); + sender.queueTelemetryEvents([{ 'event.kind': '5' }, { 'event.kind': '6' }]); + expect(sender['queue'].length).toBe(4); + await sender['sendIfDue'](); + expect(sender['queue'].length).toBe(0); + expect(sender['sendEvents']).toBeCalledTimes(2); + }); + + it("shouldn't send when telemetry is disabled", async () => { + const sender = new TelemetryEventsSender(logger); + sender['sendEvents'] = jest.fn(); + const telemetryStart = { + getIsOptedIn: jest.fn(async () => false), + }; + sender['telemetryStart'] = telemetryStart; + + sender.queueTelemetryEvents([{ 'event.kind': '1' }, { 'event.kind': '2' }]); + expect(sender['queue'].length).toBe(2); + await sender['sendIfDue'](); + + expect(sender['queue'].length).toBe(0); + expect(sender['sendEvents']).toBeCalledTimes(0); + }); + }); +}); + +describe('allowlistEventFields', () => { + const allowlist = { + a: true, + b: true, + c: { + d: true, + }, + }; + + it('filters top level', () => { + const event = { + a: 'a', + a1: 'a1', + b: 'b', + b1: 'b1', + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + a: 'a', + b: 'b', + }); + }); + + it('filters nested', () => { + const event = { + a: { + a1: 'a1', + }, + a1: 'a1', + b: { + b1: 'b1', + }, + b1: 'b1', + c: { + d: 'd', + e: 'e', + f: 'f', + }, + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + a: { + a1: 'a1', + }, + b: { + b1: 'b1', + }, + c: { + d: 'd', + }, + }); + }); + + it("doesn't create empty objects", () => { + const event = { + a: 'a', + b: 'b', + c: { + e: 'e', + }, + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + a: 'a', + b: 'b', + }); + }); +}); + +describe('getV3UrlFromV2', () => { + it('should return prod url', () => { + expect(getV3UrlFromV2('https://telemetry.elastic.co/xpack/v2/send', 'alerts-endpoint')).toBe( + 'https://telemetry.elastic.co/v3/send/alerts-endpoint' + ); + }); + + it('should return staging url', () => { + expect( + getV3UrlFromV2('https://telemetry-staging.elastic.co/xpack/v2/send', 'alerts-endpoint') + ).toBe('https://telemetry-staging.elastic.co/v3-dev/send/alerts-endpoint'); + }); + + it('should support ports and auth', () => { + expect( + getV3UrlFromV2('http://user:pass@myproxy.local:1337/xpack/v2/send', 'alerts-endpoint') + ).toBe('http://user:pass@myproxy.local:1337/v3/send/alerts-endpoint'); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts new file mode 100644 index 00000000000000..acee75abddcd9d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -0,0 +1,368 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { cloneDeep } from 'lodash'; +import axios from 'axios'; +import { LegacyAPICaller } from 'kibana/server'; +import { URL } from 'url'; +import { Logger, CoreStart } from '../../../../../../src/core/server'; +import { transformDataToNdjson } from '../../utils/read_stream/create_stream_from_ndjson'; +import { + TelemetryPluginStart, + TelemetryPluginSetup, +} from '../../../../../../src/plugins/telemetry/server'; + +export type SearchTypes = + | string + | string[] + | number + | number[] + | boolean + | boolean[] + | object + | object[] + | undefined; + +export interface TelemetryEvent { + [key: string]: SearchTypes; + '@timestamp'?: string; + data_stream?: { + [key: string]: SearchTypes; + dataset?: string; + }; + cluster_name?: string; + cluster_uuid?: string; + file?: { + [key: string]: SearchTypes; + Ext?: { + [key: string]: SearchTypes; + }; + }; + license?: ESLicense; +} + +export class TelemetryEventsSender { + private readonly initialCheckDelayMs = 10 * 1000; + private readonly checkIntervalMs = 60 * 1000; + private readonly logger: Logger; + private core?: CoreStart; + private maxQueueSize = 100; + private telemetryStart?: TelemetryPluginStart; + private telemetrySetup?: TelemetryPluginSetup; + private intervalId?: NodeJS.Timeout; + private isSending = false; + private queue: TelemetryEvent[] = []; + private isOptedIn?: boolean = true; // Assume true until the first check + + constructor(logger: Logger) { + this.logger = logger.get('telemetry_events'); + } + + public setup(telemetrySetup?: TelemetryPluginSetup) { + this.telemetrySetup = telemetrySetup; + } + + public start(core?: CoreStart, telemetryStart?: TelemetryPluginStart) { + this.telemetryStart = telemetryStart; + this.core = core; + + this.logger.debug(`Starting task`); + setTimeout(() => { + this.sendIfDue(); + this.intervalId = setInterval(() => this.sendIfDue(), this.checkIntervalMs); + }, this.initialCheckDelayMs); + } + + public stop() { + if (this.intervalId) { + clearInterval(this.intervalId); + } + } + + public queueTelemetryEvents(events: TelemetryEvent[]) { + const qlength = this.queue.length; + + if (events.length === 0) { + return; + } + + this.logger.debug(`Queue events`); + + if (qlength >= this.maxQueueSize) { + // we're full already + return; + } + + if (events.length > this.maxQueueSize - qlength) { + this.queue.push(...this.processEvents(events.slice(0, this.maxQueueSize - qlength))); + } else { + this.queue.push(...this.processEvents(events)); + } + } + + public processEvents(events: TelemetryEvent[]): TelemetryEvent[] { + return events.map(function (obj: TelemetryEvent): TelemetryEvent { + return copyAllowlistedFields(allowlistEventFields, obj); + }); + } + + private async sendIfDue() { + if (this.isSending) { + return; + } + + if (this.queue.length === 0) { + return; + } + + try { + this.isSending = true; + + // Checking opt-in status is relatively expensive (calls a saved-object), so + // we only check it when we have things to send. + this.isOptedIn = await this.telemetryStart?.getIsOptedIn(); + if (!this.isOptedIn) { + this.logger.debug(`Telemetry is not opted-in.`); + this.queue = []; + this.isSending = false; + return; + } + + const [telemetryUrl, clusterInfo, licenseInfo] = await Promise.all([ + this.fetchTelemetryUrl(), + this.fetchClusterInfo(), + this.fetchLicenseInfo(), + ]); + + this.logger.debug(`Telemetry URL: ${telemetryUrl}`); + this.logger.debug( + `cluster_uuid: ${clusterInfo?.cluster_uuid} cluster_name: ${clusterInfo?.cluster_name}` + ); + + const toSend: TelemetryEvent[] = cloneDeep(this.queue).map((event) => ({ + ...event, + ...(licenseInfo ? { license: this.copyLicenseFields(licenseInfo) } : {}), + cluster_uuid: clusterInfo.cluster_uuid, + cluster_name: clusterInfo.cluster_name, + })); + this.queue = []; + + await this.sendEvents(toSend, telemetryUrl, clusterInfo.cluster_uuid, licenseInfo?.uid); + } catch (err) { + this.logger.warn(`Error sending telemetry events data: ${err}`); + this.queue = []; + } + this.isSending = false; + } + + private async fetchClusterInfo(): Promise { + if (!this.core) { + throw Error("Couldn't fetch cluster info because core is not available"); + } + const callCluster = this.core.elasticsearch.legacy.client.callAsInternalUser; + return getClusterInfo(callCluster); + } + + private async fetchTelemetryUrl(): Promise { + const telemetryUrl = await this.telemetrySetup?.getTelemetryUrl(); + if (!telemetryUrl) { + throw Error("Couldn't get telemetry URL"); + } + return getV3UrlFromV2(telemetryUrl.toString(), 'alerts-endpoint'); + } + + private async fetchLicenseInfo(): Promise { + if (!this.core) { + return undefined; + } + try { + const callCluster = this.core.elasticsearch.legacy.client.callAsInternalUser; + const ret = await getLicense(callCluster, true); + return ret.license; + } catch (err) { + this.logger.warn(`Error retrieving license: ${err}`); + return undefined; + } + } + + private copyLicenseFields(lic: ESLicense) { + return { + uid: lic.uid, + status: lic.status, + type: lic.type, + ...(lic.issued_to ? { issued_to: lic.issued_to } : {}), + ...(lic.issuer ? { issuer: lic.issuer } : {}), + }; + } + + private async sendEvents( + events: unknown[], + telemetryUrl: string, + clusterUuid: string, + licenseId: string | undefined + ) { + // this.logger.debug(`Sending events: ${JSON.stringify(events, null, 2)}`); + const ndjson = transformDataToNdjson(events); + // this.logger.debug(`NDJSON: ${ndjson}`); + + try { + const resp = await axios.post(telemetryUrl, ndjson, { + headers: { + 'Content-Type': 'application/x-ndjson', + 'X-Elastic-Cluster-ID': clusterUuid, + ...(licenseId ? { 'X-Elastic-License-ID': licenseId } : {}), + 'X-Elastic-Telemetry': '1', // TODO: no longer needed? + }, + }); + this.logger.debug(`Events sent!. Response: ${resp.status} ${JSON.stringify(resp.data)}`); + } catch (err) { + this.logger.warn( + `Error sending events: ${err.response.status} ${JSON.stringify(err.response.data)}` + ); + } + } +} + +// For the Allowlist definition. +interface AllowlistFields { + [key: string]: boolean | AllowlistFields; +} + +// Allow list for the data we include in the events. True means that it is deep-cloned +// blindly. Object contents means that we only copy the fields that appear explicitly in +// the sub-object. +const allowlistEventFields: AllowlistFields = { + '@timestamp': true, + agent: true, + Endpoint: true, + ecs: true, + elastic: true, + event: true, + file: { + name: true, + path: true, + size: true, + created: true, + accessed: true, + mtime: true, + directory: true, + hash: true, + Ext: { + code_signature: true, + malware_classification: true, + }, + }, + host: { + os: true, + }, + process: { + name: true, + executable: true, + command_line: true, + hash: true, + Ext: { + code_signature: true, + }, + parent: { + name: true, + executable: true, + command_line: true, + hash: true, + Ext: { + code_signature: true, + }, + }, + }, +}; + +export function copyAllowlistedFields( + allowlist: AllowlistFields, + event: TelemetryEvent +): TelemetryEvent { + return Object.entries(allowlist).reduce((newEvent, [allowKey, allowValue]) => { + const eventValue = event[allowKey]; + if (eventValue) { + if (allowValue === true) { + return { ...newEvent, [allowKey]: eventValue }; + } else if (typeof allowValue === 'object' && typeof eventValue === 'object') { + const values = copyAllowlistedFields(allowValue, eventValue as TelemetryEvent); + return { + ...newEvent, + ...(Object.keys(values).length > 0 ? { [allowKey]: values } : {}), + }; + } + } + return newEvent; + }, {}); +} + +// Forms URLs like: +// https://telemetry.elastic.co/v3/send/my-channel-name or +// https://telemetry-staging.elastic.co/v3-dev/send/my-channel-name +export function getV3UrlFromV2(v2url: string, channel: string): string { + const url = new URL(v2url); + if (!url.hostname.includes('staging')) { + url.pathname = `/v3/send/${channel}`; + } else { + url.pathname = `/v3-dev/send/${channel}`; + } + return url.toString(); +} + +// For getting cluster info. Copied from telemetry_collection/get_cluster_info.ts +export interface ESClusterInfo { + cluster_uuid: string; + cluster_name: string; + version?: { + number: string; + build_flavor: string; + build_type: string; + build_hash: string; + build_date: string; + build_snapshot?: boolean; + lucene_version: string; + minimum_wire_compatibility_version: string; + minimum_index_compatibility_version: string; + }; +} + +/** + * Get the cluster info from the connected cluster. + * + * This is the equivalent to GET / + * + * @param {function} callCluster The callWithInternalUser handler (exposed for testing) + */ +function getClusterInfo(callCluster: LegacyAPICaller) { + return callCluster('info'); +} + +// From https://www.elastic.co/guide/en/elasticsearch/reference/current/get-license.html +export interface ESLicense { + status: string; + uid: string; + type: string; + issue_date?: string; + issue_date_in_millis?: number; + expiry_date?: string; + expirty_date_in_millis?: number; + max_nodes?: number; + issued_to?: string; + issuer?: string; + start_date_in_millis?: number; +} + +function getLicense(callCluster: LegacyAPICaller, local: boolean) { + return callCluster<{ license: ESLicense }>('transport.request', { + method: 'GET', + path: '/_license', + query: { + local, + // For versions >= 7.6 and < 8.0, this flag is needed otherwise 'platinum' is returned for 'enterprise' license. + accept_enterprise: 'true', + }, + }); +} diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 22dbd623930c54..177978c888ebe6 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -64,6 +64,11 @@ import { registerTrustedAppsRoutes } from './endpoint/routes/trusted_apps'; import { securitySolutionSearchStrategyProvider } from './search_strategy/security_solution'; import { securitySolutionIndexFieldsProvider } from './search_strategy/index_fields'; import { securitySolutionTimelineSearchStrategyProvider } from './search_strategy/timeline'; +import { TelemetryEventsSender } from './lib/telemetry/sender'; +import { + TelemetryPluginStart, + TelemetryPluginSetup, +} from '../../../../src/plugins/telemetry/server'; export interface SetupPlugins { alerts: AlertingSetup; @@ -77,12 +82,14 @@ export interface SetupPlugins { spaces?: SpacesSetup; taskManager?: TaskManagerSetupContract; usageCollection?: UsageCollectionSetup; + telemetry?: TelemetryPluginSetup; } export interface StartPlugins { data: DataPluginStart; ingestManager?: IngestManagerStartContract; taskManager?: TaskManagerStartContract; + telemetry?: TelemetryPluginStart; } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -107,6 +114,7 @@ export class Plugin implements IPlugin({ max: 3, maxAge: 1000 * 60 * 5 }); + this.telemetryEventsSender = new TelemetryEventsSender(this.logger); this.logger.debug('plugin initialized'); } @@ -241,6 +250,7 @@ export class Plugin implements IPlugin Date: Wed, 30 Sep 2020 13:32:10 -0700 Subject: [PATCH 13/36] Fixes for the Ticket 78375 (#79004) --- .../apps/endpoint/resolver.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts index 3e9726bf40073d..13b76f77383215 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts @@ -12,9 +12,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const esArchiver = getService('esArchiver'); const queryBar = getService('queryBar'); + const browser = getService('browser'); - // FLAKY: https://github.com/elastic/kibana/issues/78375 - describe.skip('Endpoint Event Resolver', function () { + describe('Endpoint Event Resolver', function () { before(async () => { await esArchiver.load('endpoint/resolver_tree', { useCreate: true }); await pageObjects.hosts.navigateToSecurityHostsPage(); @@ -23,9 +23,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await queryBar.setQuery('event.dataset : endpoint.events.file'); await queryBar.submitQuery(); - await (await testSubjects.find('draggable-content-host.name')).click(); + await browser.refresh(); + await browser.setWindowSize(1800, 1200); + await testSubjects.click('draggable-content-host.name'); await testSubjects.existOrFail('header-page-title'); - await (await testSubjects.find('navigation-events')).click(); + await testSubjects.click('navigation-events'); await testSubjects.existOrFail('events-viewer-panel'); await testSubjects.exists('investigate-in-resolver-button', { timeout: 4000 }); await (await testSubjects.findAll('investigate-in-resolver-button'))[0].click(); From 2377d12e7da4cf3f58bf4de8244ec2dc1c8f79dc Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Wed, 30 Sep 2020 17:44:16 -0400 Subject: [PATCH 14/36] [SECURITY_SOLUTION] unskip tests after fixing Kibana and package (#78954) --- .../endpoint/metadata/destination_index/data.json | 6 +++--- .../apis/epm/install_remove_assets.ts | 7 +++---- .../apis/epm/update_assets.ts | 3 +-- .../0.1.0/elasticsearch/transform/test/default.json | 2 +- x-pack/test/ingest_manager_api_integration/config.ts | 2 +- .../apps/endpoint/endpoint_list.ts | 12 +++++------- .../apis/artifacts/index.ts | 3 +-- .../apis/metadata.ts | 3 +-- 8 files changed, 16 insertions(+), 22 deletions(-) diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/destination_index/data.json b/x-pack/test/functional/es_archives/endpoint/metadata/destination_index/data.json index b19e5e2cbf1d7f..ef840d454a7630 100644 --- a/x-pack/test/functional/es_archives/endpoint/metadata/destination_index/data.json +++ b/x-pack/test/functional/es_archives/endpoint/metadata/destination_index/data.json @@ -2,7 +2,7 @@ "type": "doc", "value": { "id": "M92ScEJT9M9QusfIi3hpEb0AAAAAAAAA", - "index": "metrics-endpoint.metadata_current-default", + "index": "metrics-endpoint.metadata_current_default", "source": { "HostDetails": { "@timestamp": 1579881969541, @@ -75,7 +75,7 @@ "type": "doc", "value": { "id": "OU3RgCJaNnR90byeDEHutp8AAAAAAAAA", - "index": "metrics-endpoint.metadata_current-default", + "index": "metrics-endpoint.metadata_current_default", "source": { "HostDetails": { "@timestamp": 1579881969541, @@ -151,7 +151,7 @@ "type": "doc", "value": { "id": "YjqDCEuI6JmLeLOSyZx_NhMAAAAAAAAA", - "index": "metrics-endpoint.metadata_current-default", + "index": "metrics-endpoint.metadata_current_default", "source": { "HostDetails": { "@timestamp": 1579881969541, diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts index a0675017668733..d33d0445d1cd6e 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts @@ -29,8 +29,7 @@ export default function (providerContext: FtrProviderContext) { .send({ force: true }); }; - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 - describe.skip('installs and uninstalls all assets', async () => { + describe('installs and uninstalls all assets', async () => { describe('installs all assets when installing a package for the first time', async () => { skipIfNoDockerRegistry(providerContext); before(async () => { @@ -88,7 +87,7 @@ export default function (providerContext: FtrProviderContext) { it('should have installed the transform components', async function () { const res = await es.transport.request({ method: 'GET', - path: `/_transform/${pkgName}-test-default-${pkgVersion}`, + path: `/_transform/${pkgName}.test-default-${pkgVersion}`, }); expect(res.statusCode).equal(200); }); @@ -170,7 +169,7 @@ export default function (providerContext: FtrProviderContext) { type: 'index_template', }, { - id: 'logs-all_assets.test_logs-default-0.1.0', + id: 'all_assets.test-default-0.1.0', type: 'transform', }, ], diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts index 8203b4d1838716..9af27f5f985582 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts @@ -32,8 +32,7 @@ export default function (providerContext: FtrProviderContext) { .send({ force: true }); }; - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 - describe.skip('updates all assets when updating a package to a different version', async () => { + describe('updates all assets when updating a package to a different version', async () => { skipIfNoDockerRegistry(providerContext); before(async () => { await installPackage(pkgKey); diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/elasticsearch/transform/test/default.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/elasticsearch/transform/test/default.json index 27f75af131eedb..eddc6bc0c304a8 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/elasticsearch/transform/test/default.json +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/elasticsearch/transform/test/default.json @@ -3,7 +3,7 @@ "index": "logs-all_assets.test_log-default*" }, "dest": { - "index": "logs-all_assets.test_log_current-default" + "index": "logs-all_assets.test_log_current_default" }, "pivot": { "group_by": { diff --git a/x-pack/test/ingest_manager_api_integration/config.ts b/x-pack/test/ingest_manager_api_integration/config.ts index 94fbee0593d3e7..862ef732cb8b4a 100644 --- a/x-pack/test/ingest_manager_api_integration/config.ts +++ b/x-pack/test/ingest_manager_api_integration/config.ts @@ -12,7 +12,7 @@ import { defineDockerServersConfig } from '@kbn/test'; // Docker image to use for Ingest Manager API integration tests. // This hash comes from the commit hash here: https://github.com/elastic/package-storage/commit export const dockerImage = - 'docker.elastic.co/package-registry/distribution:5e0e12ce1bc2cb0c2f67f2e07d11b9a6043bcf25'; + 'docker.elastic.co/package-registry/distribution:518a65a993ab7e9c77b1d8d20cd6f847921d04ec'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index 569378df5930ae..e81851238882cf 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -64,8 +64,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ], ]; - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 - describe.skip('endpoint list', function () { + describe('endpoint list', function () { this.tags('ciGroup7'); const sleep = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -85,8 +84,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('finds data after load and polling', async () => { - await esArchiver.load('endpoint/metadata/api_feature', { useCreate: true }); - await pageObjects.endpoint.waitForTableToHaveData('endpointListTable', 100000); + await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); + await pageObjects.endpoint.waitForTableToHaveData('endpointListTable', 1100); const tableData = await pageObjects.endpointPageUtils.tableData('endpointListTable'); expect(tableData).to.eql(expectedData); }); @@ -94,8 +93,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('when there is data,', () => { before(async () => { - await esArchiver.load('endpoint/metadata/api_feature', { useCreate: true }); - await sleep(100000); + await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); await pageObjects.endpoint.navigateToEndpointList(); }); after(async () => { @@ -212,7 +210,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('displays the correct table data for the kql queries', () => { before(async () => { - await esArchiver.load('endpoint/metadata/api_feature', { useCreate: true }); + await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); await pageObjects.endpoint.navigateToEndpointList(); }); after(async () => { diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts index 5a4053ee6f0a9a..17a4182fe9371d 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts @@ -18,8 +18,7 @@ export default function (providerContext: FtrProviderContext) { const supertestWithoutAuth = getSupertestWithoutAuth(providerContext); let agentAccessAPIKey: string; - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 - describe.skip('artifact download', () => { + describe('artifact download', () => { before(async () => { await esArchiver.load('endpoint/artifacts/api_feature', { useCreate: true }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts index ad0cbd765f1fc4..50f3374b7b84dc 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts @@ -22,8 +22,7 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 - describe.skip('test metadata api', () => { + describe('test metadata api', () => { describe(`POST ${METADATA_REQUEST_ROUTE} when index is empty`, () => { it('metadata api should return empty result when index is empty', async () => { await deleteMetadataStream(getService); From 71dc864dfb08baaf22b13d431f9d5779d0221fd1 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 30 Sep 2020 15:19:06 -0700 Subject: [PATCH 15/36] [kbn/config] don't include x-pack/examples with --oss (#79032) Co-authored-by: spalger --- packages/kbn-config/src/env.test.ts | 12 ++++++++++++ packages/kbn-config/src/env.ts | 5 +++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/kbn-config/src/env.test.ts b/packages/kbn-config/src/env.test.ts index f3d51a021246e5..1613a90951d403 100644 --- a/packages/kbn-config/src/env.test.ts +++ b/packages/kbn-config/src/env.test.ts @@ -198,6 +198,18 @@ test('pluginSearchPaths contains x-pack/examples plugins path if --run-examples expect(env.pluginSearchPaths).toContain('/some/home/dir/x-pack/examples'); }); +test('pluginSearchPaths does not contain x-pack/examples plugins path if --oss flag is true', () => { + const env = new Env( + '/some/home/dir', + packageInfos, + getEnvOptions({ + cliArgs: { runExamples: true, oss: true }, + }) + ); + + expect(env.pluginSearchPaths).not.toContain('/some/home/dir/x-pack/examples'); +}); + test('pluginSearchPaths does not contains examples plugins path if --run-examples flag is false', () => { const env = new Env( '/some/home/dir', diff --git a/packages/kbn-config/src/env.ts b/packages/kbn-config/src/env.ts index 250c7b72d47a9c..e4585056696f92 100644 --- a/packages/kbn-config/src/env.ts +++ b/packages/kbn-config/src/env.ts @@ -123,8 +123,9 @@ export class Env { resolve(this.homeDir, 'src', 'plugins'), ...(options.cliArgs.oss ? [] : [resolve(this.homeDir, 'x-pack', 'plugins')]), resolve(this.homeDir, 'plugins'), - ...(options.cliArgs.runExamples - ? [resolve(this.homeDir, 'examples'), resolve(this.homeDir, 'x-pack', 'examples')] + ...(options.cliArgs.runExamples ? [resolve(this.homeDir, 'examples')] : []), + ...(options.cliArgs.runExamples && !options.cliArgs.oss + ? [resolve(this.homeDir, 'x-pack', 'examples')] : []), resolve(this.homeDir, '..', 'kibana-extra'), ]; From 886ad8504b87599afff296e10cea63dbc920502c Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 30 Sep 2020 15:37:31 -0700 Subject: [PATCH 16/36] skip flaky suite (#77401) --- .../functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts index 359be662b0216b..9de7d99c095e52 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts @@ -40,7 +40,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { return createdAlert; } - describe('alerts', function () { + // FLAKY: https://github.com/elastic/kibana/issues/77401 + describe.skip('alerts', function () { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('alertsTab'); From 06d1628a004b4773c47c6a33a50700c85f66ef81 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 30 Sep 2020 15:44:43 -0700 Subject: [PATCH 17/36] [ts] enable "resolveJsonModule" and disable existing failures (#78855) Co-authored-by: spalger --- tsconfig.base.json | 2 ++ typings/index.d.ts | 9 --------- .../app/ErrorGroupOverview/List/__test__/List.test.tsx | 1 + .../__test__/DiscoverTransactionButton.test.tsx | 2 ++ .../server/lib/service_map/group_resource_nodes.test.ts | 3 ++- .../apm/server/lib/services/annotations/index.test.ts | 3 +++ .../canvas_plugin_src/functions/server/demodata/index.ts | 1 + .../shareable_runtime/components/__tests__/app.test.tsx | 8 ++++---- x-pack/plugins/canvas/shareable_runtime/test/index.ts | 9 ++++++++- x-pack/plugins/infra/public/utils/apollo_client.ts | 1 + .../annotation_description_list/index.test.tsx | 1 + .../components/action_delete/delete_action_name.test.tsx | 8 +++++++- .../timeseries_chart_annotations.test.ts | 1 + .../models/job_validation/validate_time_range.test.ts | 1 + .../public/common/lib/compose/helpers.test.ts | 1 + .../public/common/lib/compose/kibana_compose.tsx | 1 + .../timeline/body/renderers/suricata/suricata_links.ts | 5 ++++- .../routes/index/get_signals_template.ts | 9 +++++++-- .../detection_engine/rules/get_prepackaged_rules.test.ts | 2 ++ .../lib/detection_engine/rules/get_prepackaged_rules.ts | 8 ++++++-- .../transform/public/app/common/transform_stats.test.ts | 7 ++++--- .../components/action_start/start_action_name.test.tsx | 1 + .../components/action_stop/stop_action_name.test.tsx | 1 + .../components/transform_list/common.test.ts | 5 +++-- .../components/transform_list/expanded_row.test.tsx | 1 + .../components/tabs/checkup/checkup_tab.test.tsx | 1 + .../server/lib/es_migration_apis.test.ts | 1 + .../api_integration/services/infraops_graphql_client.ts | 1 + .../services/security_solution_graphql_client.ts | 1 + x-pack/tsconfig.json | 1 - x-pack/typings/index.d.ts | 9 --------- 31 files changed, 69 insertions(+), 36 deletions(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 58a6237846a547..0aad8d6b9c124e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -39,6 +39,8 @@ // Resolve modules in the same way as Node.js. Aka make `require` works the // same in TypeScript as it does in Node.js. "moduleResolution": "node", + // "resolveJsonModule" allows for importing, extracting types from and generating .json files. + "resolveJsonModule": true, // Disallow inconsistently-cased references to the same file. "forceConsistentCasingInFileNames": true, // Forbid unused local variables as the rule was deprecated by ts-lint diff --git a/typings/index.d.ts b/typings/index.d.ts index 6d97aca4024c37..db6530d3f9e0be 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -35,15 +35,6 @@ declare module '*.svg' { export default content; } -// allow JSON files to be imported directly without lint errors -// see: https://github.com/palantir/tslint/issues/1264#issuecomment-228433367 -// and: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#arbitrary-expressions-are-forbidden-in-export-assignments-in-ambient-contexts -declare module '*.json' { - const json: any; - // eslint-disable-next-line import/no-default-export - export default json; -} - type MethodKeysOf = { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; }[keyof T]; diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx index 84b72b62248b0f..a5a38da3d5d886 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx @@ -43,6 +43,7 @@ describe('ErrorGroupOverview -> List', () => { + {/* @ts-expect-error invalid json props */} diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx index 02e0e72826f3a5..17dca4796ec749 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx @@ -15,6 +15,7 @@ import mockTransaction from './mockTransaction.json'; describe('DiscoverTransactionLink component', () => { it('should render with data', () => { + // @ts-expect-error invalid json mock const transaction: Transaction = mockTransaction; expect( @@ -25,6 +26,7 @@ describe('DiscoverTransactionLink component', () => { describe('getDiscoverQuery', () => { it('should return the correct query params object', () => { + // @ts-expect-error invalid json mock const transaction: Transaction = mockTransaction; const result = getDiscoverQuery(transaction); expect(result).toMatchSnapshot(); diff --git a/x-pack/plugins/apm/server/lib/service_map/group_resource_nodes.test.ts b/x-pack/plugins/apm/server/lib/service_map/group_resource_nodes.test.ts index 2a9a2daf1fe47f..23ef3f92e21a24 100644 --- a/x-pack/plugins/apm/server/lib/service_map/group_resource_nodes.test.ts +++ b/x-pack/plugins/apm/server/lib/service_map/group_resource_nodes.test.ts @@ -10,6 +10,7 @@ import expectedGroupedData from './mock_responses/group_resource_nodes_grouped.j describe('groupResourceNodes', () => { it('should group external nodes', () => { + // @ts-expect-error invalid json mock const responseWithGroups = groupResourceNodes(preGroupedData); expect(responseWithGroups.elements).toHaveLength( expectedGroupedData.elements.length @@ -17,7 +18,7 @@ describe('groupResourceNodes', () => { for (const element of responseWithGroups.elements) { const expectedElement = expectedGroupedData.elements.find( ({ data: { id } }: { data: { id: string } }) => id === element.data.id - ); + )!; expect(element).toMatchObject(expectedElement); } }); diff --git a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts index e1a3ee1c9380df..9bd9c7b7a10404 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts @@ -31,6 +31,7 @@ describe('getServiceAnnotations', () => { searchAggregatedTransactions: false, }), { + // @ts-expect-error invalid json mock mockResponse: () => noVersions, } ); @@ -50,6 +51,7 @@ describe('getServiceAnnotations', () => { searchAggregatedTransactions: false, }), { + // @ts-expect-error invalid json mock mockResponse: () => oneVersion, } ); @@ -74,6 +76,7 @@ describe('getServiceAnnotations', () => { searchAggregatedTransactions: false, }), { + // @ts-expect-error invalid json mock mockResponse: () => responses.shift(), } ); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts index e29f1f511685e3..102e6cdc726d58 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts @@ -61,6 +61,7 @@ export function demodata(): ExpressionFunctionDefinition< { id: 'project', name: 'project', meta: { type: 'string' } }, { id: 'percent_uptime', name: 'percent_uptime', meta: { type: 'number' } }, ], + // @ts-expect-error invalid json mock rows: sortBy(demoRows, 'time'), }; } else if (args.type === DemoRows.SHIRTS) { diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx b/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx index 4b4f8f7d4de66d..2ec3cfde8bd681 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx +++ b/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -/* - One test relies on react-dom at a version of 16.9... it can be enabled - once renovate completes the upgrade. Relevant code has been commented out - in the meantime. +/* + One test relies on react-dom at a version of 16.9... it can be enabled + once renovate completes the upgrade. Relevant code has been commented out + in the meantime. */ import { mount, ReactWrapper } from 'enzyme'; diff --git a/x-pack/plugins/canvas/shareable_runtime/test/index.ts b/x-pack/plugins/canvas/shareable_runtime/test/index.ts index e07d94a6e10541..288dd0dc3a5be8 100644 --- a/x-pack/plugins/canvas/shareable_runtime/test/index.ts +++ b/x-pack/plugins/canvas/shareable_runtime/test/index.ts @@ -11,4 +11,11 @@ import test from './workpads/test.json'; export * from './utils'; export type WorkpadNames = keyof typeof sharedWorkpads; -export const sharedWorkpads = { hello, austin, test }; +export const sharedWorkpads = { + // TODO: the automatic types for these JSON files are insufficient, and "austin" is so massive + // that Typescript refuses to type it. These should be converted to TypeScript and typed to fit + // the requirements. "austin" should also be reduced to the necessary data + hello: hello as any, + austin: austin as any, + test: test as any, +}; diff --git a/x-pack/plugins/infra/public/utils/apollo_client.ts b/x-pack/plugins/infra/public/utils/apollo_client.ts index 3c69ef4c98fac2..41831a03cabbbe 100644 --- a/x-pack/plugins/infra/public/utils/apollo_client.ts +++ b/x-pack/plugins/infra/public/utils/apollo_client.ts @@ -16,6 +16,7 @@ export const createApolloClient = (fetch: HttpHandler) => { const cache = new InMemoryCache({ addTypename: false, fragmentMatcher: new IntrospectionFragmentMatcher({ + // @ts-expect-error apollo-cache-inmemory types don't match actual introspection data introspectionQueryResultData, }), }); diff --git a/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.test.tsx b/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.test.tsx index 2568a6f40d3266..e7330ca1dbe5dd 100644 --- a/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.test.tsx +++ b/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.test.tsx @@ -21,6 +21,7 @@ describe('AnnotationDescriptionList', () => { }); test('Initialization with annotation.', () => { + // @ts-expect-error mock data is too loosely typed const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx index e033af64361302..63c4a0fe889f60 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx @@ -51,6 +51,7 @@ describe('DeleteAction', () => { it('should display a tooltip when isDisabled prop is true.', () => { const { container } = render( + // @ts-expect-error mock data is incorrectly typed ); @@ -59,6 +60,7 @@ describe('DeleteAction', () => { it('should not display a tooltip when isDisabled prop is false.', () => { const { container } = render( + // @ts-expect-error mock data is incorrectly typed ); @@ -78,8 +80,12 @@ describe('DeleteAction', () => { {deleteAction.isModalVisible && } diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.test.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.test.ts index 240e840363a1f3..cf7e2890c1fd2d 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.test.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.test.ts @@ -10,6 +10,7 @@ import { getAnnotationLevels } from './timeseries_chart_annotations'; describe('Timeseries Chart Annotations: getAnnotationLevels()', () => { test('getAnnotationLevels()', () => { + // @ts-expect-error mock data is too loosely typed const levels = getAnnotationLevels(mockAnnotationsOverlap); expect(levels).toEqual({ A: 0, B: 1, C: 2, D: 2 }); }); diff --git a/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts b/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts index ddf73166e18589..6065d22406bd00 100644 --- a/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts +++ b/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts @@ -144,6 +144,7 @@ describe('ML - validateTimeRange', () => { it('invalid time field', () => { const mockSearchResponseInvalid = cloneDeep(mockSearchResponse); + // @ts-expect-error creating intentionally invalid data mockSearchResponseInvalid.fieldCaps = undefined; const duration = { start: 0, end: 1 }; return validateTimeRange( diff --git a/x-pack/plugins/security_solution/public/common/lib/compose/helpers.test.ts b/x-pack/plugins/security_solution/public/common/lib/compose/helpers.test.ts index c34027648c8967..01f74a5678be69 100644 --- a/x-pack/plugins/security_solution/public/common/lib/compose/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/lib/compose/helpers.test.ts @@ -26,6 +26,7 @@ describe('getLinks helper', () => { const mockCache = new InMemoryCache({ dataIdFromObject: () => null, fragmentMatcher: new IntrospectionFragmentMatcher({ + // @ts-expect-error apollo-cache-inmemory types don't match actual introspection data introspectionQueryResultData, }), }); diff --git a/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx b/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx index 30d3311a40b619..055e2591f78051 100644 --- a/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx @@ -17,6 +17,7 @@ export function composeLibs(core: CoreStart): AppFrontendLibs { const cache = new InMemoryCache({ dataIdFromObject: () => null, fragmentMatcher: new IntrospectionFragmentMatcher({ + // @ts-expect-error apollo-cache-inmemory types don't match actual introspection data introspectionQueryResultData, }), }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_links.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_links.ts index fe6b44af202a8b..4f389382dc475a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_links.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_links.ts @@ -7,8 +7,11 @@ import { uniq } from 'lodash/fp'; import { db } from 'suricata-sid-db'; +const has = (obj: T, key: string | number | symbol): key is keyof T => + Object.prototype.hasOwnProperty.call(obj, key); + export const getLinksFromSignature = (id: number): string[] => { - const refs = db[id]; + const refs = has(db, id) ? db[id] : null; if (refs != null) { return uniq(refs); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts index cc22f34560c713..5ccde6d3eab97b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts @@ -8,7 +8,6 @@ import signalsMapping from './signals_mapping.json'; import ecsMapping from './ecs_mapping.json'; export const getSignalsTemplate = (index: string) => { - ecsMapping.mappings.properties.signal = signalsMapping.mappings.properties.signal; const template = { settings: { index: { @@ -24,7 +23,13 @@ export const getSignalsTemplate = (index: string) => { }, }, index_patterns: [`${index}-*`], - mappings: ecsMapping.mappings, + mappings: { + ...ecsMapping.mappings, + properties: { + ...ecsMapping.mappings.properties, + signal: signalsMapping.mappings.properties.signal, + }, + }, version: 1, }; return template; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.test.ts index ed1a239facf792..cf9a566cf4f903 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.test.ts @@ -33,12 +33,14 @@ describe('get_existing_prepackaged_rules', () => { }); test('should throw an exception if a pre-packaged rule is not valid', () => { + // @ts-expect-error intentionally invalid argument expect(() => getPrepackagedRules([{ not_valid_made_up_key: true }])).toThrow( 'name: "(rule name unknown)", rule_id: "(rule rule_id unknown)" within the folder rules/prepackaged_rules is not a valid detection engine rule. Expect the system to not work with pre-packaged rules until this rule is fixed or the file is removed. Error is: Invalid value "undefined" supplied to "description",Invalid value "undefined" supplied to "risk_score",Invalid value "undefined" supplied to "name",Invalid value "undefined" supplied to "severity",Invalid value "undefined" supplied to "type",Invalid value "undefined" supplied to "rule_id",Invalid value "undefined" supplied to "version", Full rule contents are:\n{\n "not_valid_made_up_key": true\n}' ); }); test('should throw an exception with a message having rule_id and name in it', () => { + // @ts-expect-error intentionally invalid argument expect(() => getPrepackagedRules([{ name: 'rule name', rule_id: 'id-123' }])).toThrow( 'name: "rule name", rule_id: "id-123" within the folder rules/prepackaged_rules is not a valid detection engine rule. Expect the system to not work with pre-packaged rules until this rule is fixed or the file is removed. Error is: Invalid value "undefined" supplied to "description",Invalid value "undefined" supplied to "risk_score",Invalid value "undefined" supplied to "severity",Invalid value "undefined" supplied to "type",Invalid value "undefined" supplied to "version", Full rule contents are:\n{\n "name": "rule name",\n "rule_id": "id-123"\n}' ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts index 354f8b90fae23e..3d187d74432e87 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts @@ -15,6 +15,8 @@ import { AddPrepackagedRulesSchemaDecoded, } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; import { BadRequestError } from '../errors/bad_request_error'; + +// TODO: convert rules files to TS and add explicit type definitions import { rawRules } from './prepackaged_rules'; /** @@ -49,5 +51,7 @@ export const validateAllPrepackagedRules = ( }); }; -export const getPrepackagedRules = (rules = rawRules): AddPrepackagedRulesSchemaDecoded[] => - validateAllPrepackagedRules(rules); +export const getPrepackagedRules = ( + // @ts-expect-error mock data is too loosely typed + rules: AddPrepackagedRulesSchema[] = rawRules +): AddPrepackagedRulesSchemaDecoded[] => validateAllPrepackagedRules(rules); diff --git a/x-pack/plugins/transform/public/app/common/transform_stats.test.ts b/x-pack/plugins/transform/public/app/common/transform_stats.test.ts index 342ca77a67b386..c030a24ff5c3ca 100644 --- a/x-pack/plugins/transform/public/app/common/transform_stats.test.ts +++ b/x-pack/plugins/transform/public/app/common/transform_stats.test.ts @@ -12,11 +12,12 @@ import { getTransformProgress, isCompletedBatchTransform } from './transform_sta const getRow = (statsId: string) => { return { + // @ts-expect-error mock data does not actually match TransformListRow type ...(mockTransformListRow as TransformListRow), stats: { - ...mockTransformStats.transforms.find( - (stats: TransformListRow['stats']) => stats.id === statsId - ), + ...(mockTransformStats.transforms as Array).find( + (stats) => stats.id === statsId + )!, }, }; }; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx index 9c06675d69ca4d..87e890b1b9718b 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx @@ -17,6 +17,7 @@ jest.mock('../../../../../app/app_dependencies'); describe('Transform: Transform List Actions ', () => { test('Minimal initialization', () => { + // @ts-expect-error mock data is too loosely typed const item: TransformListRow = transformListRow; const props: StartActionNameProps = { forceDisable: false, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.test.tsx index 643e8c6126f0f7..dcc26593de44b8 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.test.tsx @@ -17,6 +17,7 @@ jest.mock('../../../../../app/app_dependencies'); describe('Transform: Transform List Actions ', () => { test('Minimal initialization', () => { + // @ts-expect-error mock data is too loosely typed const item: TransformListRow = transformListRow; const props: StopActionNameProps = { forceDisable: false, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts index f6708f7c36f267..9a6988445c73de 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts @@ -15,14 +15,15 @@ describe('Transform: isCompletedBatchTransform()', () => { // check the transform config/state against the conditions // that will be used by isCompletedBatchTransform() // followed by a call to isCompletedBatchTransform() itself + // @ts-expect-error mock data is too loosely typed const row = mockTransformListRow as TransformListRow; expect(row.stats.checkpointing.last.checkpoint === 1).toBe(true); expect(row.config.sync === undefined).toBe(true); expect(row.stats.state === TRANSFORM_STATE.STOPPED).toBe(true); - expect(isCompletedBatchTransform(mockTransformListRow)).toBe(true); + expect(isCompletedBatchTransform(row)).toBe(true); // adapt the mock config to resemble a non-completed transform. row.stats.checkpointing.last.checkpoint = 0; - expect(isCompletedBatchTransform(mockTransformListRow)).toBe(false); + expect(isCompletedBatchTransform(row)).toBe(false); }); }); diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx index 6d04c08f8b238b..2846e6a20494f6 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx @@ -31,6 +31,7 @@ describe('Transform: Transform List ', () => { test('Minimal initialization', async () => { const mlShared = await getMlSharedImports(); + // @ts-expect-error mock data is too loosely typed const item: TransformListRow = transformListRow; const { getByText, getByTestId } = render( diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.test.tsx index 9ba5441604ddc0..a4054bacba448c 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.test.tsx @@ -25,6 +25,7 @@ const defaultProps = { */ describe('CheckupTab', () => { test('render with deprecations', () => { + // @ts-expect-error mock data is too loosely typed expect(shallow()).toMatchSnapshot(); }); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts index f97a056d5cd36d..312a7275382b87 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts @@ -34,6 +34,7 @@ describe('getUpgradeAssistantStatus', () => { }); beforeEach(() => { + // @ts-expect-error mock data is too loosely typed deprecationsResponse = _.cloneDeep(fakeDeprecations); }); diff --git a/x-pack/test/api_integration/services/infraops_graphql_client.ts b/x-pack/test/api_integration/services/infraops_graphql_client.ts index 9eb26994315486..81f0ac8fad919a 100644 --- a/x-pack/test/api_integration/services/infraops_graphql_client.ts +++ b/x-pack/test/api_integration/services/infraops_graphql_client.ts @@ -47,6 +47,7 @@ export function InfraOpsGraphQLClientFactoryProvider({ getService }: FtrProvider return new ApolloClient({ cache: new InMemoryCache({ fragmentMatcher: new IntrospectionFragmentMatcher({ + // @ts-expect-error apollo-cache-inmemory types don't match actual introspection data introspectionQueryResultData, }), }), diff --git a/x-pack/test/api_integration/services/security_solution_graphql_client.ts b/x-pack/test/api_integration/services/security_solution_graphql_client.ts index 0bcf94fdb7d3fa..c592ced6234673 100644 --- a/x-pack/test/api_integration/services/security_solution_graphql_client.ts +++ b/x-pack/test/api_integration/services/security_solution_graphql_client.ts @@ -46,6 +46,7 @@ export function SecuritySolutionGraphQLClientFactoryProvider({ getService }: Ftr return new ApolloClient({ cache: new InMemoryCache({ fragmentMatcher: new IntrospectionFragmentMatcher({ + // @ts-expect-error apollo-cache-inmemory types don't match actual introspection data introspectionQueryResultData, }), }), diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index d4722aba4882ca..9a52aca381e87d 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -15,7 +15,6 @@ "plugins/licensing/**/*" ], "compilerOptions": { - "outDir": ".", "paths": { "kibana/public": ["src/core/public"], "kibana/server": ["src/core/server"], diff --git a/x-pack/typings/index.d.ts b/x-pack/typings/index.d.ts index 50c57c9e776b99..90e2fa5868744d 100644 --- a/x-pack/typings/index.d.ts +++ b/x-pack/typings/index.d.ts @@ -34,15 +34,6 @@ type Writable = { -readonly [K in keyof T]: T[K]; }; -// allow JSON files to be imported directly without lint errors -// see: https://github.com/palantir/tslint/issues/1264#issuecomment-228433367 -// and: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#arbitrary-expressions-are-forbidden-in-export-assignments-in-ambient-contexts -declare module '*.json' { - const json: any; - // eslint-disable-next-line import/no-default-export - export default json; -} - // Storybook references this module. It's @ts-ignored in the codebase but when // built into its dist it strips that out. Add it here to avoid a type checking // error. From 2344dcfae89d89bfbb9898a702a501a5a8720d34 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Wed, 30 Sep 2020 18:53:17 -0500 Subject: [PATCH 18/36] [ML] Migrate machine learning URLs to BrowserRouter format for APM, Security, and Infra (#78209) * [ML] Adds ability to pass multiple jobIds to job management url * [ML][APM] Update links to jobs management page for MLLink and LegacyJobsCallout * [ML][APM] Update useTimeSeriesExplorerHref * [ML][APM] Update tests * [ML][APM] Move test from useTimeSeriesExplorerHref to MLJobLink.test.tsx * [ML][Infra] Update ML links in infra to non-hash paths * [ML] Move MlUrlGenerator registration outside of licensing block for security solution * [ML][Security] Update ml links in security * [ML][APM] Update test snapshots * [ML][APM] Update snapshots * [ML][Security solution] Update tests * [ML] Update MLLink to include globalState * [ML] Update useTimeSeriesExplorerHref * [ML] Update apm and security_solution to use useMlHref hook * [ML] Update APM to use useUrlParams hook, update security solution hook * [ML] Update tests, fix duplicate imports * [ML] Update imports, remove ml exports to shared cause it's not needed [ML] Add import * [ML] Update snapshot * [ML] Fix warnings for jobs_table.test.tsx Co-authored-by: Elastic Machine --- .../app/Home/__snapshots__/Home.test.tsx.snap | 28 +++++- .../anomaly_detection/legacy_jobs_callout.tsx | 19 ++-- .../MachineLearningLinks/MLJobLink.test.tsx | 24 ++++- .../MachineLearningLinks/MLLink.test.tsx | 6 +- .../Links/MachineLearningLinks/MLLink.tsx | 47 ++++++---- .../useTimeSeriesExplorerHref.test.ts | 34 ------- .../useTimeSeriesExplorerHref.ts | 66 ++++++------- .../ApmPluginContext/MockApmPluginContext.tsx | 12 ++- x-pack/plugins/apm/public/plugin.ts | 3 + .../plugins/apm/public/utils/testHelpers.tsx | 5 +- .../analyze_in_ml_button.tsx | 4 +- .../public/hooks/use_link_props.test.tsx | 8 +- .../ml/common/types/ml_url_generator.ts | 2 +- .../job_filter_bar/job_filter_bar.js | 9 +- .../jobs/jobs_list/components/utils.d.ts | 1 + .../jobs/jobs_list/components/utils.js | 14 ++- x-pack/plugins/ml/public/index.ts | 1 + .../ml/public/ml_url_generator/index.ts | 2 + .../ml_url_generator/ml_url_generator.ts | 2 +- .../ml/public/ml_url_generator/use_ml_href.ts | 34 +++++++ x-pack/plugins/ml/public/plugin.ts | 27 ++++-- x-pack/plugins/ml/public/register_helper.ts | 1 - x-pack/plugins/security_solution/kibana.json | 2 +- .../ml_popover/jobs_table/jobs_table.test.tsx | 67 ++++++++----- .../ml_popover/jobs_table/jobs_table.tsx | 93 ++++++++++++------- .../common/lib/kibana/kibana_react.mock.ts | 7 ++ .../description_step/ml_job_description.tsx | 15 ++- .../plugins/security_solution/public/types.ts | 3 + 28 files changed, 350 insertions(+), 186 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts create mode 100644 x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts diff --git a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index 9706895b164a69..00be0b37a0e82a 100644 --- a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -34,6 +34,7 @@ exports[`Home component should render services 1`] = ` }, "http": Object { "basePath": Object { + "get": [Function], "prepend": [Function], }, }, @@ -51,7 +52,18 @@ exports[`Home component should render services 1`] = ` "get$": [Function], }, }, - "plugins": Object {}, + "plugins": Object { + "ml": Object { + "urlGenerator": MlUrlGenerator { + "createUrl": [Function], + "id": "ML_APP_URL_GENERATOR", + "params": Object { + "appBasePath": "/app/ml", + "useHash": false, + }, + }, + }, + }, } } > @@ -95,6 +107,7 @@ exports[`Home component should render traces 1`] = ` }, "http": Object { "basePath": Object { + "get": [Function], "prepend": [Function], }, }, @@ -112,7 +125,18 @@ exports[`Home component should render traces 1`] = ` "get$": [Function], }, }, - "plugins": Object {}, + "plugins": Object { + "ml": Object { + "urlGenerator": MlUrlGenerator { + "createUrl": [Function], + "id": "ML_APP_URL_GENERATOR", + "params": Object { + "appBasePath": "/app/ml", + "useHash": false, + }, + }, + }, + }, } } > diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx index 54053097ab02ee..1844e5754cfba5 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx @@ -8,9 +8,20 @@ import { EuiCallOut, EuiButton } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; +import { useMlHref } from '../../../../../../ml/public'; export function LegacyJobsCallout() { - const { core } = useApmPluginContext(); + const { + core, + plugins: { ml }, + } = useApmPluginContext(); + const mlADLink = useMlHref(ml, core.http.basePath.get(), { + page: 'jobs', + pageState: { + jobId: 'high_mean_response_time', + }, + }); + return ( - + {i18n.translate( 'xpack.apm.settings.anomaly_detection.legacy_jobs.button', { defaultMessage: 'Review jobs' } diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx index e6888c4cb60a27..01336a0e8f0ce4 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx @@ -22,7 +22,7 @@ describe('MLJobLink', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))"` + `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(mlTimeSeriesExplorer:(),zoom:(from:now%2Fw,to:now-4h))"` ); }); it('should produce the correct URL with jobId, serviceName, and transactionType', async () => { @@ -41,7 +41,27 @@ describe('MLJobLink', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request)))"` + `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request)),zoom:(from:now%2Fw,to:now-4h))"` + ); + }); + + it('correctly encodes time range values', async () => { + const href = await getRenderedHref( + () => ( + + ), + { + search: + '?rangeFrom=2020-07-29T17:27:29.000Z&rangeTo=2020-07-29T18:45:00.000Z&refreshInterval=10000&refreshPaused=true', + } as Location + ); + + expect(href).toMatchInlineSnapshot( + `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request)),zoom:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))"` ); }); }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx index 4eb886509805d8..be00364cab92e6 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx @@ -11,9 +11,7 @@ import { MLLink } from './MLLink'; test('MLLink produces the correct URL', async () => { const href = await getRenderedHref( - () => ( - - ), + () => , { search: '?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', @@ -21,6 +19,6 @@ test('MLLink produces the correct URL', async () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml#/some/path?_g=(ml:(jobIds:!(something)),refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))&mlManagement=(groupIds:!(apm))"` + `"/app/ml/jobs?mlManagement=(groupIds:!(apm),jobId:!(something))&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))"` ); }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx index 93ee9e0db48649..5fbcd475cb47ba 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx @@ -6,11 +6,9 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { useLocation } from 'react-router-dom'; -import rison, { RisonValue } from 'rison-node'; -import url from 'url'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; -import { getTimepickerRisonData, TimepickerRisonData } from '../rison_helpers'; +import { useMlHref, ML_PAGES } from '../../../../../../ml/public'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; interface MlRisonData { ml?: { @@ -26,28 +24,41 @@ interface Props { } export function MLLink({ children, path = '', query = {}, external }: Props) { - const { core } = useApmPluginContext(); - const location = useLocation(); + const { + core, + plugins: { ml }, + } = useApmPluginContext(); - const risonQuery: MlRisonData & TimepickerRisonData = getTimepickerRisonData( - location.search - ); - - if (query.ml) { - risonQuery.ml = query.ml; + let jobIds: string[] = []; + if (query.ml?.jobIds) { + jobIds = query.ml.jobIds; } + const { urlParams } = useUrlParams(); + const { rangeFrom, rangeTo, refreshInterval, refreshPaused } = urlParams; - const href = url.format({ - pathname: core.http.basePath.prepend('/app/ml'), - hash: `${path}?_g=${rison.encode( - risonQuery as RisonValue - )}&mlManagement=${rison.encode({ groupIds: ['apm'] })}`, + // default to link to ML Anomaly Detection jobs management page + const mlADLink = useMlHref(ml, core.http.basePath.get(), { + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + pageState: { + jobId: jobIds, + groupIds: ['apm'], + globalState: { + time: + rangeFrom !== undefined && rangeTo !== undefined + ? { from: rangeFrom, to: rangeTo } + : undefined, + refreshInterval: + refreshPaused !== undefined && refreshInterval !== undefined + ? { pause: refreshPaused, value: refreshInterval } + : undefined, + }, + }, }); return ( diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts deleted file mode 100644 index d84f55af993aa2..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { useTimeSeriesExplorerHref } from './useTimeSeriesExplorerHref'; - -jest.mock('../../../../hooks/useApmPluginContext', () => ({ - useApmPluginContext: () => ({ - core: { http: { basePath: { prepend: (url: string) => url } } }, - }), -})); - -jest.mock('react-router-dom', () => ({ - useLocation: () => ({ - search: - '?rangeFrom=2020-07-29T17:27:29.000Z&rangeTo=2020-07-29T18:45:00.000Z&refreshInterval=10000&refreshPaused=true', - }), -})); - -describe('useTimeSeriesExplorerHref', () => { - it('correctly encodes time range values', async () => { - const href = useTimeSeriesExplorerHref({ - jobId: 'apm-production-485b-high_mean_transaction_duration', - serviceName: 'opbeans-java', - transactionType: 'request', - }); - - expect(href).toMatchInlineSnapshot( - `"/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request)))"` - ); - }); -}); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts index 0cb87a4f515b6e..a758f266b44171 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import querystring from 'querystring'; -import { useLocation } from 'react-router-dom'; -import rison from 'rison-node'; -import url from 'url'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; -import { getTimepickerRisonData } from '../rison_helpers'; +import { useMlHref } from '../../../../../../ml/public'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; export function useTimeSeriesExplorerHref({ jobId, @@ -20,41 +17,38 @@ export function useTimeSeriesExplorerHref({ serviceName?: string; transactionType?: string; }) { - const { core } = useApmPluginContext(); - const location = useLocation(); - const { time, refreshInterval } = getTimepickerRisonData(location.search); + // default to link to ML Anomaly Detection jobs management page + const { + core, + plugins: { ml }, + } = useApmPluginContext(); + const { urlParams } = useUrlParams(); + const { rangeFrom, rangeTo, refreshInterval, refreshPaused } = urlParams; - const search = querystring.stringify( - { - _g: rison.encode({ - ml: { jobIds: [jobId] }, - time, - refreshInterval, - }), + const timeRange = + rangeFrom !== undefined && rangeTo !== undefined + ? { from: rangeFrom, to: rangeTo } + : undefined; + const mlAnomalyDetectionHref = useMlHref(ml, core.http.basePath.get(), { + page: 'timeseriesexplorer', + pageState: { + jobIds: [jobId], + timeRange, + refreshInterval: + refreshPaused !== undefined && refreshInterval !== undefined + ? { pause: refreshPaused, value: refreshInterval } + : undefined, + zoom: timeRange, ...(serviceName && transactionType ? { - _a: rison.encode({ - mlTimeSeriesExplorer: { - entities: { - 'service.name': serviceName, - 'transaction.type': transactionType, - }, - }, - }), + entities: { + 'service.name': serviceName, + 'transaction.type': transactionType, + }, } - : null), + : {}), }, - undefined, - undefined, - { - encodeURIComponent(str: string) { - return str; - }, - } - ); - - return url.format({ - pathname: core.http.basePath.prepend('/app/ml'), - hash: url.format({ pathname: '/timeseriesexplorer', search }), }); + + return mlAnomalyDetectionHref; } diff --git a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx index 48206572932b11..65f6dca179e711 100644 --- a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx +++ b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx @@ -9,6 +9,7 @@ import { ApmPluginContext, ApmPluginContextValue } from '.'; import { ConfigSchema } from '../..'; import { UI_SETTINGS } from '../../../../../../src/plugins/data/common'; import { createCallApmApi } from '../../services/rest/createCallApmApi'; +import { MlUrlGenerator } from '../../../../ml/public'; const uiSettings: Record = { [UI_SETTINGS.TIMEPICKER_QUICK_RANGES]: [ @@ -54,6 +55,7 @@ const mockCore = { http: { basePath: { prepend: (path: string) => `/basepath${path}`, + get: () => `/basepath`, }, }, i18n: { @@ -78,10 +80,18 @@ const mockConfig: ConfigSchema = { }, }; +const mockPlugin = { + ml: { + urlGenerator: new MlUrlGenerator({ + appBasePath: '/app/ml', + useHash: false, + }), + }, +}; export const mockApmPluginContextValue = { config: mockConfig, core: mockCore, - plugins: {}, + plugins: mockPlugin, }; export function MockApmPluginContextWrapper({ diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index dd9659a4cd1bea..d9709bbe461b36 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -36,12 +36,14 @@ import { featureCatalogueEntry } from './featureCatalogueEntry'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { registerApmAlerts } from './components/alerting/register_apm_alerts'; +import { MlPluginSetup, MlPluginStart } from '../../ml/public'; export type ApmPluginSetup = void; export type ApmPluginStart = void; export interface ApmPluginSetupDeps { alerts?: AlertingPluginPublicSetup; + ml?: MlPluginSetup; data: DataPublicPluginSetup; features: FeaturesPluginSetup; home?: HomePublicPluginSetup; @@ -52,6 +54,7 @@ export interface ApmPluginSetupDeps { export interface ApmPluginStartDeps { alerts?: AlertingPluginPublicStart; + ml?: MlPluginStart; data: DataPublicPluginStart; home: void; licensing: void; diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx index a69288f7bd4f99..971455fde39462 100644 --- a/x-pack/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -24,6 +24,7 @@ import { ESSearchRequest, } from '../../typings/elasticsearch'; import { MockApmPluginContextWrapper } from '../context/ApmPluginContext/MockApmPluginContext'; +import { UrlParamsProvider } from '../context/UrlParamsContext'; const originalConsoleWarn = console.warn; // eslint-disable-line no-console /** @@ -67,7 +68,9 @@ export async function getRenderedHref(Component: React.FC, location: Location) { const el = render( - + + + ); diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx index 3e54920160c53b..c2658765227678 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx @@ -58,7 +58,7 @@ export const getOverallAnomalyExplorerLinkDescriptor = ( return { app: 'ml', - hash: '/explorer', + pathname: '/explorer', search: { _g }, }; }; @@ -89,7 +89,7 @@ export const getEntitySpecificSingleMetricViewerLink = ( return { app: 'ml', - hash: '/timeseriesexplorer', + pathname: '/timeseriesexplorer', search: { _g, _a }, }; }; diff --git a/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx b/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx index d93cc44c456233..8c1647bd797982 100644 --- a/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx +++ b/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx @@ -129,7 +129,7 @@ describe('useLinkProps hook', () => { it('Provides the correct props with hash options', () => { const { result } = renderUseLinkPropsHook({ app: 'ml', - hash: '/explorer', + pathname: '/explorer', search: { type: 'host', id: 'some-id', @@ -137,7 +137,7 @@ describe('useLinkProps hook', () => { }, }); expect(result.current.href).toBe( - '/test-basepath/s/test-space/app/ml#/explorer?type=host&id=some-id&count=12345' + '/test-basepath/s/test-space/app/ml/explorer?type=host&id=some-id&count=12345' ); expect(result.current.onClick).toBeDefined(); }); @@ -145,7 +145,7 @@ describe('useLinkProps hook', () => { it('Provides the correct props with more complex encoding', () => { const { result } = renderUseLinkPropsHook({ app: 'ml', - hash: '/explorer', + pathname: '/explorer', search: { type: 'host + host', name: 'this name has spaces and ** and %', @@ -155,7 +155,7 @@ describe('useLinkProps hook', () => { }, }); expect(result.current.href).toBe( - '/test-basepath/s/test-space/app/ml#/explorer?type=host%20%2B%20host&name=this%20name%20has%20spaces%20and%20**%20and%20%25&id=some-id&count=12345&animals=dog,cat,bear' + '/test-basepath/s/test-space/app/ml/explorer?type=host%20%2B%20host&name=this%20name%20has%20spaces%20and%20**%20and%20%25&id=some-id&count=12345&animals=dog,cat,bear' ); expect(result.current.onClick).toBeDefined(); }); diff --git a/x-pack/plugins/ml/common/types/ml_url_generator.ts b/x-pack/plugins/ml/common/types/ml_url_generator.ts index 95d06e62f9ef00..aa38fb2ec6fbbe 100644 --- a/x-pack/plugins/ml/common/types/ml_url_generator.ts +++ b/x-pack/plugins/ml/common/types/ml_url_generator.ts @@ -55,7 +55,7 @@ export type MlGenericUrlState = MLPageState< >; export interface AnomalyDetectionQueryState { - jobId?: JobId; + jobId?: JobId | string[]; groupIds?: string[]; globalState?: MlCommonGlobalState; } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js index 6eb7b00e5620c6..08373542c12346 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js @@ -9,7 +9,12 @@ import React, { Component, Fragment } from 'react'; import { ml } from '../../../../services/ml_api_service'; import { JobGroup } from '../job_group'; -import { getGroupQueryText, getSelectedIdFromUrl, clearSelectedJobIdFromUrl } from '../utils'; +import { + getGroupQueryText, + getSelectedIdFromUrl, + clearSelectedJobIdFromUrl, + getJobQueryText, +} from '../utils'; import { EuiSearchBar, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -60,7 +65,7 @@ export class JobFilterBar extends Component { if (groupIds !== undefined) { defaultQueryText = getGroupQueryText(groupIds); } else if (jobId !== undefined) { - defaultQueryText = jobId; + defaultQueryText = getJobQueryText(jobId); } if (defaultQueryText !== undefined) { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts index cf4fad9513de51..75d6b149fda08b 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts @@ -6,4 +6,5 @@ export function getSelectedIdFromUrl(str: string): { groupIds?: string[]; jobId?: string }; export function getGroupQueryText(arr: string[]): string; +export function getJobQueryText(arr: string | string[]): string; export function clearSelectedJobIdFromUrl(str: string): void; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index fd0789c9bc1033..c1f6d75637ed41 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -309,8 +309,13 @@ export function filterJobs(jobs, clauses) { } else { // filter other clauses, i.e. the toggle group buttons if (Array.isArray(c.value)) { - // the groups value is an array of group ids - js = jobs.filter((job) => jobProperty(job, c.field).some((g) => c.value.indexOf(g) >= 0)); + // if it's an array of job ids + if (c.field === 'id') { + js = jobs.filter((job) => c.value.indexOf(jobProperty(job, c.field)) >= 0); + } else { + // the groups value is an array of group ids + js = jobs.filter((job) => jobProperty(job, c.field).some((g) => c.value.indexOf(g) >= 0)); + } } else { js = jobs.filter((job) => jobProperty(job, c.field) === c.value); } @@ -353,6 +358,7 @@ function jobProperty(job, prop) { job_state: 'jobState', datafeed_state: 'datafeedState', groups: 'groups', + id: 'id', }; return job[propMap[prop]]; } @@ -389,6 +395,10 @@ export function getGroupQueryText(groupIds) { return `groups:(${groupIds.join(' or ')})`; } +export function getJobQueryText(jobIds) { + return Array.isArray(jobIds) ? `id:(${jobIds.join(' OR ')})` : jobIds; +} + export function clearSelectedJobIdFromUrl(url) { if (typeof url === 'string') { url = decodeURIComponent(url); diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index c43df1e1a3d2c6..81e10588a3845b 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -41,6 +41,7 @@ export type { // Static exports export { getSeverityColor, getSeverityType } from '../common/util/anomaly_utils'; export { ANOMALY_SEVERITY } from '../common'; +export { useMlHref, ML_PAGES, MlUrlGenerator } from './ml_url_generator'; // Bundled shared exports // Exported this way so the code doesn't end up in ML's page load bundle diff --git a/x-pack/plugins/ml/public/ml_url_generator/index.ts b/x-pack/plugins/ml/public/ml_url_generator/index.ts index 1579b187278c4f..bd9e58654828b6 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/index.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/index.ts @@ -4,3 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ export { MlUrlGenerator, registerUrlGenerator } from './ml_url_generator'; +export { useMlHref } from './use_ml_href'; +export { ML_PAGES } from '../../common/constants/ml_url_generator'; diff --git a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts index 704135f5546b13..351e366d1f1d89 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts @@ -106,7 +106,7 @@ export function registerUrlGenerator( core: CoreSetup ) { const baseUrl = core.http.basePath.prepend('/app/ml'); - share.urlGenerators.registerUrlGenerator( + return share.urlGenerators.registerUrlGenerator( new MlUrlGenerator({ appBasePath: baseUrl, useHash: core.uiSettings.get('state:storeInSessionStorage'), diff --git a/x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts b/x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts new file mode 100644 index 00000000000000..8e5a6ef64e59fe --- /dev/null +++ b/x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; +import { MlPluginStart } from '../index'; +import { MlUrlGeneratorState } from '../../common/types/ml_url_generator'; +export const useMlHref = ( + ml: MlPluginStart | undefined, + basePath: string, + params: MlUrlGeneratorState +) => { + const [mlLink, setMlLink] = useState(`${basePath}/app/ml/${params.page}`); + + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl(params); + if (!isCancelled) { + setMlLink(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator, params]); + + return mlLink; +}; diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 034ed090e2212d..1f98de380312af 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -16,7 +16,11 @@ import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; import type { ManagementSetup } from 'src/plugins/management/public'; -import type { SharePluginSetup, SharePluginStart } from 'src/plugins/share/public'; +import type { + SharePluginSetup, + SharePluginStart, + UrlGeneratorContract, +} from 'src/plugins/share/public'; import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import type { DataPublicPluginStart } from 'src/plugins/data/public'; import type { HomePublicPluginSetup } from 'src/plugins/home/public'; @@ -34,6 +38,8 @@ import type { SecurityPluginSetup } from '../../security/public'; import { PLUGIN_ICON_SOLUTION, PLUGIN_ID } from '../common/constants/app'; import { setDependencyCache } from './application/util/dependency_cache'; +import { ML_APP_URL_GENERATOR } from '../common/constants/ml_url_generator'; +import { registerUrlGenerator } from './ml_url_generator'; export interface MlStartDependencies { data: DataPublicPluginStart; @@ -59,6 +65,7 @@ export type MlCoreSetup = CoreSetup; export class MlPlugin implements Plugin { private appUpdater = new BehaviorSubject(() => ({})); + private urlGenerator: undefined | UrlGeneratorContract; constructor(private initializerContext: PluginInitializerContext) {} @@ -98,6 +105,10 @@ export class MlPlugin implements Plugin { }, }); + if (pluginsSetup.share) { + this.urlGenerator = registerUrlGenerator(pluginsSetup.share, core); + } + const licensing = pluginsSetup.licensing.license$.pipe(take(1)); licensing.subscribe(async (license) => { const [coreStart] = await core.getStartServices(); @@ -109,7 +120,6 @@ export class MlPlugin implements Plugin { registerFeature, registerManagementSection, registerMlUiActions, - registerUrlGenerator, MlCardState, } = await import('./register_helper'); @@ -118,11 +128,6 @@ export class MlPlugin implements Plugin { if (pluginsSetup.home) { registerFeature(pluginsSetup.home); } - - // the mlUrlGenerator should be registered even without full license - // for other plugins to access ML links - registerUrlGenerator(pluginsSetup.share, core); - const { capabilities } = coreStart.application; // register ML for the index pattern management no data screen. @@ -149,7 +154,9 @@ export class MlPlugin implements Plugin { } }); - return {}; + return { + urlGenerator: this.urlGenerator, + }; } start(core: CoreStart, deps: any) { @@ -159,7 +166,9 @@ export class MlPlugin implements Plugin { http: core.http, i18n: core.i18n, }); - return {}; + return { + urlGenerator: this.urlGenerator, + }; } public stop() {} diff --git a/x-pack/plugins/ml/public/register_helper.ts b/x-pack/plugins/ml/public/register_helper.ts index 97574e296d1eb8..b24ec443637759 100644 --- a/x-pack/plugins/ml/public/register_helper.ts +++ b/x-pack/plugins/ml/public/register_helper.ts @@ -12,4 +12,3 @@ export { registerEmbeddables } from './embeddables'; export { registerFeature } from './register_feature'; export { registerManagementSection } from './application/management'; export { registerMlUiActions } from './ui_actions'; -export { registerUrlGenerator } from './ml_url_generator'; diff --git a/x-pack/plugins/security_solution/kibana.json b/x-pack/plugins/security_solution/kibana.json index 7bd76838c7559b..3b566559abfcdf 100644 --- a/x-pack/plugins/security_solution/kibana.json +++ b/x-pack/plugins/security_solution/kibana.json @@ -32,5 +32,5 @@ ], "server": true, "ui": true, - "requiredBundles": ["esUiShared", "ingestManager", "kibanaUtils", "kibanaReact", "lists"] + "requiredBundles": ["esUiShared", "ingestManager", "kibanaUtils", "kibanaReact", "lists", "ml"] } diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx index 3d7e47a15fc1ef..156475f63aa65f 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx @@ -6,7 +6,7 @@ import { shallow, mount } from 'enzyme'; import React from 'react'; -import { waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { JobsTableComponent } from './jobs_table'; import { mockSecurityJobs } from '../api.mock'; import { cloneDeep } from 'lodash/fp'; @@ -14,9 +14,19 @@ import { SecurityJob } from '../types'; jest.mock('../../../lib/kibana'); +export async function getRenderedHref(Component: React.FC, selector: string) { + const el = render(); + + await waitFor(() => el.container.querySelector(selector)); + + const a = el.container.querySelector(selector); + return a?.getAttribute('href') ?? ''; +} + describe('JobsTableComponent', () => { let securityJobs: SecurityJob[]; let onJobStateChangeMock = jest.fn(); + beforeEach(() => { securityJobs = cloneDeep(mockSecurityJobs); onJobStateChangeMock = jest.fn(); @@ -33,30 +43,36 @@ describe('JobsTableComponent', () => { expect(wrapper).toMatchSnapshot(); }); - test('should render the hyperlink which points specifically to the job id', () => { - const wrapper = mount( - + test('should render the hyperlink which points specifically to the job id', async () => { + const href = await getRenderedHref( + () => ( + + ), + '[data-test-subj="jobs-table-link"]' ); - expect(wrapper.find('[data-test-subj="jobs-table-link"]').first().props().href).toEqual( - '/test/base/path/app/ml#/jobs?mlManagement=(jobId:linux_anomalous_network_activity_ecs)' + await waitFor(() => + expect(href).toEqual('/app/ml/jobs?mlManagement=(jobId:linux_anomalous_network_activity_ecs)') ); }); - test('should render the hyperlink with URI encodings which points specifically to the job id', () => { + test('should render the hyperlink with URI encodings which points specifically to the job id', async () => { securityJobs[0].id = 'job id with spaces'; - const wrapper = mount( - + const href = await getRenderedHref( + () => ( + + ), + '[data-test-subj="jobs-table-link"]' ); - expect(wrapper.find('[data-test-subj="jobs-table-link"]').first().props().href).toEqual( - '/test/base/path/app/ml#/jobs?mlManagement=(jobId:job%20id%20with%20spaces)' + await waitFor(() => + expect(href).toEqual("/app/ml/jobs?mlManagement=(jobId:'job%20id%20with%20spaces')") ); }); @@ -68,6 +84,7 @@ describe('JobsTableComponent', () => { onJobStateChange={onJobStateChangeMock} /> ); + wrapper .find('button[data-test-subj="job-switch"]') .first() @@ -79,7 +96,7 @@ describe('JobsTableComponent', () => { }); }); - test('should have a switch when it is not in the loading state', () => { + test('should have a switch when it is not in the loading state', async () => { const wrapper = mount( { onJobStateChange={onJobStateChangeMock} /> ); - expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(true); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(true); + }); }); - test('should not have a switch when it is in the loading state', () => { + test('should not have a switch when it is in the loading state', async () => { const wrapper = mount( { onJobStateChange={onJobStateChangeMock} /> ); - expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(false); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx index 1e9e689dcd6ff2..5e3045efe1f4d5 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx @@ -22,10 +22,11 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; -import { useBasePath } from '../../../lib/kibana'; +import { useBasePath, useKibana } from '../../../lib/kibana'; import * as i18n from './translations'; import { JobSwitch } from './job_switch'; import { SecurityJob } from '../types'; +import { useMlHref, ML_PAGES } from '../../../../../../ml/public'; const JobNameWrapper = styled.div` margin: 5px 0; @@ -36,6 +37,37 @@ JobNameWrapper.displayName = 'JobNameWrapper'; // TODO: Use SASS mixin @include EuiTextTruncate when we switch from styled components const truncateThreshold = 200; +interface JobNameProps { + id: string; + description: string; + basePath: string; +} + +const JobName = ({ id, description, basePath }: JobNameProps) => { + const { + services: { ml }, + } = useKibana(); + + const jobUrl = useMlHref(ml, basePath, { + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + pageState: { + jobId: id, + }, + }); + + return ( + + + {id} + + + {description.length > truncateThreshold + ? `${description.substring(0, truncateThreshold)}...` + : description} + + + ); +}; const getJobsTableColumns = ( isLoading: boolean, onJobStateChange: (job: SecurityJob, latestTimestampMs: number, enable: boolean) => Promise, @@ -44,20 +76,7 @@ const getJobsTableColumns = ( { name: i18n.COLUMN_JOB_NAME, render: ({ id, description }: SecurityJob) => ( - - - {id} - - - {description.length > truncateThreshold - ? `${description.substring(0, truncateThreshold)}...` - : description} - - + ), }, { @@ -141,22 +160,32 @@ export const JobsTable = React.memo(JobsTableComponent); JobsTable.displayName = 'JobsTable'; -export const NoItemsMessage = React.memo(({ basePath }: { basePath: string }) => ( - {i18n.NO_ITEMS_TEXT}} - titleSize="xs" - actions={ - - {i18n.CREATE_CUSTOM_JOB} - - } - /> -)); +export const NoItemsMessage = React.memo(({ basePath }: { basePath: string }) => { + const { + services: { ml }, + } = useKibana(); + + const createNewAnomalyDetectionJoUrl = useMlHref(ml, basePath, { + page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX, + }); + + return ( + {i18n.NO_ITEMS_TEXT}} + titleSize="xs" + actions={ + + {i18n.CREATE_CUSTOM_JOB} + + } + /> + ); +}); NoItemsMessage.displayName = 'NoItemsMessage'; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index 6f8ff2e1bb21a2..06c152b94cfd82 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -30,6 +30,7 @@ import { } from '../../../../common/constants'; import { StartServices } from '../../../types'; import { createSecuritySolutionStorageMock } from '../../mock/mock_local_storage'; +import { MlUrlGenerator } from '../../../../../ml/public'; const mockUiSettings: Record = { [DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' }, @@ -113,6 +114,12 @@ export const createStartServicesMock = (): StartServices => { }, security, storage, + ml: { + urlGenerator: new MlUrlGenerator({ + appBasePath: '/app/ml', + useHash: false, + }), + }, } as unknown) as StartServices; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx index 414f6f2c2d3bb9..e25da49cf7e62d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx @@ -8,7 +8,7 @@ import React from 'react'; import styled from 'styled-components'; import { EuiBadge, EuiIcon, EuiLink, EuiToolTip } from '@elastic/eui'; -import { MlSummaryJob } from '../../../../../../ml/public'; +import { ML_PAGES, MlSummaryJob, useMlHref } from '../../../../../../ml/public'; import { isJobStarted } from '../../../../../common/machine_learning/helpers'; import { useSecurityJobs } from '../../../../common/components/ml_popover/hooks/use_security_jobs'; import { useKibana } from '../../../../common/lib/kibana'; @@ -72,9 +72,16 @@ const Wrapper = styled.div` const MlJobDescriptionComponent: React.FC<{ jobId: string }> = ({ jobId }) => { const { jobs } = useSecurityJobs(false); - const jobUrl = useKibana().services.application.getUrlForApp( - `ml#/jobs?mlManagement=(jobId:${encodeURI(jobId)})` - ); + const { + services: { http, ml }, + } = useKibana(); + const jobUrl = useMlHref(ml, http.basePath.get(), { + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + pageState: { + jobId: [jobId], + }, + }); + const job = jobs.find(({ id }) => id === jobId); const jobIdSpan = {jobId}; diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 62069484dd8bda..ef40d34104b723 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -23,12 +23,14 @@ import { SecurityPluginSetup } from '../../security/public'; import { AppFrontendLibs } from './common/lib/lib'; import { ResolverPluginSetup } from './resolver/types'; import { Inspect } from '../common/search_strategy'; +import { MlPluginSetup, MlPluginStart } from '../../ml/public'; export interface SetupPlugins { home?: HomePublicPluginSetup; security: SecurityPluginSetup; triggers_actions_ui: TriggersActionsSetup; usageCollection?: UsageCollectionSetup; + ml?: MlPluginSetup; } export interface StartPlugins { @@ -40,6 +42,7 @@ export interface StartPlugins { newsfeed?: NewsfeedStart; triggers_actions_ui: TriggersActionsStart; uiActions: UiActionsStart; + ml?: MlPluginStart; } export type StartServices = CoreStart & From 5aa3fb5e0634c84ae3521de6cad663585d7b01ae Mon Sep 17 00:00:00 2001 From: Tre Date: Wed, 30 Sep 2020 19:04:22 -0600 Subject: [PATCH 19/36] [QA][Code Coverage] Team Assignment Docs Update (#78890) [QA][Code Coverage] Team Assignment Docs Update (#78890) --- .../docs/team_assignment/README.md | 43 ++++++++++++++++-- .../team_assignment/security_privleges.png | Bin 231895 -> 0 bytes 2 files changed, 38 insertions(+), 5 deletions(-) delete mode 100644 src/dev/code_coverage/docs/team_assignment/security_privleges.png diff --git a/src/dev/code_coverage/docs/team_assignment/README.md b/src/dev/code_coverage/docs/team_assignment/README.md index 3509fb5c2a4fc1..f2884fb42b8d75 100644 --- a/src/dev/code_coverage/docs/team_assignment/README.md +++ b/src/dev/code_coverage/docs/team_assignment/README.md @@ -1,8 +1,41 @@ -# Team Assignment Ingestion Pipeline +# Code Coverage Team Assignments -Team assignment will occur once per ci run. -Team assignment uses an ingest pipeline. +Team assignment occurs once per ci run. -The coverage user has the coverage admin role. +The "orchestration" entry point is a [Jenkinsfile Scripted Pipeline](https://github.com/elastic/kibana/blob/f73bc48b3bbbb5ad2042c1aa267aea2150b7b742/.ci/Jenkinsfile_coverage#L21) +This Jenkinsfile runs a [shell script](https://github.com/elastic/kibana/blob/master/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh#L33) that kicks everything off. +The end result is the data is ingested to our [Kibana Stats Cluster](https://kibana-stats.elastic.dev/app/dashboards#/view/58b8db70-62f9-11ea-8312-7f2d69b79843?_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-7d%2Cto%3Anow))) -This role must have the rights depicted below ![Cluster Rights](./security_privleges.png) +## Team Assignment Parsing (from .github/CODEOWNERS) +We add additional metadata to the CODEOWNERS file. +This metadata allows users to assign teams to paths, in a friendly location. +Example CODEOWNERS Block: +_notice the coverage delimiter `#CC# ...`_ +``` +/x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team @elastic/siem +/x-pack/test/plugin_functional/plugins/resolver_test/ @elastic/endpoint-app-team @elastic/siem +/x-pack/test/plugin_functional/test_suites/resolver/ @elastic/endpoint-app-team @elastic/siem +#CC# /x-pack/legacy/plugins/siem/ @elastic/siem +#CC# /x-pack/plugins/siem/ @elastic/siem +#CC# /x-pack/plugins/security_solution/ @elastic/siem +``` +The first 3 lines above fill the usual purpose of the CODEOWNERS file and cause PRs modifying files in these paths to require approval by the listed team(s). +They also attribute files in those paths for purpose of code coverage reporting. +The last 3 lines above ONLY attribute files in those paths for purpose of code coverage reporting. + +## Team Assignment Data File Creation (Before Ingestion) +We create a data file containing all paths in the repo, with a team assigned. +Example Team Assignments Block: +``` +x-pack/plugins/security_solution/common/constants.ts siem +x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.test.ts siem +x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.ts siem +... +``` + +## Team Assignment Data File Usage (During Code Coverage Ingestion) +Subsequently, we use the data file during ingestion. +We search the data file, for any given "coveredFilePath" + - Given the above assignments block, and lets say the "coveredFilePath" during ingestion is + - `x-pack/plugins/security_solution/common/constants.ts` + - The team assignment would be `siem` in our [Kibana Stats Cluster](https://kibana-stats.elastic.dev/app/dashboards#/view/58b8db70-62f9-11ea-8312-7f2d69b79843?_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-7d%2Cto%3Anow))) diff --git a/src/dev/code_coverage/docs/team_assignment/security_privleges.png b/src/dev/code_coverage/docs/team_assignment/security_privleges.png deleted file mode 100644 index 002774f3ecefaebad90c086026e518df958adcb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231895 zcmd?QcQjn>-##jhAVeZa^b|xFz1N6dVi+-6L>;|%MuJF0uhEGz+8~VHl8D}oUPkZC zh&Gtu>^$##-uHLbS<>xvC z1jG;mf@>8w34tx`BgPj51SG^z1qE#t1qEhpS0^i|y(IyG^2g95FJ0XU>W;x?6rIEC zjbd&G|(k#NY#ULmE8Vb1>fT26Pg z`$pX7mF6RKX7$7=A^>-akzT)e#zgRh=7qoit-for_C|MSP-xX28D?z~9BB{%H3b0^ z#cMvwW$w8-V}iS|vdajZxw6Pz+^Bx_#lh+2EfcWKBT9lU1~YMi%#4t|D#Ff-UHO zl$=@h-6SMk3=A3j@g@#M5m-A-N!LhL6u&|NCOJ*BI*~b4xEQhK`*FE6`PpKPX&ush z$JHi1nx-MU8^sU>%Rhw%W3$%5_n4pJzB#gl-eAi6gDef_r z%2I>`Kc%3$`~Jy8A7!eMw;M4JEJ*8RGqnls-3nE3(dJLMvKzXeEnj%AIAkk_DZ6(_ zXoFhtmQ+YVw$BFHA@Pgv7N41@!>C?sd?q*iF;F4W^ng5!a=~4cPWpQCH@!WBI;s}J z`L{}oDkm&WAy=Q?4Syf^q9}Lcsa*mc2L;vpH@TiUt}ojQIdmzDuZ@K@grz>`&X)f2 z)rJE?f`}59lgJJjQg^@VOC!!?9A2hS`^9e;pIv-*R_9

7_|cBb>f@Eh_C*olsr9EtmOP-T86F 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≤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�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

PZ$%ePVjX}vuHA#gG05D2Q+pzF3yAjl%p!@^-S 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 KQ_lUz`+op6`cJ+9 From 0776bf7a3ebb6f5639b32a145887d22f31393f9e Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 1 Oct 2020 07:40:27 +0200 Subject: [PATCH 20/36] [UX] Improve page responsive (#78759) * WIP * add resizeable panel * Improve page responsiveness * Improve page responsiveness * fix types Co-authored-by: Elastic Machine --- .../RumDashboard/Charts/PageLoadDistChart.tsx | 5 +- .../RumDashboard/Charts/PageViewsChart.tsx | 2 +- .../Charts/VisitorBreakdownChart.tsx | 7 ++- .../app/RumDashboard/CoreVitals/index.tsx | 8 +-- .../ImpactfulMetrics/JSErrors.tsx | 10 ++-- .../PercentileAnnotations.tsx | 12 ++-- .../PageLoadDistribution/index.tsx | 3 +- .../app/RumDashboard/PageViewsTrend/index.tsx | 3 +- .../RumDashboard/Panels/PageLoadAndViews.tsx | 39 +++++++++++++ .../RumDashboard/Panels/VisitorBreakdowns.tsx | 39 +++++++++++++ .../app/RumDashboard/RumDashboard.tsx | 55 +++++++++---------- .../components/app/RumDashboard/RumHome.tsx | 32 +++++------ .../RumDashboard/UXMetrics/KeyUXMetrics.tsx | 2 +- .../app/RumDashboard/UXMetrics/index.tsx | 4 +- .../RumDashboard/VisitorBreakdown/index.tsx | 2 +- .../app/RumDashboard/hooks/useBreakPoints.ts | 38 +++++++++++++ .../app/RumDashboard/translations.ts | 3 + .../lib/rum_client/get_visitor_breakdown.ts | 20 ++++--- 18 files changed, 203 insertions(+), 81 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx create mode 100644 x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx create mode 100644 x-pack/plugins/apm/public/components/app/RumDashboard/hooks/useBreakPoints.ts diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx index 79cd1c5753ae50..4a5f43dacedf4c 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx @@ -89,7 +89,10 @@ export function PageLoadDistChart({ const [darkMode] = useUiSetting$('theme:darkMode'); return ( - + {(!loading || data) && ( + {(!loading || data) && ( + d.count as number} valueGetter="percent" percentFormatter={(d: number) => diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx index cd7fd0af6d6838..fcc7b214943ffa 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx @@ -25,8 +25,8 @@ export function CoreVitals({ data, loading }: Props) { const { lcp, lcpRanks, fid, fidRanks, cls, clsRanks } = data || {}; return ( - - + + - + - + 0 + ? ((data?.totalErrorPages ?? 0) / totalPageViews) * 100 + : 0; + return ( <> @@ -109,10 +114,7 @@ export function JSErrors() { title={i18n.translate('xpack.apm.rum.jsErrors.errorRateValue', { defaultMessage: '{errorRate} %', values: { - errorRate: ( - ((data?.totalErrorPages ?? 0) / totalPageViews) * - 100 - ).toFixed(0), + errorRate: errorRate.toFixed(0), }, })} description={I18LABELS.errorRate} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/PercentileAnnotations.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/PercentileAnnotations.tsx index 7e81dc011bdb5b..2abbcb8239aa85 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/PercentileAnnotations.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/PercentileAnnotations.tsx @@ -10,9 +10,9 @@ import { LineAnnotation, LineAnnotationDatum, LineAnnotationStyle, + Position, } from '@elastic/charts'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import styled from 'styled-components'; import { EuiToolTip } from '@elastic/eui'; interface Props { @@ -28,11 +28,6 @@ function generateAnnotationData( })); } -const PercentileMarker = styled.span` - position: relative; - bottom: 205px; -`; - export function PercentileAnnotations({ percentiles }: Props) { const dataValues = generateAnnotationData(percentiles) ?? []; @@ -66,8 +61,9 @@ export function PercentileAnnotations({ percentiles }: Props) { dataValues={[annotation]} style={style} hideTooltips={true} + markerPosition={Position.Top} marker={ - + } content={ @@ -76,7 +72,7 @@ export function PercentileAnnotations({ percentiles }: Props) { > <>{annotation.details}th - + } /> ))} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx index 88d14a0213a962..45a40712f90fb0 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx @@ -13,6 +13,7 @@ import { BreakdownFilter } from '../Breakdowns/BreakdownFilter'; import { PageLoadDistChart } from '../Charts/PageLoadDistChart'; import { BreakdownItem } from '../../../../../typings/ui_filters'; import { ResetPercentileZoom } from './ResetPercentileZoom'; +import { FULL_HEIGHT } from '../RumDashboard'; export interface PercentileRange { min?: number | null; @@ -71,7 +72,7 @@ export function PageLoadDistribution() { }; return ( -

+
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx index 621098b6028cba..7492096b938984 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx @@ -12,6 +12,7 @@ import { I18LABELS } from '../translations'; import { BreakdownFilter } from '../Breakdowns/BreakdownFilter'; import { PageViewsChart } from '../Charts/PageViewsChart'; import { BreakdownItem } from '../../../../../typings/ui_filters'; +import { FULL_HEIGHT } from '../RumDashboard'; export function PageViewsTrend() { const { urlParams, uiFilters } = useUrlParams(); @@ -48,7 +49,7 @@ export function PageViewsTrend() { ); return ( -
+
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx new file mode 100644 index 00000000000000..cdc52c98de971c --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiPanel, EuiResizableContainer } from '@elastic/eui'; +import { FULL_HEIGHT } from '../RumDashboard'; +import { PageLoadDistribution } from '../PageLoadDistribution'; +import { PageViewsTrend } from '../PageViewsTrend'; +import { useBreakPoints } from '../hooks/useBreakPoints'; + +export function PageLoadAndViews() { + const { isLarge } = useBreakPoints(); + + return ( + + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + + + + + + + + + + + + )} + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx new file mode 100644 index 00000000000000..87ffacbf56f96b --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiPanel, EuiResizableContainer } from '@elastic/eui'; +import { VisitorBreakdown } from '../VisitorBreakdown'; +import { VisitorBreakdownMap } from '../VisitorBreakdownMap'; +import { FULL_HEIGHT } from '../RumDashboard'; +import { useBreakPoints } from '../hooks/useBreakPoints'; + +export function VisitorBreakdownsPanel() { + const { isLarge } = useBreakPoints(); + + return ( + + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + + + + + + + + + + + + )} + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx index 37522b06970c11..0004599b1821b4 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx @@ -10,20 +10,24 @@ import { EuiTitle, EuiSpacer, EuiPanel, + EuiResizableContainer, } from '@elastic/eui'; import React from 'react'; import { ClientMetrics } from './ClientMetrics'; -import { PageViewsTrend } from './PageViewsTrend'; -import { PageLoadDistribution } from './PageLoadDistribution'; import { I18LABELS } from './translations'; -import { VisitorBreakdown } from './VisitorBreakdown'; import { UXMetrics } from './UXMetrics'; -import { VisitorBreakdownMap } from './VisitorBreakdownMap'; import { ImpactfulMetrics } from './ImpactfulMetrics'; +import { PageLoadAndViews } from './Panels/PageLoadAndViews'; +import { VisitorBreakdownsPanel } from './Panels/VisitorBreakdowns'; +import { useBreakPoints } from './hooks/useBreakPoints'; + +export const FULL_HEIGHT = { height: '100%' }; export function RumDashboard() { + const { isLarge, isSmall } = useBreakPoints(); + return ( - + @@ -41,31 +45,22 @@ export function RumDashboard() { - - - - - - - - - - - - - - - - - - - - - - - - - + + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + + + + + + + + )} + diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx index 71a992ae4df82b..f30f9ba5af257f 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx @@ -18,22 +18,20 @@ export const UX_LABEL = i18n.translate('xpack.apm.ux.title', { export function RumHome() { return ( -
- - - - - -

{UX_LABEL}

-
-
- - - -
-
- -
-
+ + + + + +

{UX_LABEL}

+
+
+ + + +
+
+ +
); } diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx index 37836a2c47d642..53722658cafefc 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx @@ -57,7 +57,7 @@ export function KeyUXMetrics({ data, loading }: Props) { // Note: FCP value is in ms unit return ( - + - +

{I18LABELS.userExperienceMetrics}

@@ -70,7 +70,7 @@ export function UXMetrics() {
- +

diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdown/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdown/index.tsx index 092c416303bb5d..67127f9c2fd81b 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdown/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdown/index.tsx @@ -44,7 +44,7 @@ export function VisitorBreakdown() {

{VisitorBreakdownLabel}

- +

{I18LABELS.browser}

diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/hooks/useBreakPoints.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/hooks/useBreakPoints.ts new file mode 100644 index 00000000000000..ea7b155045fdc3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/hooks/useBreakPoints.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useState } from 'react'; +import useWindowSize from 'react-use/lib/useWindowSize'; +import useDebounce from 'react-use/lib/useDebounce'; +import { isWithinMaxBreakpoint } from '@elastic/eui'; + +export function useBreakPoints() { + const [screenSizes, setScreenSizes] = useState({ + isSmall: false, + isMedium: false, + isLarge: false, + isXl: false, + }); + + const { width } = useWindowSize(); + + useDebounce( + () => { + const windowWidth = window.innerWidth; + + setScreenSizes({ + isSmall: isWithinMaxBreakpoint(windowWidth, 's'), + isMedium: isWithinMaxBreakpoint(windowWidth, 'm'), + isLarge: isWithinMaxBreakpoint(windowWidth, 'l'), + isXl: isWithinMaxBreakpoint(windowWidth, 'xl'), + }); + }, + 50, + [width] + ); + + return screenSizes; +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts index dd7721d9f7129a..fd118096526d7b 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts @@ -150,6 +150,9 @@ export const I18LABELS = { percentile99th: i18n.translate('xpack.apm.ux.percentile.99th', { defaultMessage: '99th', }), + noData: i18n.translate('xpack.apm.ux.visitorBreakdown.noData', { + defaultMessage: 'No data.', + }), }; export const VisitorBreakdownLabel = i18n.translate( diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts index 7345d6acc0f82b..52d089e4e29c99 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts @@ -74,20 +74,24 @@ export async function getVisitorBreakdown({ name: bucket.key as string, })); - browserItems.push({ - count: totalItems - browserTotal, - name: 'Others', - }); + if (totalItems > 0) { + browserItems.push({ + count: totalItems - browserTotal, + name: 'Others', + }); + } const osItems = os.buckets.map((bucket) => ({ count: bucket.doc_count, name: bucket.key as string, })); - osItems.push({ - count: totalItems - osTotal, - name: 'Others', - }); + if (totalItems > 0) { + osItems.push({ + count: totalItems - osTotal, + name: 'Others', + }); + } return { os: osItems, From 42b5d787e654bca0c1e0b789cd7f6f27065b7109 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Thu, 1 Oct 2020 07:46:07 +0200 Subject: [PATCH 21/36] [ML] Functional tests - stabilize calendar edit tests (#78950) This PR stabilizes the calendar edit test by making sure the correct calendar form is displayed on the edit page. --- .../__snapshots__/calendar_form.test.js.snap | 9 +++++++-- .../calendars/edit/calendar_form/calendar_form.js | 4 ++-- .../functional/apps/ml/settings/calendar_edit.ts | 11 ++++++++--- x-pack/test/functional/apps/ml/settings/index.ts | 2 +- .../functional/services/ml/settings_calendar.ts | 15 ++++++++++++--- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap b/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap index ad76bb91156172..49caddfd29f825 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap @@ -1,9 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`CalendarForm CalendarId shown as title when editing 1`] = ` - +

+

- +

+ {isEdit === true ? ( ) : ( diff --git a/x-pack/test/functional/apps/ml/settings/calendar_edit.ts b/x-pack/test/functional/apps/ml/settings/calendar_edit.ts index e738b50a2fe053..f9ba8fccd3c1c9 100644 --- a/x-pack/test/functional/apps/ml/settings/calendar_edit.ts +++ b/x-pack/test/functional/apps/ml/settings/calendar_edit.ts @@ -20,8 +20,7 @@ export default function ({ getService }: FtrProviderContext) { const jobConfigs = [createJobConfig('test_calendar_ad_1'), createJobConfig('test_calendar_ad_2')]; const newJobGroups = ['farequote']; - // FLAKY: https://github.com/elastic/kibana/issues/78288 - describe.skip('calendar edit', function () { + describe('calendar edit', function () { before(async () => { await esArchiver.loadIfNeeded('ml/farequote'); await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); @@ -56,6 +55,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('calendar edit opens existing calendar'); await ml.settingsCalendar.openCalendarEditForm(calendarId); + await ml.settingsCalendar.assertCalendarTitleValue(calendarId); await ml.testExecution.logTestStep( 'calendar edit deselects previous job selection and assigns new job groups' @@ -85,14 +85,19 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('calendar edit re-opens the updated calendar'); await ml.settingsCalendar.openCalendarEditForm(calendarId); + await ml.settingsCalendar.assertCalendarTitleValue(calendarId); + await ml.testExecution.logTestStep('calendar edit verifies the job selection is empty'); await ml.settingsCalendar.assertJobSelection([]); + await ml.testExecution.logTestStep( 'calendar edit verifies the job group selection was updated' ); await ml.settingsCalendar.assertJobGroupSelection(newJobGroups); - await ml.testExecution.logTestStep('calendar edit verifies calendar updated correctly'); + await ml.testExecution.logTestStep( + 'calendar edit verifies calendar events updated correctly' + ); await asyncForEach(testEvents, async ({ description }) => { await ml.settingsCalendar.assertEventRowMissing(description); }); diff --git a/x-pack/test/functional/apps/ml/settings/index.ts b/x-pack/test/functional/apps/ml/settings/index.ts index 5b2c7d15e19595..eb91e005ebd713 100644 --- a/x-pack/test/functional/apps/ml/settings/index.ts +++ b/x-pack/test/functional/apps/ml/settings/index.ts @@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('settings', function () { - this.tags(['quynh', 'skipFirefox']); + this.tags(['mlqa', 'skipFirefox']); loadTestFile(require.resolve('./calendar_creation')); loadTestFile(require.resolve('./calendar_edit')); diff --git a/x-pack/test/functional/services/ml/settings_calendar.ts b/x-pack/test/functional/services/ml/settings_calendar.ts index c269636522923f..3a2c9149a0634b 100644 --- a/x-pack/test/functional/services/ml/settings_calendar.ts +++ b/x-pack/test/functional/services/ml/settings_calendar.ts @@ -121,7 +121,7 @@ export function MachineLearningSettingsCalendarProvider( async openCalendarEditForm(calendarId: string) { await testSubjects.click(this.rowSelector(calendarId, 'mlEditCalendarLink')); - await testSubjects.existOrFail('mlPageCalendarEdit'); + await testSubjects.existOrFail('mlPageCalendarEdit > mlCalendarFormEdit', { timeout: 5000 }); }, async assertApplyToAllJobsSwitchEnabled(expectedValue: boolean) { @@ -224,6 +224,15 @@ export function MachineLearningSettingsCalendarProvider( ); }, + async assertCalendarTitleValue(expectedCalendarId: string) { + const actualValue = await testSubjects.getVisibleText('mlCalendarTitle'); + const expectedValue = `Calendar ${expectedCalendarId}`; + expect(actualValue).to.eql( + expectedValue, + `Calendar title should be '${expectedValue}' (got '${actualValue}')` + ); + }, + async setCalendarId(calendarId: string) { await mlCommonUI.setValueWithChecks('mlCalendarIdInput', calendarId, { clearWithKeyboard: true, @@ -271,13 +280,13 @@ export function MachineLearningSettingsCalendarProvider( async navigateToCalendarCreationPage() { await testSubjects.existOrFail('mlCalendarButtonCreate'); await testSubjects.click('mlCalendarButtonCreate'); - await testSubjects.existOrFail('mlPageCalendarEdit'); + await testSubjects.existOrFail('mlPageCalendarEdit > mlCalendarFormNew', { timeout: 5000 }); }, async openNewCalendarEventForm() { await testSubjects.existOrFail('mlCalendarNewEventButton'); await testSubjects.click('mlCalendarNewEventButton'); - await testSubjects.existOrFail('mlPageCalendarEdit'); + await testSubjects.existOrFail('mlCalendarEventForm'); }, async assertCalendarEventDescriptionValue(expectedValue: string) { From ec9d220b3c22d45a8a71b10920eedae122053b81 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 1 Oct 2020 08:23:07 +0200 Subject: [PATCH 22/36] [Discover] Unskip doc link functional test (#78600) * Flaky test runner confirmed it's not flaky --- test/functional/apps/discover/_doc_navigation.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/functional/apps/discover/_doc_navigation.js b/test/functional/apps/discover/_doc_navigation.js index 31aef96918ffab..87a150c7d6961c 100644 --- a/test/functional/apps/discover/_doc_navigation.js +++ b/test/functional/apps/discover/_doc_navigation.js @@ -28,8 +28,7 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const retry = getService('retry'); - // FLAKY: https://github.com/elastic/kibana/issues/78373 - describe.skip('doc link in discover', function contextSize() { + describe('doc link in discover', function contextSize() { beforeEach(async function () { log.debug('load kibana index with default index pattern'); await esArchiver.loadIfNeeded('discover'); From 3d9ea52803f433d3103ba62464b8157ac5541a24 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 1 Oct 2020 09:34:25 +0300 Subject: [PATCH 23/36] [Actions][Jira] Set parent issue for Sub-task issue type (#78772) --- docs/user/alerting/action-types/jira.asciidoc | 2 + x-pack/plugins/actions/README.md | 25 ++-- .../builtin_action_types/jira/api.test.ts | 32 +++++ .../server/builtin_action_types/jira/api.ts | 20 +++- .../server/builtin_action_types/jira/index.ts | 26 ++++- .../server/builtin_action_types/jira/mocks.ts | 13 +++ .../builtin_action_types/jira/schema.ts | 11 ++ .../builtin_action_types/jira/service.test.ts | 109 ++++++++++++++++++ .../builtin_action_types/jira/service.ts | 67 +++++++++++ .../server/builtin_action_types/jira/types.ts | 35 +++++- .../builtin_action_types/jira/api.ts | 38 ++++++ .../jira/jira_params.test.tsx | 3 + .../builtin_action_types/jira/jira_params.tsx | 32 ++++- .../jira/search_issues.tsx | 104 +++++++++++++++++ .../builtin_action_types/jira/translations.ts | 37 ++++++ .../builtin_action_types/jira/types.ts | 1 + .../jira/use_get_issues.tsx | 94 +++++++++++++++ .../jira/use_get_single_issue.tsx | 96 +++++++++++++++ .../actions/builtin_action_types/jira.ts | 12 +- 19 files changed, 733 insertions(+), 24 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/search_issues.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx diff --git a/docs/user/alerting/action-types/jira.asciidoc b/docs/user/alerting/action-types/jira.asciidoc index 48bd6c8501b9f6..65e5ee4fc4a013 100644 --- a/docs/user/alerting/action-types/jira.asciidoc +++ b/docs/user/alerting/action-types/jira.asciidoc @@ -69,6 +69,8 @@ Priority:: The priority of the incident. Labels:: The labels of the incident. Title:: A title for the issue, used for searching the contents of the knowledge base. Description:: The details about the incident. +Parent:: The parent issue id or key. Only for `Sub-task` issue types. +Priority:: The priority of the incident. Additional comments:: Additional information for the client, such as how to troubleshoot the issue. [[configuring-jira]] diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index af29a1d5374990..02e8e91c987d8e 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -274,12 +274,12 @@ Running the action by scheduling a task means that we will no longer have a user The following table describes the properties of the `options` object. -| Property | Description | Type | -| -------- | ------------------------------------------------------------------------------------------------------ | ------ | -| id | The id of the action you want to execute. | string | -| params | The `params` value to give the action type executor. | object | -| spaceId | The space id the action is within. | string | -| apiKey | The Elasticsearch API key to use for context. (Note: only required and used when security is enabled). | string | +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------------------------ | ---------------- | +| id | The id of the action you want to execute. | string | +| params | The `params` value to give the action type executor. | object | +| spaceId | The space id the action is within. | string | +| apiKey | The Elasticsearch API key to use for context. (Note: only required and used when security is enabled). | string | | source | The source of the execution, either an HTTP request or a reference to a Saved Object. | object, optional | ## Example @@ -308,11 +308,11 @@ This api runs the action and asynchronously returns the result of running the ac The following table describes the properties of the `options` object. -| Property | Description | Type | -| -------- | ------------------------------------------------------------------------------------ | ------ | -| id | The id of the action you want to execute. | string | -| params | The `params` value to give the action type executor. | object | -| source | The source of the execution, either an HTTP request or a reference to a Saved Object.| object, optional | +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------- | ---------------- | +| id | The id of the action you want to execute. | string | +| params | The `params` value to give the action type executor. | object | +| source | The source of the execution, either an HTTP request or a reference to a Saved Object. | object, optional | ## Example @@ -330,7 +330,7 @@ const result = await actionsClient.execute({ }, source: asSavedObjectExecutionSource({ id: '573891ae-8c48-49cb-a197-0cd5ec34a88b', - type: 'alert' + type: 'alert', }), }); ``` @@ -620,6 +620,7 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla | issueType | The id of the issue type in Jira. | string _(optional)_ | | priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | | labels | An array of labels. | string[] _(optional)_ | +| parent | The parent issue id or key. Only for `Sub-task` issue types. | string _(optional)_ | | comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | #### `subActionParams (issueTypes)` diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts index 4495c37f758eea..3948a19d40daea 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts @@ -93,6 +93,7 @@ describe('api', () => { issueType: '10006', labels: ['kibana', 'elastic'], priority: 'High', + parent: null, }, }); expect(externalService.updateIncident).not.toHaveBeenCalled(); @@ -252,6 +253,7 @@ describe('api', () => { issueType: '10006', labels: ['kibana', 'elastic'], priority: 'High', + parent: null, }, }); expect(externalService.createIncident).not.toHaveBeenCalled(); @@ -380,6 +382,36 @@ describe('api', () => { }); }); + describe('getIssues', () => { + test('it returns the issues correctly', async () => { + const res = await api.issues({ + externalService, + params: { title: 'Title test' }, + }); + expect(res).toEqual([ + { + id: '10267', + key: 'RJ-107', + title: 'Test title', + }, + ]); + }); + }); + + describe('getIssue', () => { + test('it returns the issue correctly', async () => { + const res = await api.issue({ + externalService, + params: { id: 'RJ-107' }, + }); + expect(res).toEqual({ + id: '10267', + key: 'RJ-107', + title: 'Test title', + }); + }); + }); + describe('mapping variations', () => { test('overwrite & append', async () => { mapping.set('title', { diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts index a64eb7a2036cac..559bbc047b134f 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts @@ -13,8 +13,10 @@ import { Incident, GetFieldsByIssueTypeHandlerArgs, GetIssueTypesHandlerArgs, + GetIssuesHandlerArgs, PushToServiceApiParams, PushToServiceResponse, + GetIssueHandlerArgs, } from './types'; // TODO: to remove, need to support Case @@ -46,6 +48,18 @@ const getFieldsByIssueTypeHandler = async ({ return res; }; +const getIssuesHandler = async ({ externalService, params }: GetIssuesHandlerArgs) => { + const { title } = params; + const res = await externalService.getIssues(title); + return res; +}; + +const getIssueHandler = async ({ externalService, params }: GetIssueHandlerArgs) => { + const { id } = params; + const res = await externalService.getIssue(id); + return res; +}; + const pushToServiceHandler = async ({ externalService, mapping, @@ -83,8 +97,8 @@ const pushToServiceHandler = async ({ currentIncident, }); } else { - const { title, description, priority, labels, issueType } = params; - incident = { summary: title, description, priority, labels, issueType }; + const { title, description, priority, labels, issueType, parent } = params; + incident = { summary: title, description, priority, labels, issueType, parent }; } if (externalId != null) { @@ -134,4 +148,6 @@ export const api: ExternalServiceApi = { getIncident: getIncidentHandler, issueTypes: getIssueTypesHandler, fieldsByIssueType: getFieldsByIssueTypeHandler, + issues: getIssuesHandler, + issue: getIssueHandler, }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts index d3346557f36841..9d6ff90c337009 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts @@ -25,6 +25,8 @@ import { JiraExecutorResultData, ExecutorSubActionGetFieldsByIssueTypeParams, ExecutorSubActionGetIssueTypesParams, + ExecutorSubActionGetIssuesParams, + ExecutorSubActionGetIssueParams, } from './types'; import * as i18n from './translations'; import { Logger } from '../../../../../../src/core/server'; @@ -37,7 +39,13 @@ interface GetActionTypeParams { configurationUtilities: ActionsConfigurationUtilities; } -const supportedSubActions: string[] = ['pushToService', 'issueTypes', 'fieldsByIssueType']; +const supportedSubActions: string[] = [ + 'pushToService', + 'issueTypes', + 'fieldsByIssueType', + 'issues', + 'issue', +]; // action type definition export function getActionType( @@ -137,5 +145,21 @@ async function executor( }); } + if (subAction === 'issues') { + const getIssuesParams = subActionParams as ExecutorSubActionGetIssuesParams; + data = await api.issues({ + externalService, + params: getIssuesParams, + }); + } + + if (subAction === 'issue') { + const getIssueParams = subActionParams as ExecutorSubActionGetIssueParams; + data = await api.issue({ + externalService, + params: getIssueParams, + }); + } + return { status: 'ok', data: data ?? {}, actionId }; } diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts index 53f8d43ebc2d8a..b98eda799e3aad 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts @@ -61,6 +61,18 @@ const createMock = (): jest.Mocked => { defaultValue: { name: 'Medium', id: '3' }, }, })), + getIssues: jest.fn().mockImplementation(() => [ + { + id: '10267', + key: 'RJ-107', + title: 'Test title', + }, + ]), + getIssue: jest.fn().mockImplementation(() => ({ + id: '10267', + key: 'RJ-107', + title: 'Test title', + })), }; service.createComment.mockImplementationOnce(() => @@ -120,6 +132,7 @@ const executorParams: ExecutorSubActionPushParams = { labels: ['kibana', 'elastic'], priority: 'High', issueType: '10006', + parent: null, comments: [ { commentId: 'case-comment-1', diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts index 9fee465e72efc2..4c31691280c2c8 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts @@ -44,6 +44,7 @@ export const ExecutorSubActionPushParamsSchema = schema.object({ issueType: schema.nullable(schema.string()), priority: schema.nullable(schema.string()), labels: schema.nullable(schema.arrayOf(schema.string())), + parent: schema.nullable(schema.string()), // TODO: modify later to string[] - need for support Case schema comments: schema.nullable(schema.arrayOf(CommentSchema)), ...EntityInformation, @@ -60,6 +61,8 @@ export const ExecutorSubActionGetIssueTypesParamsSchema = schema.object({}); export const ExecutorSubActionGetFieldsByIssueTypeParamsSchema = schema.object({ id: schema.string(), }); +export const ExecutorSubActionGetIssuesParamsSchema = schema.object({ title: schema.string() }); +export const ExecutorSubActionGetIssueParamsSchema = schema.object({ id: schema.string() }); export const ExecutorParamsSchema = schema.oneOf([ schema.object({ @@ -82,4 +85,12 @@ export const ExecutorParamsSchema = schema.oneOf([ subAction: schema.literal('fieldsByIssueType'), subActionParams: ExecutorSubActionGetFieldsByIssueTypeParamsSchema, }), + schema.object({ + subAction: schema.literal('issues'), + subActionParams: ExecutorSubActionGetIssuesParamsSchema, + }), + schema.object({ + subAction: schema.literal('issue'), + subActionParams: ExecutorSubActionGetIssueParamsSchema, + }), ]); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts index 2439c507c33286..605c05e2a9f259 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts @@ -95,6 +95,14 @@ const fieldsResponse = { }, }; +const issueResponse = { + id: '10267', + key: 'RJ-107', + fields: { summary: 'Test title' }, +}; + +const issuesResponse = [issueResponse]; + describe('Jira service', () => { let service: ExternalService; @@ -219,6 +227,7 @@ describe('Jira service', () => { labels: [], issueType: '10006', priority: 'High', + parent: null, }, }); @@ -264,6 +273,7 @@ describe('Jira service', () => { labels: [], priority: 'High', issueType: null, + parent: null, }, }); @@ -308,6 +318,7 @@ describe('Jira service', () => { labels: [], issueType: '10006', priority: 'High', + parent: 'RJ-107', }, }); @@ -324,6 +335,7 @@ describe('Jira service', () => { issuetype: { id: '10006' }, labels: [], priority: { name: 'High' }, + parent: { key: 'RJ-107' }, }, }, }); @@ -344,6 +356,7 @@ describe('Jira service', () => { labels: [], issueType: '10006', priority: 'High', + parent: null, }, }) ).rejects.toThrow( @@ -370,6 +383,7 @@ describe('Jira service', () => { labels: [], issueType: '10006', priority: 'High', + parent: null, }, }); @@ -398,6 +412,7 @@ describe('Jira service', () => { labels: [], issueType: '10006', priority: 'High', + parent: 'RJ-107', }, }); @@ -414,6 +429,7 @@ describe('Jira service', () => { priority: { name: 'High' }, issuetype: { id: '10006' }, project: { key: 'CK' }, + parent: { key: 'RJ-107' }, }, }, }); @@ -435,6 +451,7 @@ describe('Jira service', () => { labels: [], issueType: '10006', priority: 'High', + parent: null, }, }) ).rejects.toThrow( @@ -916,4 +933,96 @@ describe('Jira service', () => { }); }); }); + + describe('getIssues', () => { + test('it should return the issues', async () => { + requestMock.mockImplementation(() => ({ + data: { + issues: issuesResponse, + }, + })); + + const res = await service.getIssues('Test title'); + + expect(res).toEqual([ + { + id: '10267', + key: 'RJ-107', + title: 'Test title', + }, + ]); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { + issues: issuesResponse, + }, + })); + + await service.getIssues('Test title'); + expect(requestMock).toHaveBeenLastCalledWith({ + axios, + logger, + method: 'get', + url: `https://siem-kibana.atlassian.net/rest/api/2/search?jql=project=CK and summary ~"Test title"`, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + const error: ResponseError = new Error('An error has occurred'); + error.response = { data: { errors: { issuetypes: 'Could not get issue types' } } }; + throw error; + }); + + expect(service.getIssues('Test title')).rejects.toThrow( + '[Action][Jira]: Unable to get issues. Error: An error has occurred. Reason: Could not get issue types' + ); + }); + }); + + describe('getIssue', () => { + test('it should return a single issue', async () => { + requestMock.mockImplementation(() => ({ + data: issueResponse, + })); + + const res = await service.getIssue('RJ-107'); + + expect(res).toEqual({ + id: '10267', + key: 'RJ-107', + title: 'Test title', + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { + issues: issuesResponse, + }, + })); + + await service.getIssue('RJ-107'); + expect(requestMock).toHaveBeenLastCalledWith({ + axios, + logger, + method: 'get', + url: `https://siem-kibana.atlassian.net/rest/api/2/issue/RJ-107`, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + const error: ResponseError = new Error('An error has occurred'); + error.response = { data: { errors: { issuetypes: 'Could not get issue types' } } }; + throw error; + }); + + expect(service.getIssue('RJ-107')).rejects.toThrow( + '[Action][Jira]: Unable to get issue with id RJ-107. Error: An error has occurred. Reason: Could not get issue types' + ); + }); + }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts index 84b6e70d2a1002..7429c3d36d7b0b 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts @@ -53,6 +53,8 @@ export const createExternalService = ( const getIssueTypeFieldsOldAPIURL = `${url}/${BASE_URL}/issue/createmeta?projectKeys=${projectKey}&issuetypeIds={issueTypeId}&expand=projects.issuetypes.fields`; const getIssueTypesUrl = `${url}/${BASE_URL}/issue/createmeta/${projectKey}/issuetypes`; const getIssueTypeFieldsUrl = `${url}/${BASE_URL}/issue/createmeta/${projectKey}/issuetypes/{issueTypeId}`; + const searchUrl = `${url}/${BASE_URL}/search`; + const axiosInstance = axios.create({ auth: { username: email, password: apiToken }, }); @@ -90,6 +92,10 @@ export const createExternalService = ( fields = { ...fields, priority: { name: incident.priority } }; } + if (incident.parent) { + fields = { ...fields, parent: { key: incident.parent } }; + } + return fields; }; @@ -119,6 +125,17 @@ export const createExternalService = ( }; }, {}); + const normalizeSearchResults = ( + issues: Array<{ id: string; key: string; fields: { summary: string } }> + ) => + issues.map((issue) => ({ id: issue.id, key: issue.key, title: issue.fields?.summary ?? null })); + + const normalizeIssue = (issue: { id: string; key: string; fields: { summary: string } }) => ({ + id: issue.id, + key: issue.key, + title: issue.fields?.summary ?? null, + }); + const getIncident = async (id: string) => { try { const res = await request({ @@ -378,6 +395,54 @@ export const createExternalService = ( } }; + const getIssues = async (title: string) => { + const query = `${searchUrl}?jql=project=${projectKey} and summary ~"${title}"`; + try { + const res = await request({ + axios: axiosInstance, + method: 'get', + url: query, + logger, + proxySettings, + }); + + return normalizeSearchResults(res.data?.issues ?? []); + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to get issues. Error: ${error.message}. Reason: ${createErrorMessage( + error.response?.data?.errors ?? {} + )}` + ) + ); + } + }; + + const getIssue = async (id: string) => { + const getIssueUrl = `${incidentUrl}/${id}`; + try { + const res = await request({ + axios: axiosInstance, + method: 'get', + url: getIssueUrl, + logger, + proxySettings, + }); + + return normalizeIssue(res.data ?? {}); + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to get issue with id ${id}. Error: ${error.message}. Reason: ${createErrorMessage( + error.response?.data?.errors ?? {} + )}` + ) + ); + } + }; + return { getIncident, createIncident, @@ -386,5 +451,7 @@ export const createExternalService = ( getCapabilities, getIssueTypes, getFieldsByIssueType, + getIssues, + getIssue, }; }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts index 6fe7c62976f22d..050ec195d74c1d 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts @@ -17,6 +17,8 @@ import { ExecutorSubActionGetCapabilitiesParamsSchema, ExecutorSubActionGetIssueTypesParamsSchema, ExecutorSubActionGetFieldsByIssueTypeParamsSchema, + ExecutorSubActionGetIssuesParamsSchema, + ExecutorSubActionGetIssueParamsSchema, } from './schema'; import { ActionsConfigurationUtilities } from '../../actions_config'; import { IncidentConfigurationSchema } from '../case/schema'; @@ -60,7 +62,7 @@ export type ExternalServiceParams = Record; export type Incident = Pick< ExecutorSubActionPushParams, - 'description' | 'priority' | 'labels' | 'issueType' + 'description' | 'priority' | 'labels' | 'issueType' | 'parent' > & { summary: string }; export interface CreateIncidentParams { @@ -83,6 +85,13 @@ export type GetFieldsByIssueTypeResponse = Record< { allowedValues: Array<{}>; defaultValue: {} } >; +export type GetIssuesResponse = Array<{ id: string; key: string; title: string }>; +export interface GetIssueResponse { + id: string; + key: string; + title: string; +} + export interface ExternalService { getIncident: (id: string) => Promise; createIncident: (params: CreateIncidentParams) => Promise; @@ -91,6 +100,8 @@ export interface ExternalService { getCapabilities: () => Promise; getIssueTypes: () => Promise; getFieldsByIssueType: (issueTypeId: string) => Promise; + getIssues: (title: string) => Promise; + getIssue: (id: string) => Promise; } export interface PushToServiceApiParams extends ExecutorSubActionPushParams { @@ -117,6 +128,12 @@ export type ExecutorSubActionGetFieldsByIssueTypeParams = TypeOf< typeof ExecutorSubActionGetFieldsByIssueTypeParamsSchema >; +export type ExecutorSubActionGetIssuesParams = TypeOf< + typeof ExecutorSubActionGetIssuesParamsSchema +>; + +export type ExecutorSubActionGetIssueParams = TypeOf; + export interface ExternalServiceApiHandlerArgs { externalService: ExternalService; mapping: Map | null; @@ -149,6 +166,16 @@ export interface PushToServiceResponse extends ExternalServiceIncidentResponse { comments?: ExternalServiceCommentResponse[]; } +export interface GetIssuesHandlerArgs { + externalService: ExternalService; + params: ExecutorSubActionGetIssuesParams; +} + +export interface GetIssueHandlerArgs { + externalService: ExternalService; + params: ExecutorSubActionGetIssueParams; +} + export interface ExternalServiceApi { handshake: (args: HandshakeApiHandlerArgs) => Promise; pushToService: (args: PushToServiceApiHandlerArgs) => Promise; @@ -157,12 +184,16 @@ export interface ExternalServiceApi { fieldsByIssueType: ( args: GetFieldsByIssueTypeHandlerArgs ) => Promise; + issues: (args: GetIssuesHandlerArgs) => Promise; + issue: (args: GetIssueHandlerArgs) => Promise; } export type JiraExecutorResultData = | PushToServiceResponse | GetIssueTypesResponse - | GetFieldsByIssueTypeResponse; + | GetFieldsByIssueTypeResponse + | GetIssuesResponse + | GetIssueResponse; export interface Fields { [key: string]: string | string[] | { name: string } | { key: string } | { id: string }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts index 86893e5b87ddf4..bc9fee042a9a67 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts @@ -42,3 +42,41 @@ export async function getFieldsByIssueType({ signal, }); } + +export async function getIssues({ + http, + signal, + connectorId, + title, +}: { + http: HttpSetup; + signal: AbortSignal; + connectorId: string; + title: string; +}): Promise> { + return await http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { + body: JSON.stringify({ + params: { subAction: 'issues', subActionParams: { title } }, + }), + signal, + }); +} + +export async function getIssue({ + http, + signal, + connectorId, + id, +}: { + http: HttpSetup; + signal: AbortSignal; + connectorId: string; + id: string; +}): Promise> { + return await http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { + body: JSON.stringify({ + params: { subAction: 'getIncident', subActionParams: { id } }, + }), + signal, + }); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx index d96657f8ca4077..416f6f7b18755d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx @@ -31,8 +31,10 @@ const actionParams = { priority: 'High', savedObjectId: '123', externalId: null, + parent: null, }, }; + const connector = { secrets: {}, config: {}, @@ -237,5 +239,6 @@ describe('JiraParamsFields renders', () => { expect(wrapper.find('[data-test-subj="prioritySelect"]').exists()).toBeFalsy(); expect(wrapper.find('[data-test-subj="descriptionTextArea"]').exists()).toBeFalsy(); expect(wrapper.find('[data-test-subj="labelsComboBox"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="search-parent-issues"]').exists()).toBeFalsy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx index b457dcc60a43f9..c19d2c4048665d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx @@ -19,6 +19,7 @@ import { TextFieldWithMessageVariables } from '../../text_field_with_message_var import { JiraActionParams } from './types'; import { useGetIssueTypes } from './use_get_issue_types'; import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type'; +import { SearchIssues } from './search_issues'; const JiraParamsFields: React.FunctionComponent> = ({ actionParams, @@ -30,7 +31,7 @@ const JiraParamsFields: React.FunctionComponent { - const { title, description, comments, issueType, priority, labels, savedObjectId } = + const { title, description, comments, issueType, priority, labels, parent, savedObjectId } = actionParams.subActionParams || {}; const [issueTypesSelectOptions, setIssueTypesSelectOptions] = useState([]); @@ -62,6 +63,7 @@ const JiraParamsFields: React.FunctionComponent Object.prototype.hasOwnProperty.call(fields, 'priority'), [ fields, ]); + const hasParent = useMemo(() => Object.prototype.hasOwnProperty.call(fields, 'parent'), [fields]); useEffect(() => { const options = issueTypes.map((type) => ({ @@ -179,6 +181,34 @@ const JiraParamsFields: React.FunctionComponent + {hasParent && ( + <> + + + + { + editSubActionProperty('parent', parentIssueKey); + }} + /> + + + + + + )} <> {hasPriority && ( <> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/search_issues.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/search_issues.tsx new file mode 100644 index 00000000000000..fff606982677aa --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/search_issues.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useMemo, useEffect, useCallback, useState, memo } from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; + +import { HttpSetup, ToastsApi } from 'kibana/public'; +import { ActionConnector } from '../../../../types'; +import { useGetIssues } from './use_get_issues'; +import { useGetSingleIssue } from './use_get_single_issue'; +import * as i18n from './translations'; + +interface Props { + selectedValue: string | null; + http: HttpSetup; + toastNotifications: Pick< + ToastsApi, + 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' + >; + actionConnector?: ActionConnector; + onChange: (parentIssueKey: string) => void; +} + +const SearchIssuesComponent: React.FC = ({ + selectedValue, + http, + toastNotifications, + actionConnector, + onChange, +}) => { + const [query, setQuery] = useState(null); + const [selectedOptions, setSelectedOptions] = useState>>( + [] + ); + const [options, setOptions] = useState>>([]); + + const { isLoading: isLoadingIssues, issues } = useGetIssues({ + http, + toastNotifications, + actionConnector, + query, + }); + + const { isLoading: isLoadingSingleIssue, issue: singleIssue } = useGetSingleIssue({ + http, + toastNotifications, + actionConnector, + id: selectedValue, + }); + + useEffect(() => setOptions(issues.map((issue) => ({ label: issue.title, value: issue.key }))), [ + issues, + ]); + + useEffect(() => { + if (isLoadingSingleIssue || singleIssue == null) { + return; + } + + const singleIssueAsOptions = [{ label: singleIssue.title, value: singleIssue.key }]; + setOptions(singleIssueAsOptions); + setSelectedOptions(singleIssueAsOptions); + }, [singleIssue, isLoadingSingleIssue]); + + const onSearchChange = useCallback((searchVal: string) => { + setQuery(searchVal); + }, []); + + const onChangeComboBox = useCallback( + (changedOptions) => { + setSelectedOptions(changedOptions); + onChange(changedOptions[0].value); + }, + [onChange] + ); + + const inputPlaceholder = useMemo( + (): string => + isLoadingIssues || isLoadingSingleIssue + ? i18n.SEARCH_ISSUES_LOADING + : i18n.SEARCH_ISSUES_PLACEHOLDER, + [isLoadingIssues, isLoadingSingleIssue] + ); + + return ( + + ); +}; + +export const SearchIssues = memo(SearchIssuesComponent); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts index bfcb72d1cb977f..2517552304d8da 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts @@ -131,3 +131,40 @@ export const FIELDS_API_ERROR = i18n.translate( defaultMessage: 'Unable to get fields', } ); + +export const ISSUES_API_ERROR = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage', + { + defaultMessage: 'Unable to get issues', + } +); + +export const GET_ISSUE_API_ERROR = (id: string) => + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueMessage', + { + defaultMessage: 'Unable to get issue with id {id}', + values: { id }, + } + ); + +export const SEARCH_ISSUES_COMBO_BOX_ARIA_LABEL = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel', + { + defaultMessage: 'Select parent issue', + } +); + +export const SEARCH_ISSUES_PLACEHOLDER = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder', + { + defaultMessage: 'Select parent issue', + } +); + +export const SEARCH_ISSUES_LOADING = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading', + { + defaultMessage: 'Loading...', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts index ff11199f35fea9..4c13d067913f21 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts @@ -22,6 +22,7 @@ export interface JiraActionParams { issueType: string; priority: string; labels: string[]; + parent: string | null; }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx new file mode 100644 index 00000000000000..d6590b8c70939a --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { isEmpty, debounce } from 'lodash/fp'; +import { useState, useEffect, useRef } from 'react'; +import { HttpSetup, ToastsApi } from 'kibana/public'; +import { ActionConnector } from '../../../../types'; +import { getIssues } from './api'; +import * as i18n from './translations'; + +type Issues = Array<{ id: string; key: string; title: string }>; + +interface Props { + http: HttpSetup; + toastNotifications: Pick< + ToastsApi, + 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' + >; + actionConnector?: ActionConnector; + query: string | null; +} + +export interface UseGetIssues { + issues: Issues; + isLoading: boolean; +} + +export const useGetIssues = ({ + http, + actionConnector, + toastNotifications, + query, +}: Props): UseGetIssues => { + const [isLoading, setIsLoading] = useState(false); + const [issues, setIssues] = useState([]); + const abortCtrl = useRef(new AbortController()); + + useEffect(() => { + let didCancel = false; + const fetchData = debounce(500, async () => { + if (!actionConnector || isEmpty(query)) { + setIsLoading(false); + return; + } + + abortCtrl.current = new AbortController(); + setIsLoading(true); + + try { + const res = await getIssues({ + http, + signal: abortCtrl.current.signal, + connectorId: actionConnector.id, + title: query ?? '', + }); + + if (!didCancel) { + setIsLoading(false); + setIssues(res.data ?? []); + if (res.status && res.status === 'error') { + toastNotifications.addDanger({ + title: i18n.ISSUES_API_ERROR, + text: `${res.serviceMessage ?? res.message}`, + }); + } + } + } catch (error) { + if (!didCancel) { + toastNotifications.addDanger({ + title: i18n.ISSUES_API_ERROR, + text: error.message, + }); + } + } + }); + + abortCtrl.current.abort(); + fetchData(); + + return () => { + didCancel = true; + setIsLoading(false); + abortCtrl.current.abort(); + }; + }, [http, actionConnector, toastNotifications, query]); + + return { + issues, + isLoading, + }; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx new file mode 100644 index 00000000000000..7df9834f1bd850 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useState, useEffect, useRef } from 'react'; +import { HttpSetup, ToastsApi } from 'kibana/public'; +import { ActionConnector } from '../../../../types'; +import { getIssue } from './api'; +import * as i18n from './translations'; + +interface Issue { + id: string; + key: string; + title: string; +} + +interface Props { + http: HttpSetup; + toastNotifications: Pick< + ToastsApi, + 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' + >; + id: string | null; + actionConnector?: ActionConnector; +} + +export interface UseGetSingleIssue { + issue: Issue | null; + isLoading: boolean; +} + +export const useGetSingleIssue = ({ + http, + toastNotifications, + actionConnector, + id, +}: Props): UseGetSingleIssue => { + const [isLoading, setIsLoading] = useState(false); + const [issue, setIssue] = useState(null); + const abortCtrl = useRef(new AbortController()); + + useEffect(() => { + let didCancel = false; + const fetchData = async () => { + if (!actionConnector || !id) { + setIsLoading(false); + return; + } + + abortCtrl.current = new AbortController(); + setIsLoading(true); + try { + const res = await getIssue({ + http, + signal: abortCtrl.current.signal, + connectorId: actionConnector.id, + id, + }); + + if (!didCancel) { + setIsLoading(false); + setIssue(res.data ?? {}); + if (res.status && res.status === 'error') { + toastNotifications.addDanger({ + title: i18n.GET_ISSUE_API_ERROR(id), + text: `${res.serviceMessage ?? res.message}`, + }); + } + } + } catch (error) { + if (!didCancel) { + toastNotifications.addDanger({ + title: i18n.GET_ISSUE_API_ERROR(id), + text: error.message, + }); + } + } + }; + + abortCtrl.current.abort(); + fetchData(); + + return () => { + didCancel = true; + setIsLoading(false); + abortCtrl.current.abort(); + }; + }, [http, actionConnector, id, toastNotifications]); + + return { + isLoading, + issue, + }; +}; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts index 84fad699525a9c..1a56a9dfcb4dbf 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts @@ -333,7 +333,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]', }); }); }); @@ -351,7 +351,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]', }); }); }); @@ -369,7 +369,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]', }); }); }); @@ -392,7 +392,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]', }); }); }); @@ -420,7 +420,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]', }); }); }); @@ -448,7 +448,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]', }); }); }); From 4c9a7bdf4872b6cb73e4bf8524bab0816279cdb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Thu, 1 Oct 2020 08:22:51 +0100 Subject: [PATCH 24/36] [Usage Collection] [schema] `ui_metric` (#78827) --- .telemetryrc.json | 3 +- .../server/collectors/ui_metric/schema.ts | 103 +++ .../telemetry_ui_metric_collector.ts | 13 +- src/plugins/telemetry/schema/oss_plugins.json | 745 ++++++++++++++++++ 4 files changed, 860 insertions(+), 4 deletions(-) create mode 100644 src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts diff --git a/.telemetryrc.json b/.telemetryrc.json index d3446b45033eec..3d1b0df1d8f93b 100644 --- a/.telemetryrc.json +++ b/.telemetryrc.json @@ -5,8 +5,7 @@ "exclude": [ "src/plugins/kibana_react/", "src/plugins/testbed/", - "src/plugins/kibana_utils/", - "src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts" + "src/plugins/kibana_utils/" ] } ] diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts new file mode 100644 index 00000000000000..53bb1f9b939493 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts @@ -0,0 +1,103 @@ +/* + * 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 { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; +import { UIMetricUsage } from './telemetry_ui_metric_collector'; + +const commonSchema: MakeSchemaFrom[string] = { + type: 'array', + items: { + key: { type: 'keyword' }, + value: { type: 'long' }, + }, +}; + +// TODO: Find a way to retrieve it automatically +// plugin `data` registers all UI Metric for each appId where searches are performed (keys below are copy-pasted from application_usage) +const uiMetricFromDataPluginSchema: MakeSchemaFrom = { + // OSS + dashboards: commonSchema, + dev_tools: commonSchema, + discover: commonSchema, + home: commonSchema, + kibana: commonSchema, // It's a forward app so we'll likely never report it + management: commonSchema, + short_url_redirect: commonSchema, // It's a forward app so we'll likely never report it + timelion: commonSchema, + visualize: commonSchema, + + // X-Pack + apm: commonSchema, + csm: commonSchema, + canvas: commonSchema, + dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it + enterpriseSearch: commonSchema, + appSearch: commonSchema, + workplaceSearch: commonSchema, + graph: commonSchema, + logs: commonSchema, + metrics: commonSchema, + infra: commonSchema, // It's a forward app so we'll likely never report it + ingestManager: commonSchema, + lens: commonSchema, + maps: commonSchema, + ml: commonSchema, + monitoring: commonSchema, + 'observability-overview': commonSchema, + security_account: commonSchema, + security_access_agreement: commonSchema, + security_capture_url: commonSchema, // It's a forward app so we'll likely never report it + security_logged_out: commonSchema, + security_login: commonSchema, + security_logout: commonSchema, + security_overwritten_session: commonSchema, + securitySolution: commonSchema, + 'securitySolution:overview': commonSchema, + 'securitySolution:detections': commonSchema, + 'securitySolution:hosts': commonSchema, + 'securitySolution:network': commonSchema, + 'securitySolution:timelines': commonSchema, + 'securitySolution:case': commonSchema, + 'securitySolution:administration': commonSchema, + siem: commonSchema, + space_selector: commonSchema, + uptime: commonSchema, +}; + +// TODO: Find a way to retrieve it automatically +// Searching `reportUiStats` across Kibana +export const uiMetricSchema: MakeSchemaFrom = { + console: commonSchema, + DashboardPanelVersionInUrl: commonSchema, + Kibana_home: commonSchema, // eslint-disable-line @typescript-eslint/naming-convention + visualize: commonSchema, + canvas: commonSchema, + cross_cluster_replication: commonSchema, + index_lifecycle_management: commonSchema, + index_management: commonSchema, + ingest_pipelines: commonSchema, + apm: commonSchema, + infra_logs: commonSchema, + infra_metrics: commonSchema, + stack_monitoring: commonSchema, + remote_clusters: commonSchema, + rollup_jobs: commonSchema, + securitySolution: commonSchema, + snapshot_restore: commonSchema, + ...uiMetricFromDataPluginSchema, +}; diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts index 9c02a9cbf32041..4cae892d30b5db 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts @@ -23,11 +23,19 @@ import { SavedObjectsServiceSetup, } from 'kibana/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { uiMetricSchema } from './schema'; interface UIMetricsSavedObjects extends SavedObjectAttributes { count: number; } +interface UIMetricElement { + key: string; + value: number; +} + +export type UIMetricUsage = Record; + export function registerUiMetricUsageCollector( usageCollection: UsageCollectionSetup, registerType: SavedObjectsServiceSetup['registerType'], @@ -46,8 +54,9 @@ export function registerUiMetricUsageCollector( }, }); - const collector = usageCollection.makeUsageCollector({ + const collector = usageCollection.makeUsageCollector({ type: 'ui_metric', + schema: uiMetricSchema, fetch: async () => { const savedObjectsClient = getSavedObjectsClient(); if (typeof savedObjectsClient === 'undefined') { @@ -73,7 +82,7 @@ export function registerUiMetricUsageCollector( ...accum, [appName]: [...(accum[appName] || []), pair], }; - }, {} as Record>); + }, {} as UIMetricUsage); return uiMetricsByAppName; }, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 6531262b6f1da9..1f474dcbb8ff4a 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -1623,6 +1623,751 @@ } } }, + "ui_metric": { + "properties": { + "console": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "DashboardPanelVersionInUrl": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "Kibana_home": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "visualize": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "canvas": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "cross_cluster_replication": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "index_lifecycle_management": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "index_management": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "ingest_pipelines": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "apm": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "infra_logs": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "infra_metrics": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "stack_monitoring": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "remote_clusters": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "rollup_jobs": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "snapshot_restore": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "dashboards": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "dev_tools": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "discover": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "home": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "kibana": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "management": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "short_url_redirect": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "timelion": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "csm": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "dashboard_mode": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "enterpriseSearch": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "appSearch": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "workplaceSearch": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "graph": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "logs": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "metrics": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "infra": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "ingestManager": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "lens": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "maps": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "ml": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "monitoring": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "observability-overview": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "security_account": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "security_access_agreement": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "security_capture_url": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "security_logged_out": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "security_login": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "security_logout": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "security_overwritten_session": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution:overview": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution:detections": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution:hosts": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution:network": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution:timelines": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution:case": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "securitySolution:administration": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "siem": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "space_selector": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + }, + "uptime": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + } + } + }, "telemetry": { "properties": { "opt_in_status": { From 9fdb23769bbe8ac621df7e335a55d4a91e74465d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Thu, 1 Oct 2020 08:25:05 +0100 Subject: [PATCH 25/36] [Loggers] Rename "telemetry" to "usage" (#78130) Co-authored-by: Elastic Machine --- .../home/server/services/sample_data/sample_data_registry.ts | 2 +- x-pack/plugins/actions/server/plugin.ts | 2 +- x-pack/plugins/alerts/server/plugin.ts | 2 +- x-pack/plugins/lens/server/plugin.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/home/server/services/sample_data/sample_data_registry.ts b/src/plugins/home/server/services/sample_data/sample_data_registry.ts index 356c8864364138..c9e65b292a00d0 100644 --- a/src/plugins/home/server/services/sample_data/sample_data_registry.ts +++ b/src/plugins/home/server/services/sample_data/sample_data_registry.ts @@ -52,7 +52,7 @@ export class SampleDataRegistry { } const usageTracker = usage( core.getStartServices().then(([coreStart]) => coreStart.savedObjects), - this.initContext.logger.get('sample_data', 'telemetry') + this.initContext.logger.get('sample_data', 'usage') ); const router = core.http.createRouter(); createListRoute(router, this.sampleDatasets); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index dca1114f0ae44c..1a15a5a815195f 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -151,7 +151,7 @@ export class ActionsPlugin implements Plugin, Plugi .toPromise(); this.logger = initContext.logger.get('actions'); - this.telemetryLogger = initContext.logger.get('telemetry'); + this.telemetryLogger = initContext.logger.get('usage'); this.preconfiguredActions = []; } diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts index 8f09d55c9a0e01..e9caf4b78e6279 100644 --- a/x-pack/plugins/alerts/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -117,7 +117,7 @@ export class AlertingPlugin { this.logger = initializerContext.logger.get('plugins', 'alerting'); this.taskRunnerFactory = new TaskRunnerFactory(); this.alertsClientFactory = new AlertsClientFactory(); - this.telemetryLogger = initializerContext.logger.get('telemetry'); + this.telemetryLogger = initializerContext.logger.get('usage'); this.kibanaIndex = initializerContext.config.legacy.globalConfig$ .pipe( first(), diff --git a/x-pack/plugins/lens/server/plugin.tsx b/x-pack/plugins/lens/server/plugin.tsx index 3611658fbbcd91..b801d30f5ba9b9 100644 --- a/x-pack/plugins/lens/server/plugin.tsx +++ b/x-pack/plugins/lens/server/plugin.tsx @@ -31,7 +31,7 @@ export class LensServerPlugin implements Plugin<{}, {}, {}, {}> { constructor(initializerContext: PluginInitializerContext) { this.kibanaIndexConfig = initializerContext.config.legacy.globalConfig$; - this.telemetryLogger = initializerContext.logger.get('telemetry'); + this.telemetryLogger = initializerContext.logger.get('usage'); } setup(core: CoreSetup, plugins: PluginSetupContract) { setupSavedObjects(core); From 65cf6393c770af89567685980313e464e69745d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Thu, 1 Oct 2020 08:26:14 +0100 Subject: [PATCH 26/36] [Task names in TaskManager] Rename "telemetry" to "usage" (#78129) * [Task names in TaskManager] Rename "telemetry" to "usage" * Revert task IDs but leaving renamed titles Co-authored-by: Elastic Machine --- x-pack/plugins/actions/server/usage/task.ts | 2 +- x-pack/plugins/alerts/server/usage/task.ts | 2 +- x-pack/plugins/apm/server/lib/apm_telemetry/index.ts | 2 +- x-pack/plugins/lens/server/usage/task.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/actions/server/usage/task.ts b/x-pack/plugins/actions/server/usage/task.ts index 050f0021a32c1f..efa695cdc2667b 100644 --- a/x-pack/plugins/actions/server/usage/task.ts +++ b/x-pack/plugins/actions/server/usage/task.ts @@ -38,7 +38,7 @@ function registerActionsTelemetryTask( ) { taskManager.registerTaskDefinitions({ [TELEMETRY_TASK_TYPE]: { - title: 'Actions telemetry fetch task', + title: 'Actions usage fetch task', type: TELEMETRY_TASK_TYPE, timeout: '5m', createTaskRunner: telemetryTaskRunner(logger, core, kibanaIndex), diff --git a/x-pack/plugins/alerts/server/usage/task.ts b/x-pack/plugins/alerts/server/usage/task.ts index 5909351321385b..daf3ac246adadb 100644 --- a/x-pack/plugins/alerts/server/usage/task.ts +++ b/x-pack/plugins/alerts/server/usage/task.ts @@ -41,7 +41,7 @@ function registerAlertingTelemetryTask( ) { taskManager.registerTaskDefinitions({ [TELEMETRY_TASK_TYPE]: { - title: 'Alerting telemetry fetch task', + title: 'Alerting usage fetch task', type: TELEMETRY_TASK_TYPE, timeout: '5m', createTaskRunner: telemetryTaskRunner(logger, core, kibanaIndex), diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts index 3463865d326b0e..f78280aa7428e0 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts @@ -49,7 +49,7 @@ export async function createApmTelemetry({ }) { taskManager.registerTaskDefinitions({ [APM_TELEMETRY_TASK_NAME]: { - title: 'Collect APM telemetry', + title: 'Collect APM usage', type: APM_TELEMETRY_TASK_NAME, createTaskRunner: () => { return { diff --git a/x-pack/plugins/lens/server/usage/task.ts b/x-pack/plugins/lens/server/usage/task.ts index edc5a778749af1..9fee72b59b44cd 100644 --- a/x-pack/plugins/lens/server/usage/task.ts +++ b/x-pack/plugins/lens/server/usage/task.ts @@ -47,7 +47,7 @@ function registerLensTelemetryTask( ) { taskManager.registerTaskDefinitions({ [TELEMETRY_TASK_TYPE]: { - title: 'Lens telemetry fetch task', + title: 'Lens usage fetch task', type: TELEMETRY_TASK_TYPE, timeout: '1m', createTaskRunner: telemetryTaskRunner(logger, core, config), From ad134b296b4fd1f62f93f6719caec7d83b256dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Thu, 1 Oct 2020 08:42:23 +0100 Subject: [PATCH 27/36] fixing api test (#78964) --- .../tests/transaction_groups/distribution.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts index bd669600afc147..c72d48094ca8da 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts @@ -37,8 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - // SKIP FAILING TEST to unblock CI: https://github.com/elastic/kibana/issues/78942 - describe.skip('when data is loaded', () => { + describe('when data is loaded', () => { let response: any; before(async () => { await esArchiver.load(archiveName); @@ -61,7 +60,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns the correct number of buckets', () => { - expectSnapshot(response.body.buckets.length).toMatchInline(`19`); + expectSnapshot(response.body.buckets.length).toMatchInline(`45`); }); it('returns the correct bucket size', () => { @@ -73,18 +72,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { (bucket: any) => !isEmpty(bucket.samples) ); - expectSnapshot(bucketWithSamples.count).toMatchInline(`2`); + expectSnapshot(bucketWithSamples.count).toMatchInline(`1`); expectSnapshot(bucketWithSamples.samples.sort((sample: any) => sample.traceId)) .toMatchInline(` Array [ Object { - "traceId": "a1333547d1257c636154290cddd38c3a", - "transactionId": "3e656b390989133d", - }, - Object { - "traceId": "c799c34f4ee2b0f9998745ea7354d599", - "transactionId": "69b6251b239abb46", + "traceId": "3dd90c5c2035f5bcb2728a34cb48d796", + "transactionId": "69f3ff7d35056f63", }, ] `); From 4bd0d3bf8724db1e197eb8ff7604c24a9cd7a662 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 1 Oct 2020 10:54:37 +0300 Subject: [PATCH 28/36] [TSVB] Request validation error: [panels.0.series.0.metrics.0.percentiles.1.value] (#79009) Closes: #79006 --- src/plugins/vis_type_timeseries/common/vis_schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_type_timeseries/common/vis_schema.ts b/src/plugins/vis_type_timeseries/common/vis_schema.ts index d3d863df8617f9..b33215934c5dfd 100644 --- a/src/plugins/vis_type_timeseries/common/vis_schema.ts +++ b/src/plugins/vis_type_timeseries/common/vis_schema.ts @@ -111,7 +111,7 @@ export const metricsItems = schema.object({ field: stringOptionalNullable, mode: schema.oneOf([schema.literal('line'), schema.literal('band')]), shade: schema.oneOf([numberOptional, stringOptionalNullable]), - value: schema.oneOf([numberOptional, stringOptionalNullable]), + value: schema.maybe(schema.oneOf([numberOptional, stringOptionalNullable])), percentile: stringOptionalNullable, }) ) From 09226db99c8823a0fec5fe5fa87e33909f95621a Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Thu, 1 Oct 2020 10:56:17 +0300 Subject: [PATCH 29/36] Data plugin README (#78750) * data readme * Delete old readme (other folders don't have a README of their own. * generate asciidoc * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update README.md * Update plugin-list.asciidoc * gen plugin list * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update src/plugins/data/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: Elastic Machine Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/developer/plugin-list.asciidoc | 2 +- src/plugins/data/README.md | 140 ++++++++++++++++++++++- src/plugins/data/public/search/README.md | 23 ---- 3 files changed, 135 insertions(+), 30 deletions(-) delete mode 100644 src/plugins/data/public/search/README.md diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index e314e55c34085e..ed58e77427d47d 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -48,7 +48,7 @@ NOTE: |{kib-repo}blob/{branch}/src/plugins/data/README.md[data] -|data plugin provides common data access services. +|The data plugin provides common data access services, such as search and query, for solutions and application developers. |{kib-repo}blob/{branch}/src/plugins/dev_tools/README.md[devTools] diff --git a/src/plugins/data/README.md b/src/plugins/data/README.md index da0b71122fd9eb..33c07078c5348c 100644 --- a/src/plugins/data/README.md +++ b/src/plugins/data/README.md @@ -1,9 +1,137 @@ # data -`data` plugin provides common data access services. +The data plugin provides common data access services, such as `search` and `query`, for solutions and application developers. -- `expressions` — run pipeline functions and render results. -- `filter` -- `index_patterns` -- `query` -- `search`: Elasticsearch API service and strategies \ No newline at end of file +## Autocomplete + +The autocomplete service provides suggestions for field names and values. + +It is wired into the `TopNavMenu` component, but can be used independently. + +### Fetch Query Suggestions + +The `getQuerySuggestions` function helps to construct a query. +KQL suggestion functions are registered in X-Pack, so this API does not return results in OSS. + +```.ts + + // `inputValue` is the user input + const querySuggestions = await autocomplete.getQuerySuggestions({ + language: 'kuery', + indexPatterns: [indexPattern], + query: inputValue, + }); + +``` + +### Fetch Value Suggestions + +The `getValueSuggestions` function returns suggestions for field values. +This is helpful when you want to provide a user with options, for example when constructing a filter. + +```.ts + + // `inputValue` is the user input + const valueSuggestions = await autocomplete.getValueSuggestions({ + indexPattern, + field, + query: inputValue, + }); + +``` + +## Field Formats + +Coming soon. + +## Index Patterns + +Coming soon. + +## Query + +The query service is responsible for managing the configuration of a search query (`QueryState`): filters, time range, query string, and settings such as the auto refresh behavior and saved queries. + +It contains sub-services for each of those configurations: + - `data.query.filterManager` - Manages the `filters` component of a `QueryState`. The global filter state (filters that are persisted between applications) are owned by this service. + - `data.query.timefilter` - Responsible for the time range filter and the auto refresh behavior settings. + - `data.query.queryString` - Responsible for the query string and query language settings. + - `data.query.savedQueries` - Responsible for persisting a `QueryState` into a `SavedObject`, so it can be restored and used by other applications. + + Any changes to the `QueryState` are published on the `data.query.state$`, which is useful when wanting to persist global state or run a search upon data changes. + + A simple use case is: + + ```.ts + function searchOnChange(indexPattern: IndexPattern, aggConfigs: AggConfigs) { + data.query.state$.subscribe(() => { + + // Constuct the query portion of the search request + const query = data.query.getEsQuery(indexPattern); + + // Construct a request + const request = { + params: { + index: indexPattern.title, + body: { + aggs: aggConfigs.toDsl(), + query, + }, + }, + }; + + // Search with the `data.query` config + const search$ = data.search.search(request); + + ... + }); + } + + ``` + +## Search + +Provides access to Elasticsearch using the high-level `SearchSource` API or low-level `Search Strategies`. + +### SearchSource + +The `SearchSource` API is a convenient way to construct and run an Elasticsearch search query. + +```.tsx + + const searchSource = await data.search.searchSource.create(); + const searchResponse = await searchSource + .setParent(undefined) + .setField('index', indexPattern) + .setField('filter', filters) + .fetch(); + +``` + +### Low-level search + +#### Default Search Strategy + +One benefit of using the low-level search API, is partial response support in X-Pack, allowing for a better and more responsive user experience. +In OSS only the final result is returned. + +```.ts + import { isCompleteResponse } from '../plugins/data/public'; + + const search$ = data.search.search(request) + .subscribe({ + next: (response) => { + if (isCompleteResponse(response)) { + // Final result + search$.unsubscribe(); + } else { + // Partial result - you can update the UI, but data is still loading + } + }, + error: (e: Error) => { + // Show customized toast notifications. + // You may choose to handle errors differently if you prefer. + data.search.showError(e); + }, + }); +``` diff --git a/src/plugins/data/public/search/README.md b/src/plugins/data/public/search/README.md deleted file mode 100644 index 0a123ffa3f1e99..00000000000000 --- a/src/plugins/data/public/search/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# search - -The `search` service provides you with APIs to query Elasticsearch. - -The services are split into two parts: (1) low-level API; and (2) high-level API. - -## Low-level API - -With low level API you work directly with elasticsearch DSL - -```typescript -const results = await data.search.search(request, params); -``` - -## High-level API - -Using high-level API you work with Kibana abstractions around Elasticsearch DSL: filters, queries, and aggregations. Provided by the *Search Source* service. - -```typescript -const search = data.search.searchSource.createEmpty(); -search.setField('query', data.query.queryString); -const results = await search.fetch(); -``` From 29da04551dea22179a9771b0bd9c7e848118cbd1 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 1 Oct 2020 04:09:06 -0400 Subject: [PATCH 30/36] [SECURITY SOLUTIONS] Map embeddable working with index patterns selection (#78610) * map working with sourcerer * clean up * fix types * fix unit tests * fix incorrect hight for map * show prompt when no index exists * update unit test * fix update with no index available * fixup * unit test * add unit test Co-authored-by: Angela Chuang Co-authored-by: Elastic Machine --- .../__snapshots__/embedded_map.test.tsx.snap | 42 ++--- .../embeddables/embedded_map.test.tsx | 155 ++++++++++++++++-- .../components/embeddables/embedded_map.tsx | 108 ++++++++---- .../components/embeddables/selector.test.tsx | 24 +++ .../components/embeddables/selector.tsx | 35 ++++ 5 files changed, 289 insertions(+), 75 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/network/components/embeddables/selector.test.tsx create mode 100644 x-pack/plugins/security_solution/public/network/components/embeddables/selector.tsx diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/__snapshots__/embedded_map.test.tsx.snap b/x-pack/plugins/security_solution/public/network/components/embeddables/__snapshots__/embedded_map.test.tsx.snap index 456e07cf9cd159..4c3cbecc7593d2 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/__snapshots__/embedded_map.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/__snapshots__/embedded_map.test.tsx.snap @@ -1,34 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`EmbeddedMapComponent renders correctly against snapshot 1`] = ` - - - - - Map configuration help - - - - } - > - - - - - - + `; diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx index ae0d3c2256e072..219409b10be6c9 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx @@ -4,36 +4,169 @@ * you may not use this file except in compliance with the Elastic License. */ -import { shallow } from 'enzyme'; +import { mount, ReactWrapper, shallow } from 'enzyme'; import React from 'react'; +import * as redux from 'react-redux'; +import { act } from 'react-dom/test-utils'; import '../../../common/mock/match_media'; import { useIndexPatterns } from '../../../common/hooks/use_index_patterns'; +import { TestProviders } from '../../../common/mock'; + import { EmbeddedMapComponent } from './embedded_map'; +import { createEmbeddable } from './embedded_map_helpers'; const mockUseIndexPatterns = useIndexPatterns as jest.Mock; jest.mock('../../../common/hooks/use_index_patterns'); mockUseIndexPatterns.mockImplementation(() => [true, []]); jest.mock('../../../common/lib/kibana'); +jest.mock('./embedded_map_helpers', () => ({ + createEmbeddable: jest.fn(), +})); +jest.mock('../../../common/lib/kibana', () => { + return { + useKibana: jest.fn().mockReturnValue({ + services: { + embeddable: { + EmbeddablePanel: jest.fn(() =>
), + }, + docLinks: { ELASTIC_WEBSITE_URL: 'ELASTIC_WEBSITE_URL' }, + }, + }), + }; +}); + +jest.mock('./index_patterns_missing_prompt', () => { + return { + IndexPatternsMissingPrompt: jest.fn(() =>
), + }; +}); describe('EmbeddedMapComponent', () => { - let setQuery: jest.Mock; + const setQuery: jest.Mock = jest.fn(); + const mockSelector = { + kibanaIndexPatterns: [ + { id: '6f1eeb50-023d-11eb-bcb6-6ba0578012a9', title: 'filebeat-*' }, + { id: '28995490-023d-11eb-bcb6-6ba0578012a9', title: 'auditbeat-*' }, + ], + sourcererScope: { selectedPatterns: ['filebeat-*', 'packetbeat-*'] }, + }; + const mockCreateEmbeddable = { + destroyed: false, + enhancements: { dynamicActions: {} }, + getActionContext: jest.fn(), + getFilterActions: jest.fn(), + id: '70969ddc-4d01-4048-8073-4ea63d595638', + input: { + viewMode: 'view', + title: 'Source -> Destination Point-to-Point Map', + id: '70969ddc-4d01-4048-8073-4ea63d595638', + filters: Array(0), + hidePanelTitles: true, + }, + input$: {}, + isContainer: false, + output: {}, + output$: {}, + parent: undefined, + parentSubscription: undefined, + renderComplete: {}, + runtimeId: 1, + reload: jest.fn(), + setLayerList: jest.fn(), + setEventHandlers: jest.fn(), + setRenderTooltipContent: jest.fn(), + type: 'map', + updateInput: jest.fn(), + }; + const testProps = { + endDate: '2019-08-28T05:50:57.877Z', + filters: [], + query: { query: '', language: 'kuery' }, + setQuery, + startDate: '2019-08-28T05:50:47.877Z', + }; beforeEach(() => { - setQuery = jest.fn(); + setQuery.mockClear(); }); test('renders correctly against snapshot', () => { const wrapper = shallow( - + + + ); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('EmbeddedMapComponent')).toMatchSnapshot(); + }); + + test('renders services.embeddable.EmbeddablePanel', async () => { + const spy = jest.spyOn(redux, 'useSelector'); + spy.mockReturnValue(mockSelector); + + (createEmbeddable as jest.Mock).mockResolvedValue(mockCreateEmbeddable); + + let wrapper: ReactWrapper; + await act(async () => { + wrapper = mount( + + + + ); + }); + + wrapper!.update(); + + expect(wrapper!.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(true); + expect(wrapper!.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(false); + expect(wrapper!.find('[data-test-subj="loading-panel"]').exists()).toEqual(false); + }); + + test('renders IndexPatternsMissingPrompt', async () => { + const spy = jest.spyOn(redux, 'useSelector'); + spy.mockReturnValue({ + ...mockSelector, + kibanaIndexPatterns: [], + }); + + (createEmbeddable as jest.Mock).mockResolvedValue(mockCreateEmbeddable); + + let wrapper: ReactWrapper; + await act(async () => { + wrapper = mount( + + + + ); + }); + + wrapper!.update(); + + expect(wrapper!.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(false); + expect(wrapper!.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(true); + expect(wrapper!.find('[data-test-subj="loading-panel"]').exists()).toEqual(false); + }); + + test('renders Loader', async () => { + const spy = jest.spyOn(redux, 'useSelector'); + spy.mockReturnValue(mockSelector); + + (createEmbeddable as jest.Mock).mockResolvedValue(null); + + let wrapper: ReactWrapper; + await act(async () => { + wrapper = mount( + + + + ); + }); + + wrapper!.update(); + + expect(wrapper!.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(false); + expect(wrapper!.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(false); + expect(wrapper!.find('[data-test-subj="loading-panel"]').exists()).toEqual(true); }); }); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx index 4d96c213818aa5..7ae8aecdab6064 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx @@ -5,27 +5,31 @@ */ import { EuiLink, EuiText } from '@elastic/eui'; -import React, { useEffect, useState } from 'react'; +import deepEqual from 'fast-deep-equal'; +import React, { useEffect, useState, useMemo } from 'react'; import { createPortalNode, InPortal } from 'react-reverse-portal'; import styled, { css } from 'styled-components'; -import { ErrorEmbeddable } from '../../../../../../../src/plugins/embeddable/public'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; -import { getIndexPatternTitleIdMapping } from '../../../common/hooks/api/helpers'; -import { useIndexPatterns } from '../../../common/hooks/use_index_patterns'; +import { useSelector } from 'react-redux'; +import { + ErrorEmbeddable, + isErrorEmbeddable, +} from '../../../../../../../src/plugins/embeddable/public'; import { Loader } from '../../../common/components/loader'; import { displayErrorToast, useStateToaster } from '../../../common/components/toasters'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; import { Embeddable } from './embeddable'; import { EmbeddableHeader } from './embeddable_header'; -import { createEmbeddable, findMatchingIndexPatterns } from './embedded_map_helpers'; +import { createEmbeddable } from './embedded_map_helpers'; import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt'; import { MapToolTip } from './map_tool_tip/map_tool_tip'; import * as i18n from './translations'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { MapEmbeddable } from '../../../../../../plugins/maps/public/embeddable'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; -import { useKibana, useUiSetting$ } from '../../../common/lib/kibana'; +import { useKibana } from '../../../common/lib/kibana'; +import { getDefaultSourcererSelector } from './selector'; +import { getLayerList } from './map_config'; interface EmbeddableMapProps { maintainRatio?: boolean; @@ -86,13 +90,19 @@ export const EmbeddedMapComponent = ({ const [embeddable, setEmbeddable] = React.useState( undefined ); - const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); const [isIndexError, setIsIndexError] = useState(false); const [, dispatchToaster] = useStateToaster(); - const [loadingKibanaIndexPatterns, kibanaIndexPatterns] = useIndexPatterns(); - const [siemDefaultIndices] = useUiSetting$(DEFAULT_INDEX_KEY); + const defaultSourcererScopeSelector = useMemo(getDefaultSourcererSelector, []); + const { kibanaIndexPatterns, sourcererScope } = useSelector( + defaultSourcererScopeSelector, + deepEqual + ); + + const [mapIndexPatterns, setMapIndexPatterns] = useState( + kibanaIndexPatterns.filter((kip) => sourcererScope.selectedPatterns.includes(kip.title)) + ); // This portalNode provided by react-reverse-portal allows us re-parent the MapToolTip within our // own component tree instead of the embeddables (default). This is necessary to have access to @@ -102,27 +112,30 @@ export const EmbeddedMapComponent = ({ const { services } = useKibana(); + useEffect(() => { + setMapIndexPatterns((prevMapIndexPatterns) => { + const newIndexPatterns = kibanaIndexPatterns.filter((kip) => + sourcererScope.selectedPatterns.includes(kip.title) + ); + if (!deepEqual(newIndexPatterns, prevMapIndexPatterns)) { + if (newIndexPatterns.length === 0) { + setIsError(true); + } + return newIndexPatterns; + } + return prevMapIndexPatterns; + }); + }, [kibanaIndexPatterns, sourcererScope.selectedPatterns]); + // Initial Load useEffect useEffect(() => { let isSubscribed = true; async function setupEmbeddable() { - // Ensure at least one `securitySolution:defaultIndex` kibana index pattern exists before creating embeddable - const matchingIndexPatterns = findMatchingIndexPatterns({ - kibanaIndexPatterns, - siemDefaultIndices, - }); - - if (matchingIndexPatterns.length === 0 && isSubscribed) { - setIsLoading(false); - setIsIndexError(true); - return; - } - // Create & set Embeddable try { const embeddableObject = await createEmbeddable( filters, - getIndexPatternTitleIdMapping(matchingIndexPatterns), + mapIndexPatterns, query, startDate, endDate, @@ -131,7 +144,12 @@ export const EmbeddedMapComponent = ({ services.embeddable ); if (isSubscribed) { - setEmbeddable(embeddableObject); + if (mapIndexPatterns.length === 0) { + setIsIndexError(true); + } else { + setEmbeddable(embeddableObject); + setIsIndexError(false); + } } } catch (e) { if (isSubscribed) { @@ -139,19 +157,41 @@ export const EmbeddedMapComponent = ({ setIsError(true); } } - if (isSubscribed) { - setIsLoading(false); - } } - - if (!loadingKibanaIndexPatterns) { + if (embeddable == null && sourcererScope.selectedPatterns.length > 0) { setupEmbeddable(); } + return () => { isSubscribed = false; }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loadingKibanaIndexPatterns, kibanaIndexPatterns]); + }, [ + dispatchToaster, + endDate, + embeddable, + filters, + mapIndexPatterns, + query, + portalNode, + services.embeddable, + sourcererScope.selectedPatterns, + setQuery, + startDate, + ]); + + // update layer with new index patterns + useEffect(() => { + const setLayerList = async () => { + if (embeddable != null) { + // @ts-expect-error + await embeddable.setLayerList(getLayerList(mapIndexPatterns)); + embeddable.reload(); + } + }; + if (embeddable != null && !isErrorEmbeddable(embeddable)) { + setLayerList(); + } + }, [embeddable, mapIndexPatterns]); // queryExpression updated useEffect useEffect(() => { @@ -198,10 +238,10 @@ export const EmbeddedMapComponent = ({ - {embeddable != null ? ( - - ) : !isLoading && isIndexError ? ( + {isIndexError ? ( + ) : embeddable != null ? ( + ) : ( )} diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/selector.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/selector.test.tsx new file mode 100644 index 00000000000000..d5b105dd327987 --- /dev/null +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/selector.test.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { State } from '../../../common/store'; + +import { getDefaultSourcererSelector } from './selector'; + +jest.mock('../../../common/store/sourcerer', () => ({ + sourcererSelectors: { + kibanaIndexPatternsSelector: jest.fn().mockReturnValue(jest.fn()), + scopesSelector: jest.fn().mockReturnValue(jest.fn().mockReturnValue({ default: '' })), + }, +})); + +describe('getDefaultSourcererSelector', () => { + test('Returns correct format', () => { + const mockMapStateToProps = getDefaultSourcererSelector(); + const result = mockMapStateToProps({} as State); + expect(result).toHaveProperty('kibanaIndexPatterns'); + expect(result).toHaveProperty('sourcererScope'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/selector.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/selector.tsx new file mode 100644 index 00000000000000..2d0bc970f0a51a --- /dev/null +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/selector.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { State } from '../../../common/store'; +import { sourcererSelectors } from '../../../common/store/sourcerer'; +import { + KibanaIndexPatterns, + ManageScope, + SourcererScopeName, +} from '../../../common/store/sourcerer/model'; + +export interface DefaultSourcererSelector { + kibanaIndexPatterns: KibanaIndexPatterns; + sourcererScope: ManageScope; +} + +export const getDefaultSourcererSelector = () => { + const getKibanaIndexPatternsSelector = sourcererSelectors.kibanaIndexPatternsSelector(); + const getScopesSelector = sourcererSelectors.scopesSelector(); + + const mapStateToProps = (state: State): DefaultSourcererSelector => { + const kibanaIndexPatterns = getKibanaIndexPatternsSelector(state); + const scope = getScopesSelector(state)[SourcererScopeName.default]; + + return { + kibanaIndexPatterns, + sourcererScope: scope, + }; + }; + + return mapStateToProps; +}; From 07ebb81a79d323445e089d43e9bef04b4578f44d Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 1 Oct 2020 10:16:30 +0200 Subject: [PATCH 31/36] [UX] Improve page-load axis (#78392) Co-authored-by: Elastic Machine --- .../step_definitions/csm/breakdown_filter.ts | 2 +- .../step_definitions/csm/csm_dashboard.ts | 2 +- .../step_definitions/csm/csm_filters.ts | 2 +- .../step_definitions/csm/percentile_select.ts | 2 +- .../csm/service_name_filter.ts | 2 +- .../RumDashboard/Charts/PageLoadDistChart.tsx | 20 +- .../RumDashboard/Charts/PageViewsChart.tsx | 17 +- .../app/RumDashboard/ClientMetrics/index.tsx | 19 +- .../PageLoadDistribution/BreakdownSeries.tsx | 20 +- .../RumDashboard/UXMetrics/KeyUXMetrics.tsx | 3 +- .../__snapshots__/queries.test.ts.snap | 210 +++++++++++++++++- .../lib/rum_client/get_client_metrics.ts | 6 +- .../rum_client/get_page_load_distribution.ts | 110 ++++++--- .../lib/rum_client/get_pl_dist_breakdown.ts | 12 +- .../plugins/apm/server/routes/rum_client.ts | 4 +- 15 files changed, 357 insertions(+), 74 deletions(-) diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts index acfbe6e0a4e78e..342f3e0aa52670 100644 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts +++ b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts @@ -36,7 +36,7 @@ Then(`breakdown series should appear in chart`, () => { cy.get('div.echLegendItem__label', DEFAULT_TIMEOUT).should( 'have.text', - 'ChromeChrome Mobile WebViewSafariFirefoxMobile SafariChrome MobileChrome Mobile iOSOverall' + 'OverallChromeChrome Mobile WebViewSafariFirefoxMobile SafariChrome MobileChrome Mobile iOS' ); }); }); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts index 28af4fd5d8a566..a8edf862ab256c 100644 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts +++ b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts @@ -26,7 +26,7 @@ Given(`a user browses the APM UI application for RUM Data`, () => { }); Then(`should have correct client metrics`, () => { - const metrics = ['4 ms', '0.06 s', '55 ']; + const metrics = ['4 ms', '58 ms', '55']; verifyClientMetrics(metrics, true); }); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts index 75974ef9c202c1..5c2109bb518c2d 100644 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts +++ b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts @@ -56,7 +56,7 @@ Then(/^it filters the client metrics "([^"]*)"$/, (filterName) => { cy.get('.euiStat__title-isLoading').should('not.be.visible'); const data = - filterName === 'os' ? ['5 ms', '0.06 s', '8 '] : ['4 ms', '0.05 s', '28 ']; + filterName === 'os' ? ['5 ms', '64 ms', '8'] : ['4 ms', '55 ms', '28']; verifyClientMetrics(data, true); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts index 4d2ba4d01ae6c5..55c980d5edeb40 100644 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts +++ b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts @@ -18,7 +18,7 @@ When('the user changes the selected percentile', () => { }); Then(`it displays client metric related to that percentile`, () => { - const metrics = ['14 ms', '0.13 s', '55 ']; + const metrics = ['14 ms', '131 ms', '55']; verifyClientMetrics(metrics, false); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts index b3899a5649b720..20c6a3fb72aa99 100644 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts +++ b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts @@ -15,7 +15,7 @@ When('the user changes the selected service name', () => { }); Then(`it displays relevant client metrics`, () => { - const metrics = ['4 ms', '0.06 s', '55 ']; + const metrics = ['4 ms', '58 ms', '55']; verifyClientMetrics(metrics, false); }); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx index 4a5f43dacedf4c..4eb24f8c80b9ae 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx @@ -88,6 +88,10 @@ export function PageLoadDistChart({ const [darkMode] = useUiSetting$('theme:darkMode'); + const euiChartTheme = darkMode + ? EUI_CHARTS_THEME_DARK + : EUI_CHARTS_THEME_LIGHT; + return ( numeral(d).format('0.0') + '%'} + labelFormat={(d) => d + ' %'} /> numeral(d).format('0.0') + ' %'} /> {breakdown && ( ('theme:darkMode'); @@ -83,17 +85,17 @@ export function PageViewsChart({ data, loading }: Props) { return yAccessor; }; + const euiChartTheme = darkMode + ? EUI_CHARTS_THEME_DARK + : EUI_CHARTS_THEME_LIGHT; + return ( {(!loading || data) && ( )} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx index 03f2f31f358174..310c01291aea49 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiToolTip } from '@elastic/eui'; import { useFetcher } from '../../../../hooks/useFetcher'; import { I18LABELS } from '../translations'; import { useUxQuery } from '../hooks/useUxQuery'; +import { formatToSec } from '../UXMetrics/KeyUXMetrics'; import { CsmSharedContext } from '../CsmSharedContext'; const ClFlexGroup = styled(EuiFlexGroup)` @@ -49,14 +50,14 @@ export function ClientMetrics() { const STAT_STYLE = { width: '240px' }; + const pageViewsTotal = data?.pageViews?.value ?? 0; + return ( @@ -64,7 +65,7 @@ export function ClientMetrics() { @@ -73,9 +74,13 @@ export function ClientMetrics() { - <>{numeral(data?.pageViews?.value).format('0 a') ?? '-'} - + pageViewsTotal < 10000 ? ( + numeral(pageViewsTotal).format('0,0') + ) : ( + + <>{numeral(pageViewsTotal).format('0 a')} + + ) } description={I18LABELS.pageViews} isLoading={status !== 'success'} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx index 3463327441b7b5..f348aca495c71d 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx @@ -6,8 +6,13 @@ import { CurveType, Fit, LineSeries, ScaleType } from '@elastic/charts'; import React, { useEffect } from 'react'; +import { + EUI_CHARTS_THEME_DARK, + EUI_CHARTS_THEME_LIGHT, +} from '@elastic/eui/dist/eui_charts_theme'; import { PercentileRange } from './index'; import { useBreakdowns } from './use_breakdowns'; +import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; interface Props { field: string; @@ -22,6 +27,12 @@ export function BreakdownSeries({ percentileRange, onLoadingChange, }: Props) { + const [darkMode] = useUiSetting$('theme:darkMode'); + + const euiChartTheme = darkMode + ? EUI_CHARTS_THEME_DARK + : EUI_CHARTS_THEME_LIGHT; + const { data, status } = useBreakdowns({ field, value, @@ -32,9 +43,11 @@ export function BreakdownSeries({ onLoadingChange(status !== 'success'); }, [status, onLoadingChange]); + // sort index 1 color vizColors1 is already used for overall, + // so don't user that here return ( <> - {data?.map(({ data: seriesData, name }) => ( + {data?.map(({ data: seriesData, name }, sortIndex) => ( ))} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx index 53722658cafefc..5b0e9709d4fa3c 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { EuiFlexItem, EuiStat, EuiFlexGroup } from '@elastic/eui'; +import numeral from '@elastic/numeral'; import { UXMetrics } from './index'; import { FCP_LABEL, @@ -77,7 +78,7 @@ export function KeyUXMetrics({ data, loading }: Props) { diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap index 66cfa954965d22..1c724efac37b22 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap @@ -245,10 +245,214 @@ Object { ], }, }, - "minDuration": Object { - "min": Object { + "loadDistribution": Object { + "percentile_ranks": Object { "field": "transaction.duration.us", - "missing": 0, + "hdr": Object { + "number_of_significant_value_digits": 3, + }, + "keyed": false, + "values": Array [ + 0, + 500000, + 1000000, + 1500000, + 2000000, + 2500000, + 3000000, + 3500000, + 4000000, + 4500000, + 5000000, + 5500000, + 6000000, + 6500000, + 7000000, + 7500000, + 8000000, + 8500000, + 9000000, + 9500000, + 10000000, + 10500000, + 11000000, + 11500000, + 12000000, + 12500000, + 13000000, + 13500000, + 14000000, + 14500000, + 15000000, + 15500000, + 16000000, + 16500000, + 17000000, + 17500000, + 18000000, + 18500000, + 19000000, + 19500000, + 20000000, + 20500000, + 21000000, + 21500000, + 22000000, + 22500000, + 23000000, + 23500000, + 24000000, + 24500000, + 25000000, + 25500000, + 26000000, + 26500000, + 27000000, + 27500000, + 28000000, + 28500000, + 29000000, + 29500000, + 30000000, + 30500000, + 31000000, + 31500000, + 32000000, + 32500000, + 33000000, + 33500000, + 34000000, + 34500000, + 35000000, + 35500000, + 36000000, + 36500000, + 37000000, + 37500000, + 38000000, + 38500000, + 39000000, + 39500000, + 40000000, + 40500000, + 41000000, + 41500000, + 42000000, + 42500000, + 43000000, + 43500000, + 44000000, + 44500000, + 45000000, + 45500000, + 46000000, + 46500000, + 47000000, + 47500000, + 48000000, + 48500000, + 49000000, + 49500000, + 50000000, + 50500000, + 51000000, + 51500000, + 52000000, + 52500000, + 53000000, + 53500000, + 54000000, + 54500000, + 55000000, + 55500000, + 56000000, + 56500000, + 57000000, + 57500000, + 58000000, + 58500000, + 59000000, + 59500000, + 60000000, + 60500000, + 61000000, + 61500000, + 62000000, + 62500000, + 63000000, + 63500000, + 64000000, + 64500000, + 65000000, + 65500000, + 66000000, + 66500000, + 67000000, + 67500000, + 68000000, + 68500000, + 69000000, + 69500000, + 70000000, + 70500000, + 71000000, + 71500000, + 72000000, + 72500000, + 73000000, + 73500000, + 74000000, + 74500000, + 75000000, + 75500000, + 76000000, + 76500000, + 77000000, + 77500000, + 78000000, + 78500000, + 79000000, + 79500000, + 80000000, + 80500000, + 81000000, + 81500000, + 82000000, + 82500000, + 83000000, + 83500000, + 84000000, + 84500000, + 85000000, + 85500000, + 86000000, + 86500000, + 87000000, + 87500000, + 88000000, + 88500000, + 89000000, + 89500000, + 90000000, + 90500000, + 91000000, + 91500000, + 92000000, + 92500000, + 93000000, + 93500000, + 94000000, + 94500000, + 95000000, + 95500000, + 96000000, + 96500000, + 97000000, + 97500000, + 98000000, + 98500000, + 99000000, + ], }, }, }, diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts index a210c32ceb44ee..6566ea4f5e29bf 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts @@ -72,11 +72,9 @@ export async function getClientMetrics({ // Divide by 1000 to convert ms into seconds return { pageViews, - backEnd: { value: (backEnd.values[pkey] || 0) / 1000 }, + backEnd: { value: backEnd.values[pkey] || 0 }, frontEnd: { - value: - ((domInteractive.values[pkey] || 0) - (backEnd.values[pkey] || 0)) / - 1000, + value: (domInteractive.values[pkey] || 0) - (backEnd.values[pkey] || 0), }, }; } diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts index 25de9f06fefc41..5f666feb8a18f2 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts @@ -15,8 +15,6 @@ import { export const MICRO_TO_SEC = 1000000; -const NUMBER_OF_PLD_STEPS = 100; - export function microToSec(val: number) { return Math.round((val / MICRO_TO_SEC + Number.EPSILON) * 100) / 100; } @@ -24,15 +22,31 @@ export function microToSec(val: number) { export const getPLDChartSteps = ({ maxDuration, minDuration, + initStepValue, }: { maxDuration: number; minDuration: number; + initStepValue?: number; }) => { - const stepValue = (maxDuration - minDuration) / NUMBER_OF_PLD_STEPS; - const stepValues = []; - for (let i = 1; i < NUMBER_OF_PLD_STEPS + 1; i++) { - stepValues.push((stepValue * i + minDuration).toFixed(2)); + let stepValue = 0.5; + // if diff is too low, let's lower + // down the steps value to increase steps + if (maxDuration - minDuration <= 5 * MICRO_TO_SEC) { + stepValue = 0.1; + } + + if (initStepValue) { + stepValue = initStepValue; + } + + let initValue = minDuration; + const stepValues = [initValue]; + + while (initValue < maxDuration) { + initValue += stepValue * MICRO_TO_SEC; + stepValues.push(initValue); } + return stepValues; }; @@ -52,16 +66,21 @@ export async function getPageLoadDistribution({ urlQuery, }); + // we will first get 100 steps using 0sec and 50sec duration, + // most web apps will cover this use case + // if 99th percentile is greater than 50sec, + // we will fetch additional 5 steps beyond 99th percentile + let maxDuration = (maxPercentile ? +maxPercentile : 50) * MICRO_TO_SEC; + const minDuration = minPercentile ? +minPercentile * MICRO_TO_SEC : 0; + const stepValues = getPLDChartSteps({ + maxDuration, + minDuration, + }); + const params = mergeProjection(projection, { body: { size: 0, aggs: { - minDuration: { - min: { - field: TRANSACTION_DURATION, - missing: 0, - }, - }, durPercentiles: { percentiles: { field: TRANSACTION_DURATION, @@ -71,6 +90,16 @@ export async function getPageLoadDistribution({ }, }, }, + loadDistribution: { + percentile_ranks: { + field: TRANSACTION_DURATION, + values: stepValues, + keyed: false, + hdr: { + number_of_significant_value_digits: 3, + }, + }, + }, }, }, }); @@ -86,22 +115,40 @@ export async function getPageLoadDistribution({ return null; } - const { durPercentiles, minDuration } = aggregations ?? {}; + const { durPercentiles, loadDistribution } = aggregations ?? {}; - const minPerc = minPercentile - ? +minPercentile * MICRO_TO_SEC - : minDuration?.value ?? 0; + let pageDistVals = loadDistribution?.values ?? []; - const maxPercQuery = durPercentiles?.values['99.0'] ?? 10000; + const maxPercQuery = durPercentiles?.values['99.0'] ?? 0; - const maxPerc = maxPercentile ? +maxPercentile * MICRO_TO_SEC : maxPercQuery; + // we assumed that page load will never exceed 50secs, if 99th percentile is + // greater then let's fetch additional 10 steps, to cover that on the chart + if (maxPercQuery > maxDuration && !maxPercentile) { + const additionalStepsPageVals = await getPercentilesDistribution({ + setup, + maxDuration: maxPercQuery, + // we pass 50sec as min to get next steps + minDuration: maxDuration, + }); - const pageDist = await getPercentilesDistribution({ - setup, - minDuration: minPerc, - maxDuration: maxPerc, + pageDistVals = pageDistVals.concat(additionalStepsPageVals); + maxDuration = maxPercQuery; + } + + // calculate the diff to get actual page load on specific duration value + const pageDist = pageDistVals.map(({ key, value }, index: number, arr) => { + return { + x: microToSec(key), + y: index === 0 ? value : value - arr[index - 1].value, + }; }); + if (pageDist.length > 0) { + while (pageDist[pageDist.length - 1].y === 0) { + pageDist.pop(); + } + } + Object.entries(durPercentiles?.values ?? {}).forEach(([key, val]) => { if (durPercentiles?.values?.[key]) { durPercentiles.values[key] = microToSec(val as number); @@ -111,8 +158,8 @@ export async function getPageLoadDistribution({ return { pageLoadDistribution: pageDist, percentiles: durPercentiles?.values, - minDuration: microToSec(minPerc), - maxDuration: microToSec(maxPerc), + minDuration: microToSec(minDuration), + maxDuration: microToSec(maxDuration), }; } @@ -125,7 +172,11 @@ const getPercentilesDistribution = async ({ minDuration: number; maxDuration: number; }) => { - const stepValues = getPLDChartSteps({ maxDuration, minDuration }); + const stepValues = getPLDChartSteps({ + minDuration: minDuration + 0.5 * MICRO_TO_SEC, + maxDuration, + initStepValue: 0.5, + }); const projection = getRumPageLoadTransactionsProjection({ setup, @@ -153,12 +204,5 @@ const getPercentilesDistribution = async ({ const { aggregations } = await apmEventClient.search(params); - const pageDist = aggregations?.loadDistribution.values ?? []; - - return pageDist.map(({ key, value }, index: number, arr) => { - return { - x: microToSec(key), - y: index === 0 ? value : value - arr[index - 1].value, - }; - }); + return aggregations?.loadDistribution.values ?? []; }; diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts index d59817cc682a94..bebf9c0bc99c92 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts @@ -41,21 +41,21 @@ export const getBreakdownField = (breakdown: string) => { export const getPageLoadDistBreakdown = async ({ setup, - minDuration, - maxDuration, + minPercentile, + maxPercentile, breakdown, urlQuery, }: { setup: Setup & SetupTimeRange & SetupUIFilters; - minDuration: number; - maxDuration: number; + minPercentile: number; + maxPercentile: number; breakdown: string; urlQuery?: string; }) => { // convert secs to micros const stepValues = getPLDChartSteps({ - minDuration: minDuration * MICRO_TO_SEC, - maxDuration: maxDuration * MICRO_TO_SEC, + maxDuration: (maxPercentile ? +maxPercentile : 50) * MICRO_TO_SEC, + minDuration: minPercentile ? +minPercentile * MICRO_TO_SEC : 0, }); const projection = getRumPageLoadTransactionsProjection({ diff --git a/x-pack/plugins/apm/server/routes/rum_client.ts b/x-pack/plugins/apm/server/routes/rum_client.ts index d86069a3ec27a6..2bdfaa1421eea8 100644 --- a/x-pack/plugins/apm/server/routes/rum_client.ts +++ b/x-pack/plugins/apm/server/routes/rum_client.ts @@ -89,8 +89,8 @@ export const rumPageLoadDistBreakdownRoute = createRoute(() => ({ return getPageLoadDistBreakdown({ setup, - minDuration: Number(minPercentile), - maxDuration: Number(maxPercentile), + minPercentile: Number(minPercentile), + maxPercentile: Number(maxPercentile), breakdown, urlQuery, }); From addbdf7cb608ef1b1aeda1d31d0341f6ace02c98 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 1 Oct 2020 10:29:51 +0200 Subject: [PATCH 32/36] [Drilldowns][Docs] Communicate the visualization types that support drilldowns (#78761) --- .../dashboard/dashboard-drilldown.asciidoc | 21 +++++++++++++++++++ docs/user/dashboard/url-drilldown.asciidoc | 16 ++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/docs/user/dashboard/dashboard-drilldown.asciidoc b/docs/user/dashboard/dashboard-drilldown.asciidoc index 84701cae2ecc6a..e50c1281beede5 100644 --- a/docs/user/dashboard/dashboard-drilldown.asciidoc +++ b/docs/user/dashboard/dashboard-drilldown.asciidoc @@ -11,6 +11,26 @@ This example shows a dashboard panel that contains a pie chart with a configured [role="screenshot"] image::images/drilldown_on_piechart.gif[Drilldown on pie chart that navigates to another dashboard] +[float] +[[dashboard-drilldown-supported-panels]] +==== Supported panels + +The following panels support dashboard drilldowns: + +* Lens +* Area +* Data table +* Heat map +* Horizontal bar +* Line +* Maps +* Pie +* TSVB +* Tag cloud +* Timelion +* Vega +* Vertical bar + [float] [[drilldowns-example]] ==== Try it: Create a dashboard drilldown @@ -74,3 +94,4 @@ image::images/drilldown_on_panel.png[Drilldown on pie chart that navigates to an + You are navigated to your destination dashboard. Verify that the search query, filters, and time range are carried over. + diff --git a/docs/user/dashboard/url-drilldown.asciidoc b/docs/user/dashboard/url-drilldown.asciidoc index ee879256a1fae4..620a2d2056bf13 100644 --- a/docs/user/dashboard/url-drilldown.asciidoc +++ b/docs/user/dashboard/url-drilldown.asciidoc @@ -14,6 +14,22 @@ image:images/url_drilldown_go_to_github.gif[Drilldown on pie chart that navigate NOTE: URL drilldown is available with the https://www.elastic.co/subscriptions[Gold subscription] and higher. +[float] +[[url-drilldown-supported-panels]] +==== Supported panels + +The following panels support URL drilldowns: + +* Lens +* Area +* Data table +* Heat map +* Horizontal bar +* Line +* Pie +* Tag cloud +* Vertical bar + [float] [[try-it]] ==== Try it: Create a URL drilldown From d8ded4df6cc173dae624da2408b4657b541f7f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Thu, 1 Oct 2020 10:56:52 +0200 Subject: [PATCH 33/36] Fix ML conditionals links Cypress tests (#78568) --- .../integration/ml_conditional_links.spec.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts index 3b89163392626e..7bdc461a7c73d9 100644 --- a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts @@ -24,20 +24,7 @@ import { mlNetworkSingleIpNullKqlQuery, } from '../urls/ml_conditional_links'; -// FLAKY: https://github.com/elastic/kibana/issues/78512 -// FLAKY: https://github.com/elastic/kibana/issues/78511 -// FLAKY: https://github.com/elastic/kibana/issues/78510 -// FLAKY: https://github.com/elastic/kibana/issues/78509 -// FLAKY: https://github.com/elastic/kibana/issues/78508 -// FLAKY: https://github.com/elastic/kibana/issues/78507 -// FLAKY: https://github.com/elastic/kibana/issues/78506 -// FLAKY: https://github.com/elastic/kibana/issues/78505 -// FLAKY: https://github.com/elastic/kibana/issues/78504 -// FLAKY: https://github.com/elastic/kibana/issues/78503 -// FLAKY: https://github.com/elastic/kibana/issues/78502 -// FLAKY: https://github.com/elastic/kibana/issues/78501 -// FLAKY: https://github.com/elastic/kibana/issues/78500 -describe.skip('ml conditional links', () => { +describe('ml conditional links', () => { it('sets the KQL from a single IP with a value for the query', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery); cy.get(KQL_INPUT) From 9b187c5f81297d6d662a24266594494d7ef628ae Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Thu, 1 Oct 2020 11:10:15 +0200 Subject: [PATCH 34/36] Make the actual Vislib import async (#78949) Co-authored-by: Elastic Machine --- src/plugins/vis_type_vislib/public/vis_controller.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_type_vislib/public/vis_controller.tsx b/src/plugins/vis_type_vislib/public/vis_controller.tsx index c422e9f4f3a0af..3a05030f804cad 100644 --- a/src/plugins/vis_type_vislib/public/vis_controller.tsx +++ b/src/plugins/vis_type_vislib/public/vis_controller.tsx @@ -20,8 +20,6 @@ import $ from 'jquery'; import React, { RefObject } from 'react'; -// @ts-ignore -import { Vis as Vislib } from './vislib/vis'; import { Positions } from './utils/collections'; import { VisTypeVislibDependencies } from './plugin'; import { mountReactNode } from '../../../core/public/utils'; @@ -80,6 +78,9 @@ export const createVislibVisController = (deps: VisTypeVislibDependencies) => { return resolve(); } + // @ts-expect-error + const { Vis: Vislib } = await import('./vislib/vis'); + this.vislibVis = new Vislib(this.chartEl, visParams, deps); this.vislibVis.on('brush', this.vis.API.events.brush); this.vislibVis.on('click', this.vis.API.events.filter); From e836efc3e0f89470d2d207f78b08ce74b5f995fd Mon Sep 17 00:00:00 2001 From: Bohdan Tsymbala Date: Thu, 1 Oct 2020 11:31:11 +0200 Subject: [PATCH 35/36] Changed the color of the confirm button in trusted app deletion dialog. (#78768) * Changed the color of the confirm button in trusted app deletion dialog. * Updated the snapshots. --- .../trusted_app_deletion_dialog.test.tsx.snap | 6 +++--- .../trusted_apps/view/trusted_app_deletion_dialog.tsx | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap index fdb20f229f144b..89f81948e166bf 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap @@ -85,7 +85,7 @@ exports[`TrustedAppDeletionDialog renders correctly when deletion failed 1`] = `