From 589f49f44222d691c3cc14ec96f45bd068794e7c Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 31 Mar 2021 10:04:34 -0500 Subject: [PATCH 01/26] Remove default support for TLS v1.0 and v1.1 (#90511) Co-authored-by: Tyler Smalley Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/migration/migrate_8_0.asciidoc | 9 +++++++++ src/dev/build/tasks/bin/scripts/kibana | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/migration/migrate_8_0.asciidoc b/docs/migration/migrate_8_0.asciidoc index f5ebac1ebf02e..acb343191609d 100644 --- a/docs/migration/migrate_8_0.asciidoc +++ b/docs/migration/migrate_8_0.asciidoc @@ -320,6 +320,15 @@ All supported operating systems support using systemd service files. Any system *Impact:* Any installations using `.deb` or `.rpm` packages using SysV will need to migrate to systemd. +[float] +=== TLS v1.0 and v1.1 are disabled by default + +*Details:* +Support can be re-enabled by setting `--tls-min-1.0` in the `node.options` config file that can be found inside `kibana/config` folder or any other configured with the environment variable `KBN_PATH_CONF` (for example in Debian based system would be `/etc/kibana`). + +*Impact:* +Browser and proxy clients communicating over TLS v1.0 and v1.1. + [float] === Platform removed from root folder name for `.tar.gz` and `.zip` archives diff --git a/src/dev/build/tasks/bin/scripts/kibana b/src/dev/build/tasks/bin/scripts/kibana index 3c12c8bbf58d0..a4fc5385500b5 100755 --- a/src/dev/build/tasks/bin/scripts/kibana +++ b/src/dev/build/tasks/bin/scripts/kibana @@ -26,4 +26,4 @@ if [ -f "${CONFIG_DIR}/node.options" ]; then KBN_NODE_OPTS="$(grep -v ^# < ${CONFIG_DIR}/node.options | xargs)" fi -NODE_OPTIONS="--no-warnings --max-http-header-size=65536 --tls-min-v1.0 $KBN_NODE_OPTS $NODE_OPTIONS" NODE_ENV=production exec "${NODE}" "${DIR}/src/cli/dist" ${@} +NODE_OPTIONS="--no-warnings --max-http-header-size=65536 $KBN_NODE_OPTS $NODE_OPTIONS" NODE_ENV=production exec "${NODE}" "${DIR}/src/cli/dist" ${@} From 9e55b8b6c81683ebd4887b0895ea7ccaec261d88 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 31 Mar 2021 11:25:18 -0400 Subject: [PATCH 02/26] [Fleet] Create Fleet server indices with auto_expand_replicas (#95924) --- .../fleet/server/services/fleet_server/elastic_index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugins/fleet/server/services/fleet_server/elastic_index.ts b/x-pack/plugins/fleet/server/services/fleet_server/elastic_index.ts index 95072d3ae2e2c..b0dce60085529 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server/elastic_index.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server/elastic_index.ts @@ -116,6 +116,11 @@ async function createIndex(esClient: ElasticsearchClient, indexName: string, ind index: indexName, body: { ...indexData, + settings: { + ...(indexData.settings || {}), + auto_expand_replicas: '0-1', + }, + mappings: Object.assign({ ...indexData.mappings, _meta: { ...(indexData.mappings._meta || {}), migrationHash }, From 178c2de5fd767ea6578d8031e01c2fa50c0cba39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ece=20=C3=96zalp?= Date: Wed, 31 Mar 2021 11:37:24 -0400 Subject: [PATCH 03/26] [Security Solution] Populates threat.indicator.event with _source.event (#951) (#95697) * [Security Solution] Add event data to threat.indicator (elastic/security_team/#951) * fixes mappings, updates tests * refactor mappings --- .../indicator_match_rule.spec.ts | 2 +- .../routes/index/get_signals_template.ts | 15 ++- .../enrich_signal_threat_matches.test.ts | 62 +++++++++- .../enrich_signal_threat_matches.ts | 2 + .../tests/create_threat_matching.ts | 110 ++++++++++++++++++ 5 files changed, 186 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts index e1e78f8e310e1..129d592edd264 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts @@ -502,7 +502,7 @@ describe('indicator match', () => { { line: 3, text: - ' "indicator": "{\\"first_seen\\":\\"2021-03-10T08:02:14.000Z\\",\\"file\\":{\\"size\\":80280,\\"pe\\":{},\\"type\\":\\"elf\\",\\"hash\\":{\\"sha256\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"tlsh\\":\\"6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE\\",\\"ssdeep\\":\\"1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL\\",\\"md5\\":\\"9b6c3518a91d23ed77504b5416bfb5b3\\"}},\\"type\\":\\"file\\",\\"matched\\":{\\"atomic\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"field\\":\\"myhash.mysha256\\",\\"id\\":\\"84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f\\",\\"index\\":\\"filebeat-7.12.0-2021.03.10-000001\\",\\"type\\":\\"file\\"}}"', + ' "indicator": "{\\"first_seen\\":\\"2021-03-10T08:02:14.000Z\\",\\"file\\":{\\"size\\":80280,\\"pe\\":{},\\"type\\":\\"elf\\",\\"hash\\":{\\"sha256\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"tlsh\\":\\"6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE\\",\\"ssdeep\\":\\"1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL\\",\\"md5\\":\\"9b6c3518a91d23ed77504b5416bfb5b3\\"}},\\"type\\":\\"file\\",\\"event\\":{\\"reference\\":\\"https://urlhaus-api.abuse.ch/v1/download/a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3/\\",\\"ingested\\":\\"2021-03-10T14:51:09.809069Z\\",\\"created\\":\\"2021-03-10T14:51:07.663Z\\",\\"kind\\":\\"enrichment\\",\\"module\\":\\"threatintel\\",\\"category\\":\\"threat\\",\\"type\\":\\"indicator\\",\\"dataset\\":\\"threatintel.abusemalware\\"},\\"matched\\":{\\"atomic\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"field\\":\\"myhash.mysha256\\",\\"id\\":\\"84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f\\",\\"index\\":\\"filebeat-7.12.0-2021.03.10-000001\\",\\"type\\":\\"file\\"}}"', }, { line: 2, text: ' }' }, ]; 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 c6f432a28aee4..326d5777543be 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 @@ -21,7 +21,7 @@ import ecsMapping from './ecs_mapping.json'; incremented by 10 in order to add "room" for the aforementioned patch release */ -export const SIGNALS_TEMPLATE_VERSION = 25; +export const SIGNALS_TEMPLATE_VERSION = 26; export const MIN_EQL_RULE_INDEX_VERSION = 2; export const getSignalsTemplate = (index: string) => { @@ -45,6 +45,19 @@ export const getSignalsTemplate = (index: string) => { properties: { ...ecsMapping.mappings.properties, signal: signalsMapping.mappings.properties.signal, + threat: { + ...ecsMapping.mappings.properties.threat, + properties: { + ...ecsMapping.mappings.properties.threat.properties, + indicator: { + ...ecsMapping.mappings.properties.threat.properties.indicator, + properties: { + ...ecsMapping.mappings.properties.threat.properties.indicator.properties, + event: ecsMapping.mappings.properties.event, + }, + }, + }, + }, }, _meta: { version: SIGNALS_TEMPLATE_VERSION, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts index 7b3ca099cc93c..7c80572f6b1ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts @@ -83,6 +83,7 @@ describe('buildMatchedIndicator', () => { getThreatListItemMock({ _id: '123', _source: { + event: { dataset: 'abuse.ch', reference: 'https://test.com' }, threat: { indicator: { domain: 'domain_1', other: 'other_1', type: 'type_1' } }, }, }), @@ -117,6 +118,16 @@ describe('buildMatchedIndicator', () => { expect(get(indicator, 'matched.atomic')).toEqual('domain_1'); }); + it('returns event values as a part of threat', () => { + const [indicator] = buildMatchedIndicator({ + queries, + threats, + indicatorPath, + }); + const expectedEvent = threats[0]._source!.event; + expect(get(indicator, 'event')).toEqual(expectedEvent); + }); + it('returns the _id of the matched indicator as matched.id', () => { const [indicator] = buildMatchedIndicator({ queries, @@ -162,12 +173,16 @@ describe('buildMatchedIndicator', () => { getThreatListItemMock({ _id: '123', _source: { - threat: { indicator: { domain: 'domain_1', other: 'other_1', type: 'type_1' } }, + event: { reference: 'https://test.com' }, + threat: { + indicator: { domain: 'domain_1', other: 'other_1', type: 'type_1' }, + }, }, }), getThreatListItemMock({ _id: '456', _source: { + event: { reference: 'https://test2.com' }, threat: { indicator: { domain: 'domain_1', other: 'other_1', type: 'type_1' } }, }, }), @@ -205,6 +220,10 @@ describe('buildMatchedIndicator', () => { }, other: 'other_1', type: 'type_1', + event: { + reference: 'https://test.com', + dataset: 'abuse.ch', + }, }, ]); }); @@ -214,6 +233,9 @@ describe('buildMatchedIndicator', () => { getThreatListItemMock({ _id: '123', _source: { + event: { + reference: 'https://test3.com', + }, 'threat.indicator.domain': 'domain_1', custom: { indicator: { @@ -244,6 +266,9 @@ describe('buildMatchedIndicator', () => { type: 'indicator_type', }, type: 'indicator_type', + event: { + reference: 'https://test3.com', + }, }, ]); }); @@ -307,6 +332,9 @@ describe('buildMatchedIndicator', () => { getThreatListItemMock({ _id: '123', _source: { + event: { + reference: 'https://test4.com', + }, threat: { indicator: [ { domain: 'foo', type: 'first' }, @@ -334,6 +362,9 @@ describe('buildMatchedIndicator', () => { type: 'first', }, type: 'first', + event: { + reference: 'https://test4.com', + }, }, ]); }); @@ -392,6 +423,9 @@ describe('enrichSignalThreatMatches', () => { getThreatListItemMock({ _id: '123', _source: { + event: { + category: 'malware', + }, threat: { indicator: { domain: 'domain_1', other: 'other_1', type: 'type_1' } }, }, }), @@ -419,7 +453,11 @@ describe('enrichSignalThreatMatches', () => { it('preserves existing threat.indicator objects on signals', async () => { const signalHit = getSignalHitMock({ - _source: { '@timestamp': 'mocked', threat: { indicator: [{ existing: 'indicator' }] } }, + _source: { + '@timestamp': 'mocked', + event: { category: 'malware' }, + threat: { indicator: [{ existing: 'indicator' }] }, + }, matched_queries: [matchedQuery], }); const signals = getSignalsResponseMock([signalHit]); @@ -444,6 +482,9 @@ describe('enrichSignalThreatMatches', () => { }, other: 'other_1', type: 'type_1', + event: { + category: 'malware', + }, }, ]); }); @@ -477,7 +518,11 @@ describe('enrichSignalThreatMatches', () => { it('preserves an existing threat.indicator object on signals', async () => { const signalHit = getSignalHitMock({ - _source: { '@timestamp': 'mocked', threat: { indicator: { existing: 'indicator' } } }, + _source: { + '@timestamp': 'mocked', + event: { category: 'virus' }, + threat: { indicator: { existing: 'indicator' } }, + }, matched_queries: [matchedQuery], }); const signals = getSignalsResponseMock([signalHit]); @@ -502,6 +547,9 @@ describe('enrichSignalThreatMatches', () => { }, other: 'other_1', type: 'type_1', + event: { + category: 'malware', + }, }, ]); }); @@ -573,12 +621,14 @@ describe('enrichSignalThreatMatches', () => { getThreatListItemMock({ _id: '123', _source: { + event: { category: 'threat' }, threat: { indicator: { domain: 'domain_1', other: 'other_1', type: 'type_1' } }, }, }), getThreatListItemMock({ _id: '456', _source: { + event: { category: 'bad' }, threat: { indicator: { domain: 'domain_2', other: 'other_2', type: 'type_2' } }, }, }), @@ -622,6 +672,9 @@ describe('enrichSignalThreatMatches', () => { field: 'event.field', type: 'type_1', }, + event: { + category: 'threat', + }, other: 'other_1', type: 'type_1', }, @@ -634,6 +687,9 @@ describe('enrichSignalThreatMatches', () => { field: 'event.other', type: 'type_2', }, + event: { + category: 'bad', + }, other: 'other_2', type: 'type_2', }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts index 83a3ce8cb773f..c26f03d1dd480 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts @@ -57,9 +57,11 @@ export const buildMatchedIndicator = ({ } const atomic = get(matchedThreat?._source, query.value) as unknown; const type = get(indicator, 'type') as unknown; + const event = get(matchedThreat?._source, 'event') as unknown; return { ...indicator, + event, matched: { atomic, field: query.field, id: query.id, index: query.index, type }, }; }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index a7925fa756693..f0b173d2d4c48 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -317,6 +317,16 @@ export default ({ getService }: FtrProviderContext) => { { description: "domain should match the auditbeat hosts' data's source.ip", domain: '159.89.119.67', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.595350Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978783/', + type: 'indicator', + }, first_seen: '2021-01-26T11:09:04.000Z', matched: { atomic: '159.89.119.67', @@ -339,6 +349,16 @@ export default ({ getService }: FtrProviderContext) => { { description: "domain should match the auditbeat hosts' data's source.ip", domain: '159.89.119.67', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.595350Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978783/', + type: 'indicator', + }, first_seen: '2021-01-26T11:09:04.000Z', matched: { atomic: '159.89.119.67', @@ -412,6 +432,16 @@ export default ({ getService }: FtrProviderContext) => { port: 57324, provider: 'geenensp', type: 'url', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.616763Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978782/', + type: 'indicator', + }, }, { description: 'this should match auditbeat/hosts on ip', @@ -426,6 +456,16 @@ export default ({ getService }: FtrProviderContext) => { }, provider: 'other_provider', type: 'ip', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.616763Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978782/', + type: 'indicator', + }, }, ]); }); @@ -492,6 +532,16 @@ export default ({ getService }: FtrProviderContext) => { port: 57324, provider: 'geenensp', type: 'url', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.616763Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978782/', + type: 'indicator', + }, }, // We do not merge matched indicators during enrichment, so in // certain circumstances a given indicator document could appear @@ -512,6 +562,16 @@ export default ({ getService }: FtrProviderContext) => { port: 57324, provider: 'geenensp', type: 'url', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.616763Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978782/', + type: 'indicator', + }, }, { description: 'this should match auditbeat/hosts on ip', @@ -526,6 +586,16 @@ export default ({ getService }: FtrProviderContext) => { }, provider: 'other_provider', type: 'ip', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.616763Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978782/', + type: 'indicator', + }, }, ]); }); @@ -600,6 +670,16 @@ export default ({ getService }: FtrProviderContext) => { full: 'http://159.89.119.67:59600/bin.sh', scheme: 'http', }, + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.595350Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978783/', + type: 'indicator', + }, }, ]); @@ -621,6 +701,16 @@ export default ({ getService }: FtrProviderContext) => { full: 'http://159.89.119.67:59600/bin.sh', scheme: 'http', }, + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.595350Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978783/', + type: 'indicator', + }, }, { description: 'this should match auditbeat/hosts on both port and ip', @@ -636,6 +726,16 @@ export default ({ getService }: FtrProviderContext) => { port: 57324, provider: 'geenensp', type: 'url', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.616763Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978782/', + type: 'indicator', + }, }, { description: 'this should match auditbeat/hosts on both port and ip', @@ -651,6 +751,16 @@ export default ({ getService }: FtrProviderContext) => { port: 57324, provider: 'geenensp', type: 'url', + event: { + category: 'threat', + created: '2021-01-26T11:09:05.529Z', + dataset: 'threatintel.abuseurl', + ingested: '2021-01-26T11:09:06.616763Z', + kind: 'enrichment', + module: 'threatintel', + reference: 'https://urlhaus.abuse.ch/url/978782/', + type: 'indicator', + }, }, ]); }); From 4c0d09acdc82ac84c804ed15184479959c0f4282 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Wed, 31 Mar 2021 19:52:05 +0200 Subject: [PATCH 04/26] Move more Kibana packages to UI shared deps (#95894) * move @kbn/std to kbn-ui-shared-deps * add @elastic/safer-lodash-set to kbn-ui-shared-deps * update limits * Revert "update limits" This reverts commit f396d4764d38f26d5dc1bb9060aab708a53df53d. * decrase limits for Core team owned code --- packages/kbn-optimizer/limits.yml | 16 ++++++++-------- packages/kbn-ui-shared-deps/entry.js | 2 ++ packages/kbn-ui-shared-deps/index.js | 2 ++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index f93849e011d41..056971aa5e984 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -9,7 +9,7 @@ pageLoadAssetSize: charts: 195358 cloud: 21076 console: 46091 - core: 692106 + core: 397521 crossClusterReplication: 65408 dashboard: 374194 dashboardEnhanced: 65646 @@ -24,13 +24,13 @@ pageLoadAssetSize: enterpriseSearch: 35741 esUiShared: 326654 expressions: 224136 - features: 31211 - globalSearch: 43548 - globalSearchBar: 62888 + features: 21723 + globalSearch: 29696 + globalSearchBar: 50403 globalSearchProviders: 25554 graph: 31504 grokdebugger: 26779 - home: 41661 + home: 30182 indexLifecycleManagement: 107090 indexManagement: 140608 indexPatternManagement: 28222 @@ -45,7 +45,7 @@ pageLoadAssetSize: kibanaUtils: 198829 lens: 96624 licenseManagement: 41817 - licensing: 39008 + licensing: 29004 lists: 202261 logstash: 53548 management: 46112 @@ -73,8 +73,8 @@ pageLoadAssetSize: share: 99061 snapshotRestore: 79032 spaces: 387915 - telemetry: 91832 - telemetryManagementSection: 52443 + telemetry: 51957 + telemetryManagementSection: 38586 tileMap: 65337 timelion: 29920 transform: 41007 diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index ede617908fd3d..f14c793d22a09 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -47,3 +47,5 @@ export const LodashFp = require('lodash/fp'); // runtime deps which don't need to be copied across all bundles export const TsLib = require('tslib'); export const KbnAnalytics = require('@kbn/analytics'); +export const KbnStd = require('@kbn/std'); +export const SaferLodashSet = require('@elastic/safer-lodash-set'); diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js index d1217dd8db0d4..0542bc89ff9e4 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/index.js @@ -58,5 +58,7 @@ exports.externals = { */ tslib: '__kbnSharedDeps__.TsLib', '@kbn/analytics': '__kbnSharedDeps__.KbnAnalytics', + '@kbn/std': '__kbnSharedDeps__.KbnStd', + '@elastic/safer-lodash-set': '__kbnSharedDeps__.SaferLodashSet', }; exports.publicPathLoader = require.resolve('./public_path_loader'); From 524ce98805b463104beadd1bc551f6acd8cd9044 Mon Sep 17 00:00:00 2001 From: Aleksandr Maus Date: Wed, 31 Mar 2021 14:04:41 -0400 Subject: [PATCH 05/26] Add user to .fleet-actions mapping (#95935) * Add user to .fleet-actions mapping * Leave only user.id, remove the rest of added fields * Flatten to user_id --- .../services/fleet_server/elasticsearch/fleet_actions.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_actions.json b/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_actions.json index 3008ee74ab50c..94ad02c6d5f18 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_actions.json +++ b/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_actions.json @@ -24,6 +24,9 @@ }, "type": { "type": "keyword" + }, + "user_id" : { + "type": "keyword" } } } From d2e2209cf22b860c1ebf81eef57d85b114b09dbb Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Wed, 31 Mar 2021 14:43:24 -0400 Subject: [PATCH 06/26] [Maps] Make bundle smaller (#95881) --- packages/kbn-optimizer/limits.yml | 2 +- .../VisitorBreakdownMap/EmbeddedMap.tsx | 2 +- .../VisitorBreakdownMap/MapToolTip.tsx | 2 +- .../classes/tooltips/tooltip_property.ts | 2 +- .../embeddable/map_embeddable_factory.ts | 1 - x-pack/plugins/maps/public/index.ts | 4 +- x-pack/plugins/maps/public/kibana_services.ts | 29 +++++++++++---- .../public/lazy_load_bundle/lazy/index.ts | 1 + .../maps/public/maps_vis_type_alias.ts | 6 +-- x-pack/plugins/maps/public/plugin.ts | 37 +++++++++---------- x-pack/plugins/maps/public/url_generator.ts | 8 ++-- .../embeddables/embedded_map_helpers.tsx | 10 ++--- .../network/components/embeddables/types.ts | 3 +- .../location_map/embeddables/embedded_map.tsx | 2 +- .../location_map/embeddables/map_tool_tip.tsx | 3 +- 15 files changed, 60 insertions(+), 52 deletions(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 056971aa5e984..c9c37108acef4 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -49,7 +49,7 @@ pageLoadAssetSize: lists: 202261 logstash: 53548 management: 46112 - maps: 183610 + maps: 80000 mapsLegacy: 87859 mapsLegacyLicensing: 20214 ml: 82187 diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.tsx index ef67501ec761b..1e368b2eb5368 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.tsx @@ -24,7 +24,7 @@ import { import { useLayerList } from './useLayerList'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { RenderTooltipContentParams } from '../../../../../../maps/public'; +import type { RenderTooltipContentParams } from '../../../../../../maps/public'; import { MapToolTip } from './MapToolTip'; import { useMapFilters } from './useMapFilters'; import { EmbeddableStart } from '../../../../../../../../src/plugins/embeddable/public'; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.tsx index 7e6c8ddd493bf..7501d5bfaa2c5 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.tsx @@ -20,7 +20,7 @@ import { TRANSACTION_DURATION_COUNTRY, TRANSACTION_DURATION_REGION, } from './useLayerList'; -import { RenderTooltipContentParams } from '../../../../../../maps/public'; +import type { RenderTooltipContentParams } from '../../../../../../maps/public'; import { I18LABELS } from '../translations'; type MapToolTipProps = Partial; diff --git a/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts b/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts index 5f81a74ab03ce..a8bc5b9a821f0 100644 --- a/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts +++ b/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; import { Filter } from '../../../../../../src/plugins/data/public'; -import { TooltipFeature } from '../../../../../plugins/maps/common/descriptor_types'; +import type { TooltipFeature } from '../../../../../plugins/maps/common/descriptor_types'; export interface ITooltipProperty { getPropertyKey(): string; diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts index b1944f8136709..a4ce76b702d13 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -11,7 +11,6 @@ import { EmbeddableFactoryDefinition, IContainer, } from '../../../../../src/plugins/embeddable/public'; -import '../index.scss'; import { MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; import { getMapEmbeddableDisplayName } from '../../common/i18n_getters'; import { MapByReferenceInput, MapEmbeddableInput, MapByValueInput } from './types'; diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index 3e6cd8d14ad37..dc9cb2d594fe3 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -19,6 +19,8 @@ export const plugin: PluginInitializer = ( export { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; -export { RenderTooltipContentParams } from './classes/tooltips/tooltip_property'; +export type { RenderTooltipContentParams } from './classes/tooltips/tooltip_property'; export { MapsStartApi } from './api'; + +export type { MapEmbeddable, MapEmbeddableInput } from './embeddable'; diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index 6fd14d8d42e18..e4b9397fab8e7 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -5,13 +5,12 @@ * 2.0. */ -import _ from 'lodash'; -import { CoreStart } from 'kibana/public'; +import type { CoreStart } from 'kibana/public'; import type { MapsEmsConfig } from '../../../../src/plugins/maps_ems/public'; -import { MapsConfigType } from '../config'; -import { MapsPluginStartDependencies } from './plugin'; -import { EMSSettings } from '../common/ems_settings'; -import { PaletteRegistry } from '../../../../src/plugins/charts/public'; +import type { MapsConfigType } from '../config'; +import type { MapsPluginStartDependencies } from './plugin'; +import type { EMSSettings } from '../common/ems_settings'; +import type { PaletteRegistry } from '../../../../src/plugins/charts/public'; let kibanaVersion: string; export const setKibanaVersion = (version: string) => (kibanaVersion = version); @@ -75,8 +74,22 @@ export const getEMSSettings = () => { export const getEmsTileLayerId = () => getKibanaCommonConfig().emsTileLayerId; -export const getRegionmapLayers = () => _.get(getKibanaCommonConfig(), 'regionmap.layers', []); -export const getTilemap = () => _.get(getKibanaCommonConfig(), 'tilemap', []); +export const getRegionmapLayers = () => { + const config = getKibanaCommonConfig(); + if (config.regionmap && config.regionmap.layers) { + return config.regionmap.layers; + } else { + return []; + } +}; +export const getTilemap = () => { + const config = getKibanaCommonConfig(); + if (config.tilemap) { + return config.tilemap; + } else { + return {}; + } +}; export const getShareService = () => pluginsStart.share; diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts index 0d908356b714d..85b58da0ab09a 100644 --- a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts +++ b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import '../../index.scss'; export * from '../../embeddable/map_embeddable'; export * from '../../kibana_services'; export { renderApp } from '../../render_app'; diff --git a/x-pack/plugins/maps/public/maps_vis_type_alias.ts b/x-pack/plugins/maps/public/maps_vis_type_alias.ts index a3a8b55745d84..194b4595c0c93 100644 --- a/x-pack/plugins/maps/public/maps_vis_type_alias.ts +++ b/x-pack/plugins/maps/public/maps_vis_type_alias.ts @@ -6,12 +6,12 @@ */ import { i18n } from '@kbn/i18n'; -import { +import type { VisualizationsSetup, VisualizationStage, } from '../../../../src/plugins/visualizations/public'; -import { SavedObject } from '../../../../src/core/types/saved_objects'; -import { MapSavedObject } from '../common/map_saved_object_type'; +import type { SavedObject } from '../../../../src/core/types/saved_objects'; +import type { MapSavedObject } from '../common/map_saved_object_type'; import { APP_ID, APP_ICON, diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index be2e097c71dc5..7ddab6bf509ff 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -5,19 +5,19 @@ * 2.0. */ -import { Setup as InspectorSetupContract } from 'src/plugins/inspector/public'; -import { UiActionsStart } from 'src/plugins/ui_actions/public'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { Start as InspectorStartContract } from 'src/plugins/inspector/public'; -import { DashboardStart } from 'src/plugins/dashboard/public'; -import { +import type { Setup as InspectorSetupContract } from 'src/plugins/inspector/public'; +import type { UiActionsStart } from 'src/plugins/ui_actions/public'; +import type { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import type { Start as InspectorStartContract } from 'src/plugins/inspector/public'; +import type { DashboardStart } from 'src/plugins/dashboard/public'; +import type { AppMountParameters, CoreSetup, CoreStart, Plugin, PluginInitializerContext, - DEFAULT_APP_CATEGORIES, } from '../../../../src/core/public'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; // @ts-ignore import { MapView } from './inspector/views/map_view'; import { @@ -29,8 +29,8 @@ import { } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; import { getMapsVisTypeAlias } from './maps_vis_type_alias'; -import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; -import { +import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; +import type { VisualizationsSetup, VisualizationsStart, } from '../../../../src/plugins/visualizations/public'; @@ -43,28 +43,27 @@ import { } from './url_generator'; import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; -import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; +import type { EmbeddableSetup, EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { MapsXPackConfig, MapsConfigType } from '../config'; import { getAppTitle } from '../common/i18n_getters'; import { lazyLoadMapModules } from './lazy_load_bundle'; import { MapsStartApi } from './api'; import { createLayerDescriptors, registerLayerWizard, registerSource } from './api'; -import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public'; -import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; +import type { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public'; import type { MapsEmsPluginSetup } from '../../../../src/plugins/maps_ems/public'; -import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; -import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; -import { FileUploadPluginStart } from '../../file_upload/public'; -import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public'; -import { PresentationUtilPluginStart } from '../../../../src/plugins/presentation_util/public'; +import type { DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import type { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; +import type { FileUploadPluginStart } from '../../file_upload/public'; +import type { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public'; +import type { PresentationUtilPluginStart } from '../../../../src/plugins/presentation_util/public'; import { getIsEnterprisePlus, registerLicensedFeatures, setLicensingPluginStart, } from './licensed_features'; import { EMSSettings } from '../common/ems_settings'; -import { SavedObjectTaggingPluginStart } from '../../saved_objects_tagging/public'; -import { ChartsPluginStart } from '../../../../src/plugins/charts/public'; +import type { SavedObjectTaggingPluginStart } from '../../saved_objects_tagging/public'; +import type { ChartsPluginStart } from '../../../../src/plugins/charts/public'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; diff --git a/x-pack/plugins/maps/public/url_generator.ts b/x-pack/plugins/maps/public/url_generator.ts index c82af369fe113..9f28b388c4756 100644 --- a/x-pack/plugins/maps/public/url_generator.ts +++ b/x-pack/plugins/maps/public/url_generator.ts @@ -6,17 +6,17 @@ */ import rison from 'rison-node'; -import { +import type { TimeRange, Filter, Query, - esFilters, QueryState, RefreshInterval, } from '../../../../src/plugins/data/public'; +import { esFilters } from '../../../../src/plugins/data/public'; import { setStateToKbnUrl } from '../../../../src/plugins/kibana_utils/public'; -import { UrlGeneratorsDefinition } from '../../../../src/plugins/share/public'; -import { LayerDescriptor } from '../common/descriptor_types'; +import type { UrlGeneratorsDefinition } from '../../../../src/plugins/share/public'; +import type { LayerDescriptor } from '../common/descriptor_types'; import { INITIAL_LAYERS_KEY } from '../common/constants'; import { lazyLoadMapModules } from './lazy_load_bundle'; diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx index eceea1de4edc0..297746fd23632 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx @@ -12,15 +12,11 @@ import minimatch from 'minimatch'; import { IndexPatternMapping } from './types'; import { getLayerList } from './map_config'; import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/public'; -import { +import type { + RenderTooltipContentParams, MapEmbeddable, MapEmbeddableInput, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../plugins/maps/public/embeddable'; -import { - RenderTooltipContentParams, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../plugins/maps/public/classes/tooltips/tooltip_property'; +} from '../../../../../../plugins/maps/public'; import * as i18n from './translations'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/types.ts b/x-pack/plugins/security_solution/public/network/components/embeddables/types.ts index 7d9c66261924b..6317cad7f8d98 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/types.ts +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/types.ts @@ -5,8 +5,7 @@ * 2.0. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { RenderTooltipContentParams } from '../../../../../maps/public/classes/tooltips/tooltip_property'; +import type { RenderTooltipContentParams } from '../../../../../maps/public'; export interface IndexPatternMapping { title: string; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/embedded_map.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/embedded_map.tsx index f2da38091e37f..6706a435c7b6b 100644 --- a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/embedded_map.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/embedded_map.tsx @@ -26,7 +26,7 @@ import { import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../maps/public'; import { MapToolTipComponent } from './map_tool_tip'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { RenderTooltipContentParams } from '../../../../../../../maps/public/classes/tooltips/tooltip_property'; +import type { RenderTooltipContentParams } from '../../../../../../../maps/public/classes/tooltips/tooltip_property'; export interface EmbeddedMapProps { upPoints: LocationPoint[]; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_tool_tip.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_tool_tip.tsx index f2d1227fe870e..c03ed94f8c544 100644 --- a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_tool_tip.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_tool_tip.tsx @@ -22,8 +22,7 @@ import { AppState } from '../../../../../state'; import { monitorLocationsSelector } from '../../../../../state/selectors'; import { useMonitorId } from '../../../../../hooks'; import { MonitorLocation } from '../../../../../../common/runtime_types/monitor'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { RenderTooltipContentParams } from '../../../../../../../maps/public/classes/tooltips/tooltip_property'; +import type { RenderTooltipContentParams } from '../../../../../../../maps/public'; import { formatAvailabilityValue } from '../../availability_reporting/availability_reporting'; import { LastCheckLabel } from '../../translations'; From f7caf44876d3ff604fe7270cdbad82211c3816b4 Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Wed, 31 Mar 2021 14:10:46 -0500 Subject: [PATCH 07/26] [Fleet] Match telemetry key names to UI agent states (#95567) --- .../server/collectors/agent_collectors.ts | 28 ++- .../fleet/server/collectors/register.ts | 34 ++- .../fleet/server/services/agents/status.ts | 12 +- .../schema/xpack_plugins.json | 212 +++++++++++------- .../apis/agents/status.ts | 1 + 5 files changed, 186 insertions(+), 101 deletions(-) diff --git a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts index de16f6555d4bd..0eb392e784334 100644 --- a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts +++ b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts @@ -13,10 +13,11 @@ import * as AgentService from '../services/agents'; import { isFleetServerSetup } from '../services/fleet_server'; export interface AgentUsage { - total: number; - online: number; - error: number; + total_enrolled: number; + healthy: number; + unhealthy: number; offline: number; + total_all_statuses: number; } export const getAgentUsage = async ( @@ -27,21 +28,26 @@ export const getAgentUsage = async ( // TODO: unsure if this case is possible at all. if (!soClient || !esClient || !(await isFleetServerSetup())) { return { - total: 0, - online: 0, - error: 0, + total_enrolled: 0, + healthy: 0, + unhealthy: 0, offline: 0, + total_all_statuses: 0, }; } - const { total, online, error, offline } = await AgentService.getAgentStatusForAgentPolicy( - soClient, - esClient - ); - return { + const { total, + inactive, online, error, offline, + } = await AgentService.getAgentStatusForAgentPolicy(soClient, esClient); + return { + total_enrolled: total, + healthy: online, + unhealthy: error, + offline, + total_all_statuses: total + inactive, }; }; diff --git a/x-pack/plugins/fleet/server/collectors/register.ts b/x-pack/plugins/fleet/server/collectors/register.ts index 7992d54d1dfad..842bb95fe813f 100644 --- a/x-pack/plugins/fleet/server/collectors/register.ts +++ b/x-pack/plugins/fleet/server/collectors/register.ts @@ -49,10 +49,36 @@ export function registerFleetUsageCollector( schema: { agents_enabled: { type: 'boolean' }, agents: { - total: { type: 'long' }, - online: { type: 'long' }, - error: { type: 'long' }, - offline: { type: 'long' }, + total_enrolled: { + type: 'long', + _meta: { + description: 'The total number of enrolled agents, in any state', + }, + }, + healthy: { + type: 'long', + _meta: { + description: 'The total number of enrolled agents in a healthy state', + }, + }, + unhealthy: { + type: 'long', + _meta: { + description: 'The total number of enrolled agents in an unhealthy state', + }, + }, + offline: { + type: 'long', + _meta: { + description: 'The total number of enrolled agents currently offline', + }, + }, + total_all_statuses: { + type: 'long', + _meta: { + description: 'The total number of agents in any state, both enrolled and inactive', + }, + }, }, packages: { type: 'array', diff --git a/x-pack/plugins/fleet/server/services/agents/status.ts b/x-pack/plugins/fleet/server/services/agents/status.ts index f3fb01655974e..737b6874a8133 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.ts @@ -55,17 +55,18 @@ export async function getAgentStatusForAgentPolicy( agentPolicyId?: string, filterKuery?: string ) { - const [all, online, error, offline, updating] = await pMap( + const [all, allActive, online, error, offline, updating] = await pMap( [ - undefined, + undefined, // All agents, including inactive + undefined, // All active agents AgentStatusKueryHelper.buildKueryForOnlineAgents(), AgentStatusKueryHelper.buildKueryForErrorAgents(), AgentStatusKueryHelper.buildKueryForOfflineAgents(), AgentStatusKueryHelper.buildKueryForUpdatingAgents(), ], - (kuery) => + (kuery, index) => getAgentsByKuery(esClient, { - showInactive: false, + showInactive: index === 0, perPage: 0, page: 1, kuery: joinKuerys( @@ -84,7 +85,8 @@ export async function getAgentStatusForAgentPolicy( return { events: await getEventsCount(soClient, agentPolicyId), - total: all.total, + total: allActive.total, + inactive: all.total - allActive.total, online: online.total, error: error.total, offline: offline.total, diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index ef09937da3fbc..e1e0711c2bb2c 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -1837,17 +1837,35 @@ }, "agents": { "properties": { - "total": { - "type": "long" + "total_enrolled": { + "type": "long", + "_meta": { + "description": "The total number of enrolled agents, in any state" + } }, - "online": { - "type": "long" + "healthy": { + "type": "long", + "_meta": { + "description": "The total number of enrolled agents in a healthy state" + } }, - "error": { - "type": "long" + "unhealthy": { + "type": "long", + "_meta": { + "description": "The total number of enrolled agents in an unhealthy state" + } }, "offline": { - "type": "long" + "type": "long", + "_meta": { + "description": "The total number of enrolled agents currently offline" + } + }, + "total_all_statuses": { + "type": "long", + "_meta": { + "description": "The total number of agents in any state, both enrolled and inactive" + } } } }, @@ -1978,6 +1996,42 @@ "xy_layer_added": { "type": "long" }, + "open_field_editor_edit": { + "type": "long", + "_meta": { + "description": "Number of times the user opened the editor flyout to edit a field from within Lens." + } + }, + "open_field_editor_add": { + "type": "long", + "_meta": { + "description": "Number of times the user opened the editor flyout to add a field from within Lens." + } + }, + "save_field_edit": { + "type": "long", + "_meta": { + "description": "Number of times the user edited a field from within Lens." + } + }, + "save_field_add": { + "type": "long", + "_meta": { + "description": "Number of times the user added a field from within Lens." + } + }, + "open_field_delete_modal": { + "type": "long", + "_meta": { + "description": "Number of times the user opened the field delete modal from within Lens." + } + }, + "delete_field": { + "type": "long", + "_meta": { + "description": "Number of times the user deleted a field from within Lens." + } + }, "indexpattern_dimension_operation_terms": { "type": "long", "_meta": { @@ -2034,70 +2088,50 @@ }, "indexpattern_dimension_operation_range": { "type": "long", - "_meta": { "description": "Number of times the range function was selected" } + "_meta": { + "description": "Number of times the range function was selected" + } }, "indexpattern_dimension_operation_median": { "type": "long", - "_meta": { "description": "Number of times the median function was selected" } + "_meta": { + "description": "Number of times the median function was selected" + } }, "indexpattern_dimension_operation_percentile": { - "type": "long", - "_meta": { "description": "Number of times the percentile function was selected" } - }, - "indexpattern_dimension_operation_last_value": { - "type": "long", - "_meta": { "description": "Number of times the last value function was selected" } - }, - "indexpattern_dimension_operation_cumulative_sum": { - "type": "long", - "_meta": { "description": "Number of times the cumulative sum function was selected" } - }, - "indexpattern_dimension_operation_counter_rate": { - "type": "long", - "_meta": { "description": "Number of times the counter rate function was selected" } - }, - "indexpattern_dimension_operation_derivative": { - "type": "long", - "_meta": { "description": "Number of times the derivative function was selected" } - }, - "indexpattern_dimension_operation_moving_average": { - "type": "long", - "_meta": { "description": "Number of times the moving average function was selected" } - }, - "open_field_editor_edit": { "type": "long", "_meta": { - "description": "Number of times the user opened the editor flyout to edit a field from within Lens." + "description": "Number of times the percentile function was selected" } }, - "open_field_editor_add": { + "indexpattern_dimension_operation_last_value": { "type": "long", "_meta": { - "description": "Number of times the user opened the editor flyout to add a field from within Lens." + "description": "Number of times the last value function was selected" } }, - "save_field_edit": { + "indexpattern_dimension_operation_cumulative_sum": { "type": "long", "_meta": { - "description": "Number of times the user edited a field from within Lens." + "description": "Number of times the cumulative sum function was selected" } }, - "save_field_add": { + "indexpattern_dimension_operation_counter_rate": { "type": "long", "_meta": { - "description": "Number of times the user added a field from within Lens." + "description": "Number of times the counter rate function was selected" } }, - "open_field_delete_modal": { + "indexpattern_dimension_operation_derivative": { "type": "long", "_meta": { - "description": "Number of times the user opened the field delete modal from within Lens." + "description": "Number of times the derivative function was selected" } }, - "delete_field": { + "indexpattern_dimension_operation_moving_average": { "type": "long", "_meta": { - "description": "Number of times the user deleted a field from within Lens." + "description": "Number of times the moving average function was selected" } } } @@ -2185,6 +2219,42 @@ "xy_layer_added": { "type": "long" }, + "open_field_editor_edit": { + "type": "long", + "_meta": { + "description": "Number of times the user opened the editor flyout to edit a field from within Lens." + } + }, + "open_field_editor_add": { + "type": "long", + "_meta": { + "description": "Number of times the user opened the editor flyout to add a field from within Lens." + } + }, + "save_field_edit": { + "type": "long", + "_meta": { + "description": "Number of times the user edited a field from within Lens." + } + }, + "save_field_add": { + "type": "long", + "_meta": { + "description": "Number of times the user added a field from within Lens." + } + }, + "open_field_delete_modal": { + "type": "long", + "_meta": { + "description": "Number of times the user opened the field delete modal from within Lens." + } + }, + "delete_field": { + "type": "long", + "_meta": { + "description": "Number of times the user deleted a field from within Lens." + } + }, "indexpattern_dimension_operation_terms": { "type": "long", "_meta": { @@ -2241,70 +2311,50 @@ }, "indexpattern_dimension_operation_range": { "type": "long", - "_meta": { "description": "Number of times the range function was selected" } + "_meta": { + "description": "Number of times the range function was selected" + } }, "indexpattern_dimension_operation_median": { "type": "long", - "_meta": { "description": "Number of times the median function was selected" } + "_meta": { + "description": "Number of times the median function was selected" + } }, "indexpattern_dimension_operation_percentile": { - "type": "long", - "_meta": { "description": "Number of times the percentile function was selected" } - }, - "indexpattern_dimension_operation_last_value": { - "type": "long", - "_meta": { "description": "Number of times the last value function was selected" } - }, - "indexpattern_dimension_operation_cumulative_sum": { - "type": "long", - "_meta": { "description": "Number of times the cumulative sum function was selected" } - }, - "indexpattern_dimension_operation_counter_rate": { - "type": "long", - "_meta": { "description": "Number of times the counter rate function was selected" } - }, - "indexpattern_dimension_operation_derivative": { - "type": "long", - "_meta": { "description": "Number of times the derivative function was selected" } - }, - "indexpattern_dimension_operation_moving_average": { - "type": "long", - "_meta": { "description": "Number of times the moving average function was selected" } - }, - "open_field_editor_edit": { "type": "long", "_meta": { - "description": "Number of times the user opened the editor flyout to edit a field from within Lens." + "description": "Number of times the percentile function was selected" } }, - "open_field_editor_add": { + "indexpattern_dimension_operation_last_value": { "type": "long", "_meta": { - "description": "Number of times the user opened the editor flyout to add a field from within Lens." + "description": "Number of times the last value function was selected" } }, - "save_field_edit": { + "indexpattern_dimension_operation_cumulative_sum": { "type": "long", "_meta": { - "description": "Number of times the user edited a field from within Lens." + "description": "Number of times the cumulative sum function was selected" } }, - "save_field_add": { + "indexpattern_dimension_operation_counter_rate": { "type": "long", "_meta": { - "description": "Number of times the user added a field from within Lens." + "description": "Number of times the counter rate function was selected" } }, - "open_field_delete_modal": { + "indexpattern_dimension_operation_derivative": { "type": "long", "_meta": { - "description": "Number of times the user opened the field delete modal from within Lens." + "description": "Number of times the derivative function was selected" } }, - "delete_field": { + "indexpattern_dimension_operation_moving_average": { "type": "long", "_meta": { - "description": "Number of times the user deleted a field from within Lens." + "description": "Number of times the moving average function was selected" } } } diff --git a/x-pack/test/fleet_api_integration/apis/agents/status.ts b/x-pack/test/fleet_api_integration/apis/agents/status.ts index 3245b9a459fb1..f79ff15b64d33 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/status.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/status.ts @@ -79,6 +79,7 @@ export default function ({ getService }: FtrProviderContext) { offline: 1, updating: 1, other: 1, + inactive: 0, }, }); }); From b531d2836496f5b8e9e08f44005dce8a28cae250 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 31 Mar 2021 12:29:07 -0700 Subject: [PATCH 08/26] Add "Include aliases" toggle to the Restore Snapshot Wizard (#95882) * Add support for includeAliases to restore API endpoint, with unit tests. * Remove unused deserializeRestoreSettings function. * Add 'Include aliases' option to the UI, with default value of true. * Add client integration test. --- .../helpers/http_requests.ts | 9 ++++ .../helpers/restore_snapshot.helpers.ts | 26 ++++++++++ .../restore_snapshot.test.ts | 32 ++++++++++-- .../snapshot_restore/common/lib/index.ts | 5 +- .../restore_settings_serialization.test.ts | 50 ++----------------- .../lib/restore_settings_serialization.ts | 28 +---------- .../snapshot_restore/common/types/restore.ts | 2 + .../restore_snapshot_form.tsx | 2 + .../steps/step_logistics/step_logistics.tsx | 36 +++++++++++++ .../server/routes/api/validate_schemas.ts | 1 + 10 files changed, 111 insertions(+), 80 deletions(-) diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts index 335b18de92850..45b8b23cae477 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts @@ -108,6 +108,14 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { ]); }; + const setRestoreSnapshotResponse = (response?: HttpResponse) => { + server.respondWith('POST', `${API_BASE_PATH}restore/:repository/:snapshot`, [ + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(response), + ]); + }; + return { setLoadRepositoriesResponse, setLoadRepositoryTypesResponse, @@ -119,6 +127,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { setAddPolicyResponse, setGetPolicyResponse, setCleanupRepositoryResponse, + setRestoreSnapshotResponse, }; }; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/restore_snapshot.helpers.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/restore_snapshot.helpers.ts index c0ffae81a4258..5bc970a1143a4 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/restore_snapshot.helpers.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/restore_snapshot.helpers.ts @@ -25,6 +25,7 @@ const initTestBed = registerTestBed( const setupActions = (testBed: TestBed) => { const { find, component, form } = testBed; + return { findDataStreamCallout() { return find('dataStreamWarningCallOut'); @@ -37,6 +38,28 @@ const setupActions = (testBed: TestBed) => { component.update(); }, + + toggleIncludeAliases() { + act(() => { + form.toggleEuiSwitch('includeAliasesSwitch'); + }); + + component.update(); + }, + + goToStep(step: number) { + while (--step > 0) { + find('nextButton').simulate('click'); + } + component.update(); + }, + + async clickRestore() { + await act(async () => { + find('restoreButton').simulate('click'); + }); + component.update(); + }, }; }; @@ -58,5 +81,8 @@ export const setup = async (): Promise => { export type RestoreSnapshotFormTestSubject = | 'snapshotRestoreStepLogistics' | 'includeGlobalStateSwitch' + | 'includeAliasesSwitch' + | 'nextButton' + | 'restoreButton' | 'systemIndicesInfoCallOut' | 'dataStreamWarningCallOut'; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/restore_snapshot.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/restore_snapshot.test.ts index 2fecce36f09df..9f12415b70a9f 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/restore_snapshot.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/restore_snapshot.test.ts @@ -33,7 +33,7 @@ describe('', () => { testBed.component.update(); }); - it('shows the data streams warning when the snapshot has data streams', () => { + test('shows the data streams warning when the snapshot has data streams', () => { const { exists } = testBed; expect(exists('dataStreamWarningCallOut')).toBe(true); }); @@ -49,7 +49,7 @@ describe('', () => { testBed.component.update(); }); - it('hides the data streams warning when the snapshot has data streams', () => { + test('hides the data streams warning when the snapshot has data streams', () => { const { exists } = testBed; expect(exists('dataStreamWarningCallOut')).toBe(false); }); @@ -65,7 +65,7 @@ describe('', () => { testBed.component.update(); }); - it('shows an info callout when include_global_state is enabled', () => { + test('shows an info callout when include_global_state is enabled', () => { const { exists, actions } = testBed; expect(exists('systemIndicesInfoCallOut')).toBe(false); @@ -75,4 +75,30 @@ describe('', () => { expect(exists('systemIndicesInfoCallOut')).toBe(true); }); }); + + // NOTE: This suite can be expanded to simulate the user setting non-default values for all of + // the form controls and asserting that the correct payload is sent to the API. + describe('include aliases', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setGetSnapshotResponse(fixtures.getSnapshot()); + httpRequestsMockHelpers.setRestoreSnapshotResponse({}); + + await act(async () => { + testBed = await setup(); + }); + + testBed.component.update(); + }); + + test('is sent to the API', async () => { + const { actions } = testBed; + actions.toggleIncludeAliases(); + actions.goToStep(3); + await actions.clickRestore(); + + const expectedPayload = { includeAliases: false }; + const latestRequest = server.requests[server.requests.length - 1]; + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expectedPayload); + }); + }); }); diff --git a/x-pack/plugins/snapshot_restore/common/lib/index.ts b/x-pack/plugins/snapshot_restore/common/lib/index.ts index a375709cee7c5..fc8015c5b807b 100644 --- a/x-pack/plugins/snapshot_restore/common/lib/index.ts +++ b/x-pack/plugins/snapshot_restore/common/lib/index.ts @@ -6,10 +6,7 @@ */ export { flatten } from './flatten'; -export { - deserializeRestoreSettings, - serializeRestoreSettings, -} from './restore_settings_serialization'; +export { serializeRestoreSettings } from './restore_settings_serialization'; export { deserializeSnapshotDetails, deserializeSnapshotConfig, diff --git a/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.test.ts b/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.test.ts index bb640000cc89a..3a78001c742ff 100644 --- a/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.test.ts +++ b/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.test.ts @@ -5,10 +5,7 @@ * 2.0. */ -import { - deserializeRestoreSettings, - serializeRestoreSettings, -} from './restore_settings_serialization'; +import { serializeRestoreSettings } from './restore_settings_serialization'; describe('restore_settings_serialization()', () => { it('should serialize blank restore settings', () => { @@ -56,6 +53,7 @@ describe('restore_settings_serialization()', () => { indexSettings: '{"modified_setting":123}', ignoreIndexSettings: ['setting1'], ignoreUnavailable: true, + includeAliases: true, }) ).toEqual({ indices: ['foo', 'bar'], @@ -66,6 +64,7 @@ describe('restore_settings_serialization()', () => { index_settings: { modified_setting: 123 }, ignore_index_settings: ['setting1'], ignore_unavailable: true, + include_aliases: true, }); }); @@ -76,47 +75,4 @@ describe('restore_settings_serialization()', () => { }) ).toEqual({}); }); - - it('should deserialize blank restore settings', () => { - expect(deserializeRestoreSettings({})).toEqual({}); - }); - - it('should deserialize partial restore settings', () => { - expect(deserializeRestoreSettings({})).toEqual({}); - expect( - deserializeRestoreSettings({ - indices: ['foo', 'bar'], - ignore_index_settings: ['setting1'], - partial: true, - }) - ).toEqual({ - indices: ['foo', 'bar'], - ignoreIndexSettings: ['setting1'], - partial: true, - }); - }); - - it('should deserialize full restore settings', () => { - expect( - deserializeRestoreSettings({ - indices: ['foo', 'bar'], - rename_pattern: 'capture_pattern', - rename_replacement: 'replacement_pattern', - include_global_state: true, - partial: true, - index_settings: { modified_setting: 123 }, - ignore_index_settings: ['setting1'], - ignore_unavailable: true, - }) - ).toEqual({ - indices: ['foo', 'bar'], - renamePattern: 'capture_pattern', - renameReplacement: 'replacement_pattern', - includeGlobalState: true, - partial: true, - indexSettings: '{"modified_setting":123}', - ignoreIndexSettings: ['setting1'], - ignoreUnavailable: true, - }); - }); }); diff --git a/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.ts b/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.ts index 5e026246c77b9..c017bc721884c 100644 --- a/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.ts +++ b/x-pack/plugins/snapshot_restore/common/lib/restore_settings_serialization.ts @@ -26,6 +26,7 @@ export function serializeRestoreSettings(restoreSettings: RestoreSettings): Rest indexSettings, ignoreIndexSettings, ignoreUnavailable, + includeAliases, } = restoreSettings; let parsedIndexSettings: RestoreSettingsEs['index_settings'] | undefined; @@ -47,32 +48,7 @@ export function serializeRestoreSettings(restoreSettings: RestoreSettings): Rest index_settings: parsedIndexSettings, ignore_index_settings: ignoreIndexSettings, ignore_unavailable: ignoreUnavailable, - }; - - return removeUndefinedSettings(settings); -} - -export function deserializeRestoreSettings(restoreSettingsEs: RestoreSettingsEs): RestoreSettings { - const { - indices, - rename_pattern: renamePattern, - rename_replacement: renameReplacement, - include_global_state: includeGlobalState, - partial, - index_settings: indexSettings, - ignore_index_settings: ignoreIndexSettings, - ignore_unavailable: ignoreUnavailable, - } = restoreSettingsEs; - - const settings: RestoreSettings = { - indices, - renamePattern, - renameReplacement, - includeGlobalState, - partial, - indexSettings: indexSettings ? JSON.stringify(indexSettings) : undefined, - ignoreIndexSettings, - ignoreUnavailable, + include_aliases: includeAliases, }; return removeUndefinedSettings(settings); diff --git a/x-pack/plugins/snapshot_restore/common/types/restore.ts b/x-pack/plugins/snapshot_restore/common/types/restore.ts index 1bbd5cdd5a56c..9e9b91de1859e 100644 --- a/x-pack/plugins/snapshot_restore/common/types/restore.ts +++ b/x-pack/plugins/snapshot_restore/common/types/restore.ts @@ -14,6 +14,7 @@ export interface RestoreSettings { indexSettings?: string; ignoreIndexSettings?: string[]; ignoreUnavailable?: boolean; + includeAliases?: boolean; } export interface RestoreSettingsEs { @@ -25,6 +26,7 @@ export interface RestoreSettingsEs { index_settings?: { [key: string]: any }; ignore_index_settings?: string[]; ignore_unavailable?: boolean; + include_aliases?: boolean; } export interface SnapshotRestore { diff --git a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/restore_snapshot_form.tsx b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/restore_snapshot_form.tsx index f672300db8821..82ace79f49f5d 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/restore_snapshot_form.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/restore_snapshot_form.tsx @@ -140,6 +140,7 @@ export const RestoreSnapshotForm: React.FunctionComponent = ({ iconType="arrowRight" onClick={() => onNext()} disabled={!validation.isValid} + data-test-subj="nextButton" > = ({ iconType="check" onClick={() => executeRestore()} isLoading={isSaving} + data-test-subj="restoreButton" > {isSaving ? ( = renameReplacement, partial, includeGlobalState, + includeAliases, } = restoreSettings; // States for choosing all indices, or a subset, including caching previously chosen subset list @@ -625,6 +626,41 @@ export const RestoreSnapshotStepLogistics: React.FunctionComponent = /> + + {/* Include aliases */} + +

+ +

+ + } + description={ + + } + fullWidth + > + + + } + checked={includeAliases === undefined ? true : includeAliases} + onChange={(e) => updateRestoreSettings({ includeAliases: e.target.checked })} + data-test-subj="includeAliasesSwitch" + /> + +
); }; diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts index fe156f6ba9750..af31466c2cefe 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts @@ -176,4 +176,5 @@ export const restoreSettingsSchema = schema.object({ indexSettings: schema.maybe(schema.string()), ignoreIndexSettings: schema.maybe(schema.arrayOf(schema.string())), ignoreUnavailable: schema.maybe(schema.boolean()), + includeAliases: schema.maybe(schema.boolean()), }); From fe17879ae39d978dbd66b29772ed4fa67309696f Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Wed, 31 Mar 2021 15:30:50 -0400 Subject: [PATCH 09/26] [Time to Visualize] Allow By Value Flow Without Visualize Save Permissions (#95951) * Made sure users can use by value workflow without visualize save permissions --- .../actions/add_to_library_action.test.tsx | 50 +++- .../actions/add_to_library_action.tsx | 18 +- .../actions/clone_panel_action.tsx | 3 +- src/plugins/dashboard/public/plugin.tsx | 11 +- src/plugins/dashboard/public/services/core.ts | 1 + .../saved_object_save_modal_dashboard.tsx | 10 +- ..._save_modal_dashboard_selector.stories.tsx | 7 + ...d_object_save_modal_dashboard_selector.tsx | 6 +- .../public/services/index.ts | 1 + .../public/services/kibana/capabilities.ts | 3 +- .../public/services/storybook/capabilities.ts | 2 + .../public/services/storybook/index.ts | 1 + .../public/services/stub/capabilities.ts | 1 + .../create_vis_embeddable_from_object.ts | 7 +- .../public/embeddable/visualize_embeddable.ts | 11 +- .../application/utils/get_top_nav_config.tsx | 26 +- .../public/application/utils/utils.ts | 2 +- x-pack/plugins/lens/public/app_plugin/app.tsx | 17 +- .../lens/public/app_plugin/lens_top_nav.tsx | 25 +- .../lens/public/app_plugin/save_modal.tsx | 6 +- .../embeddable/embeddable.test.tsx | 84 +++++-- .../embeddable/embeddable.tsx | 13 +- .../embeddable/embeddable_factory.ts | 8 +- .../public/routes/map_page/top_nav_config.tsx | 6 +- .../apps/dashboard/feature_controls/index.ts | 1 + .../time_to_visualize_security.ts | 233 ++++++++++++++++++ 26 files changed, 484 insertions(+), 69 deletions(-) create mode 100644 x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx index 07c38fd406eea..1156bf8c6e0d1 100644 --- a/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx +++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx @@ -41,8 +41,15 @@ const start = doStart(); let container: DashboardContainer; let embeddable: ContactCardEmbeddable & ReferenceOrValueEmbeddable; let coreStart: CoreStart; +let capabilities: CoreStart['application']['capabilities']; + beforeEach(async () => { coreStart = coreMock.createStart(); + capabilities = { + ...coreStart.application.capabilities, + visualize: { save: true }, + maps: { save: true }, + }; const containerOptions = { ExitFullScreenButton: () => null, @@ -83,7 +90,10 @@ beforeEach(async () => { }); test('Add to library is incompatible with Error Embeddables', async () => { - const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts }); + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities, + }); const errorEmbeddable = new ErrorEmbeddable( 'Wow what an awful error', { id: ' 404' }, @@ -92,20 +102,37 @@ test('Add to library is incompatible with Error Embeddables', async () => { expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false); }); +test('Add to library is incompatible on visualize embeddable without visualize save permissions', async () => { + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities: { ...capabilities, visualize: { save: false } }, + }); + expect(await action.isCompatible({ embeddable })).toBe(false); +}); + test('Add to library is compatible when embeddable on dashboard has value type input', async () => { - const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts }); + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities, + }); embeddable.updateInput(await embeddable.getInputAsValueType()); expect(await action.isCompatible({ embeddable })).toBe(true); }); test('Add to library is not compatible when embeddable input is by reference', async () => { - const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts }); + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities, + }); embeddable.updateInput(await embeddable.getInputAsRefType()); expect(await action.isCompatible({ embeddable })).toBe(false); }); test('Add to library is not compatible when view mode is set to view', async () => { - const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts }); + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities, + }); embeddable.updateInput(await embeddable.getInputAsRefType()); embeddable.updateInput({ viewMode: ViewMode.VIEW }); expect(await action.isCompatible({ embeddable })).toBe(false); @@ -126,7 +153,10 @@ test('Add to library is not compatible when embeddable is not in a dashboard con mockedByReferenceInput: { savedObjectId: 'test', id: orphanContactCard.id }, mockedByValueInput: { firstName: 'Kibanana', id: orphanContactCard.id }, }); - const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts }); + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities, + }); expect(await action.isCompatible({ embeddable: orphanContactCard })).toBe(false); }); @@ -135,7 +165,10 @@ test('Add to library replaces embeddableId and retains panel count', async () => const originalPanelCount = Object.keys(dashboard.getInput().panels).length; const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels)); - const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts }); + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities, + }); await action.execute({ embeddable }); expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount); @@ -161,7 +194,10 @@ test('Add to library returns reference type input', async () => { }); const dashboard = embeddable.getRoot() as IContainer; const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels)); - const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts }); + const action = new AddToLibraryAction({ + toasts: coreStart.notifications.toasts, + capabilities, + }); await action.execute({ embeddable }); const newPanelId = Object.keys(container.getInput().panels).find( (key) => !originalPanelKeySet.has(key) diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx index ef730e16bc5cf..fa102a9415b3f 100644 --- a/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx +++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx @@ -18,7 +18,7 @@ import { isReferenceOrValueEmbeddable, isErrorEmbeddable, } from '../../services/embeddable'; -import { NotificationsStart } from '../../services/core'; +import { ApplicationStart, NotificationsStart } from '../../services/core'; import { dashboardAddToLibraryAction } from '../../dashboard_strings'; import { DashboardPanelState, DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '..'; @@ -33,7 +33,12 @@ export class AddToLibraryAction implements Action { public readonly id = ACTION_ADD_TO_LIBRARY; public order = 15; - constructor(private deps: { toasts: NotificationsStart['toasts'] }) {} + constructor( + private deps: { + toasts: NotificationsStart['toasts']; + capabilities: ApplicationStart['capabilities']; + } + ) {} public getDisplayName({ embeddable }: AddToLibraryActionContext) { if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) { @@ -50,8 +55,15 @@ export class AddToLibraryAction implements Action { } public async isCompatible({ embeddable }: AddToLibraryActionContext) { + // TODO: Fix this, potentially by adding a 'canSave' function to embeddable interface + const canSave = + embeddable.type === 'map' + ? this.deps.capabilities.maps?.save + : this.deps.capabilities.visualize.save; + return Boolean( - !isErrorEmbeddable(embeddable) && + canSave && + !isErrorEmbeddable(embeddable) && embeddable.getInput()?.viewMode !== ViewMode.VIEW && embeddable.getRoot() && embeddable.getRoot().isContainer && diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx index c82f17f2b29c4..829344504b16b 100644 --- a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx +++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx @@ -61,7 +61,8 @@ export class ClonePanelAction implements Action { embeddable.getInput()?.viewMode !== ViewMode.VIEW && embeddable.getRoot() && embeddable.getRoot().isContainer && - embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE + embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE && + embeddable.getOutput().editable ); } diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index ae2d2b5f237c9..5bf730996ab4f 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -342,7 +342,7 @@ export class DashboardPlugin } public start(core: CoreStart, plugins: DashboardStartDependencies): DashboardStart { - const { notifications, overlays } = core; + const { notifications, overlays, application } = core; const { uiActions, data, share, presentationUtil, embeddable } = plugins; const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings); @@ -370,7 +370,10 @@ export class DashboardPlugin } if (this.dashboardFeatureFlagConfig?.allowByValueEmbeddables) { - const addToLibraryAction = new AddToLibraryAction({ toasts: notifications.toasts }); + const addToLibraryAction = new AddToLibraryAction({ + toasts: notifications.toasts, + capabilities: application.capabilities, + }); uiActions.registerAction(addToLibraryAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, addToLibraryAction.id); @@ -386,8 +389,8 @@ export class DashboardPlugin overlays, embeddable.getStateTransfer(), { - canCreateNew: Boolean(core.application.capabilities.dashboard.createNew), - canEditExisting: !Boolean(core.application.capabilities.dashboard.hideWriteControls), + canCreateNew: Boolean(application.capabilities.dashboard.createNew), + canEditExisting: !Boolean(application.capabilities.dashboard.hideWriteControls), }, presentationUtil.ContextProvider ); diff --git a/src/plugins/dashboard/public/services/core.ts b/src/plugins/dashboard/public/services/core.ts index 7c19b2d75a967..75461729841e9 100644 --- a/src/plugins/dashboard/public/services/core.ts +++ b/src/plugins/dashboard/public/services/core.ts @@ -12,4 +12,5 @@ export { PluginInitializerContext, ScopedHistory, NotificationsStart, + ApplicationStart, } from '../../../../core/public'; diff --git a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx index 57d05262319f2..4491be04b1a42 100644 --- a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx +++ b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx @@ -28,6 +28,7 @@ interface SaveModalDocumentInfo { export interface SaveModalDashboardProps { documentInfo: SaveModalDocumentInfo; + canSaveByReference: boolean; objectType: string; onClose: () => void; onSave: (props: OnSaveProps & { dashboardId: string | null; addToLibrary: boolean }) => void; @@ -35,7 +36,7 @@ export interface SaveModalDashboardProps { } export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) { - const { documentInfo, tagOptions, objectType, onClose } = props; + const { documentInfo, tagOptions, objectType, onClose, canSaveByReference } = props; const { id: documentId } = documentInfo; const initialCopyOnSave = !Boolean(documentId); @@ -49,7 +50,7 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) { documentId || disableDashboardOptions ? null : 'existing' ); const [isAddToLibrarySelected, setAddToLibrary] = useState( - !initialCopyOnSave || disableDashboardOptions + canSaveByReference && (!initialCopyOnSave || disableDashboardOptions) ); const [selectedDashboard, setSelectedDashboard] = useState<{ id: string; name: string } | null>( null @@ -65,13 +66,16 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) { onChange={(option) => { setDashboardOption(option); }} + canSaveByReference={canSaveByReference} {...{ copyOnSave, documentId, dashboardOption, setAddToLibrary, isAddToLibrarySelected }} /> ) : null; const onCopyOnSaveChange = (newCopyOnSave: boolean) => { - setAddToLibrary(true); + if (canSaveByReference) { + setAddToLibrary(true); + } setDashboardOption(null); setCopyOnSave(newCopyOnSave); }; diff --git a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx index dd6fd975f8e07..341f194b71ba4 100644 --- a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx +++ b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx @@ -33,15 +33,21 @@ export default { control: 'boolean', defaultValue: true, }, + canSaveVisualizations: { + control: 'boolean', + defaultValue: true, + }, }, }; export function Example({ copyOnSave, hasDocumentId, + canSaveVisualizations, }: { copyOnSave: boolean; hasDocumentId: boolean; + canSaveVisualizations: boolean; } & StorybookParams) { const [dashboardOption, setDashboardOption] = useState<'new' | 'existing' | null>('existing'); const [isAddToLibrarySelected, setAddToLibrary] = useState(false); @@ -52,6 +58,7 @@ export function Example({ onChange={setDashboardOption} dashboardOption={dashboardOption} copyOnSave={copyOnSave} + canSaveByReference={canSaveVisualizations} documentId={hasDocumentId ? 'abc' : undefined} isAddToLibrarySelected={isAddToLibrarySelected} setAddToLibrary={setAddToLibrary} diff --git a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx index 1ae54040571a2..78a1569c02ead 100644 --- a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx +++ b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx @@ -30,6 +30,7 @@ export interface SaveModalDashboardSelectorProps { copyOnSave: boolean; documentId?: string; onSelectDashboard: DashboardPickerProps['onChange']; + canSaveByReference: boolean; setAddToLibrary: (selected: boolean) => void; isAddToLibrarySelected: boolean; dashboardOption: 'new' | 'existing' | null; @@ -40,6 +41,7 @@ export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProp const { documentId, onSelectDashboard, + canSaveByReference, setAddToLibrary, isAddToLibrarySelected, dashboardOption, @@ -114,7 +116,7 @@ export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProp setAddToLibrary(true); onChange(null); }} - disabled={isDisabled} + disabled={isDisabled || !canSaveByReference} /> @@ -127,7 +129,7 @@ export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProp defaultMessage: 'Add to library', })} checked={isAddToLibrarySelected} - disabled={dashboardOption === null || isDisabled} + disabled={dashboardOption === null || isDisabled || !canSaveByReference} onChange={(event) => setAddToLibrary(event.target.checked)} /> diff --git a/src/plugins/presentation_util/public/services/index.ts b/src/plugins/presentation_util/public/services/index.ts index 74607b9e35e47..39dae92aa2ba9 100644 --- a/src/plugins/presentation_util/public/services/index.ts +++ b/src/plugins/presentation_util/public/services/index.ts @@ -23,6 +23,7 @@ export interface PresentationDashboardsService { export interface PresentationCapabilitiesService { canAccessDashboards: () => boolean; canCreateNewDashboards: () => boolean; + canSaveVisualizations: () => boolean; } export interface PresentationUtilServices { diff --git a/src/plugins/presentation_util/public/services/kibana/capabilities.ts b/src/plugins/presentation_util/public/services/kibana/capabilities.ts index 546281d083f2f..6949fba00c65a 100644 --- a/src/plugins/presentation_util/public/services/kibana/capabilities.ts +++ b/src/plugins/presentation_util/public/services/kibana/capabilities.ts @@ -16,10 +16,11 @@ export type CapabilitiesServiceFactory = KibanaPluginServiceFactory< >; export const capabilitiesServiceFactory: CapabilitiesServiceFactory = ({ coreStart }) => { - const { dashboard } = coreStart.application.capabilities; + const { dashboard, visualize } = coreStart.application.capabilities; return { canAccessDashboards: () => Boolean(dashboard.show), canCreateNewDashboards: () => Boolean(dashboard.createNew), + canSaveVisualizations: () => Boolean(visualize.save), }; }; diff --git a/src/plugins/presentation_util/public/services/storybook/capabilities.ts b/src/plugins/presentation_util/public/services/storybook/capabilities.ts index fcd38b29f154c..16fbe3baf488f 100644 --- a/src/plugins/presentation_util/public/services/storybook/capabilities.ts +++ b/src/plugins/presentation_util/public/services/storybook/capabilities.ts @@ -19,11 +19,13 @@ export const capabilitiesServiceFactory: CapabilitiesServiceFactory = ({ canAccessDashboards, canCreateNewDashboards, canEditDashboards, + canSaveVisualizations, }) => { const check = (value: boolean = true) => value; return { canAccessDashboards: () => check(canAccessDashboards), canCreateNewDashboards: () => check(canCreateNewDashboards), canEditDashboards: () => check(canEditDashboards), + canSaveVisualizations: () => check(canSaveVisualizations), }; }; diff --git a/src/plugins/presentation_util/public/services/storybook/index.ts b/src/plugins/presentation_util/public/services/storybook/index.ts index 37b2171635e96..dd7de54264062 100644 --- a/src/plugins/presentation_util/public/services/storybook/index.ts +++ b/src/plugins/presentation_util/public/services/storybook/index.ts @@ -18,6 +18,7 @@ export interface StorybookParams { canAccessDashboards?: boolean; canCreateNewDashboards?: boolean; canEditDashboards?: boolean; + canSaveVisualizations?: boolean; } export const providers: PluginServiceProviders = { diff --git a/src/plugins/presentation_util/public/services/stub/capabilities.ts b/src/plugins/presentation_util/public/services/stub/capabilities.ts index 979ccc8faadd5..4154fa65a0cd7 100644 --- a/src/plugins/presentation_util/public/services/stub/capabilities.ts +++ b/src/plugins/presentation_util/public/services/stub/capabilities.ts @@ -15,4 +15,5 @@ export const capabilitiesServiceFactory: CapabilitiesServiceFactory = () => ({ canAccessDashboards: () => true, canCreateNewDashboards: () => true, canEditDashboards: () => true, + canSaveVisualizations: () => true, }); diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts index 989a9bf5d2cb7..e2e2a4c089270 100644 --- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts +++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts @@ -67,7 +67,10 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe indexPatterns = [vis.data.indexPattern]; } - const editable = getCapabilities().visualize.save as boolean; + const capabilities = { + visualizeSave: Boolean(getCapabilities().visualize.save), + dashboardSave: Boolean(getCapabilities().dashboard?.showWriteControls), + }; return new VisualizeEmbeddable( getTimeFilter(), @@ -76,8 +79,8 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe indexPatterns, editPath, editUrl, - editable, deps, + capabilities, }, input, attributeService, diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 580ffef548fe1..429dabeeef042 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -50,7 +50,7 @@ export interface VisualizeEmbeddableConfiguration { indexPatterns?: IIndexPattern[]; editPath: string; editUrl: string; - editable: boolean; + capabilities: { visualizeSave: boolean; dashboardSave: boolean }; deps: VisualizeEmbeddableFactoryDeps; } @@ -111,7 +111,7 @@ export class VisualizeEmbeddable constructor( timefilter: TimefilterContract, - { vis, editPath, editUrl, indexPatterns, editable, deps }: VisualizeEmbeddableConfiguration, + { vis, editPath, editUrl, indexPatterns, deps, capabilities }: VisualizeEmbeddableConfiguration, initialInput: VisualizeInput, attributeService?: AttributeService< VisualizeSavedObjectAttributes, @@ -129,7 +129,6 @@ export class VisualizeEmbeddable editApp: 'visualize', editUrl, indexPatterns, - editable, visTypeName: vis.type.name, }, parent @@ -143,6 +142,12 @@ export class VisualizeEmbeddable this.attributeService = attributeService; this.savedVisualizationsLoader = savedVisualizationsLoader; + if (this.attributeService) { + const isByValue = !this.inputIsRefType(initialInput); + const editable = capabilities.visualizeSave || (isByValue && capabilities.dashboardSave); + this.updateOutput({ ...this.getOutput(), editable }); + } + this.subscriptions.push( this.getUpdated$().subscribe(() => { const isDirty = this.handleChanges(); diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index 4f5679a14b0b7..e696bcb5dbe4d 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -82,6 +82,7 @@ export const getTopNavConfig = ( setActiveUrl, toastNotifications, visualizeCapabilities, + dashboardCapabilities, i18n: { Context: I18nContext }, dashboard, savedObjectsTagging, @@ -205,9 +206,9 @@ export const getTopNavConfig = ( } }; + const allowByValue = dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables; const saveButtonLabel = - embeddableId || - (!savedVis.id && dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && originatingApp) + embeddableId || (!savedVis.id && allowByValue && originatingApp) ? i18n.translate('visualize.topNavMenu.saveVisualizationToLibraryButtonLabel', { defaultMessage: 'Save to library', }) @@ -219,9 +220,11 @@ export const getTopNavConfig = ( defaultMessage: 'Save', }); - const showSaveAndReturn = - originatingApp && - (savedVis?.id || dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables); + const showSaveAndReturn = originatingApp && (savedVis?.id || allowByValue); + + const showSaveButton = + visualizeCapabilities.save || + (allowByValue && !showSaveAndReturn && dashboardCapabilities.showWriteControls); const topNavMenu: TopNavMenuData[] = [ { @@ -300,7 +303,7 @@ export const getTopNavConfig = ( }, ] : []), - ...(visualizeCapabilities.save + ...(showSaveButton ? [ { id: 'save', @@ -439,7 +442,12 @@ export const getTopNavConfig = ( /> ) : ( { defaultMessage: 'Read only', }), tooltip: i18n.translate('visualize.badge.readOnly.tooltip', { - defaultMessage: 'Unable to save visualizations', + defaultMessage: 'Unable to save visualizations to the library', }), iconType: 'glasses', }); diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 9d5feec9f21e6..dbc10c751a649 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -531,7 +531,13 @@ export function App({ const { TopNavMenu } = navigation.ui; - const savingPermitted = Boolean(state.isSaveable && application.capabilities.visualize.save); + const savingToLibraryPermitted = Boolean( + state.isSaveable && application.capabilities.visualize.save + ); + const savingToDashboardPermitted = Boolean( + state.isSaveable && application.capabilities.dashboard?.showWriteControls + ); + const unsavedTitle = i18n.translate('xpack.lens.app.unsavedFilename', { defaultMessage: 'unsaved', }); @@ -545,8 +551,10 @@ export function App({ state.isSaveable && state.activeData && Object.keys(state.activeData).length ), isByValueMode: getIsByValueMode(), + allowByValue: dashboardFeatureFlag.allowByValueEmbeddables, showCancel: Boolean(state.isLinkedToOriginatingApp), - savingPermitted, + savingToLibraryPermitted, + savingToDashboardPermitted, actions: { exportToCSV: () => { if (!state.activeData) { @@ -577,7 +585,7 @@ export function App({ } }, saveAndReturn: () => { - if (savingPermitted && lastKnownDoc) { + if (savingToDashboardPermitted && lastKnownDoc) { // disabling the validation on app leave because the document has been saved. onAppLeave((actions) => { return actions.default(); @@ -597,7 +605,7 @@ export function App({ } }, showSaveModal: () => { - if (savingPermitted) { + if (savingToDashboardPermitted || savingToLibraryPermitted) { setState((s) => ({ ...s, isSaveModalVisible: true })); } }, @@ -697,6 +705,7 @@ export function App({ { const { originatingApp, + savingToLibraryPermitted, savedObjectsTagging, tagsIds, lastKnownDoc, @@ -85,13 +87,15 @@ export const SaveModal = (props: Props) => { { const saveToLibrary = Boolean(saveProps.addToLibrary); onSave(saveProps, { saveToLibrary }); }} onClose={onClose} documentInfo={{ - id: lastKnownDoc.savedObjectId, + // if the user cannot save to the library - treat this as a new document. + id: savingToLibraryPermitted ? lastKnownDoc.savedObjectId : undefined, title: lastKnownDoc.title || '', description: lastKnownDoc.description || '', }} diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index 157975b630e1e..00eaadeaf8299 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -112,7 +112,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -151,7 +154,7 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { canSaveDashboards: true, canSaveVisualizations: true }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -191,7 +194,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -231,7 +237,10 @@ describe('embeddable', () => { indexPatternService: ({ get: (id: string) => Promise.resolve({ id }), } as unknown) as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -266,7 +275,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -307,7 +319,7 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { canSaveDashboards: true, canSaveVisualizations: true }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -352,7 +364,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -395,7 +410,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -445,7 +463,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -495,7 +516,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -544,7 +568,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: ({ get: jest.fn() } as unknown) as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -582,7 +609,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -620,7 +650,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -658,7 +691,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -711,7 +747,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -780,7 +819,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -824,7 +866,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ @@ -868,7 +913,10 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, - editable: true, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + }, getTrigger, documentToExpression: () => Promise.resolve({ diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 1db067606dc82..a3316e0083d35 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -88,13 +88,13 @@ export interface LensEmbeddableDeps { documentToExpression: ( doc: Document ) => Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }>; - editable: boolean; indexPatternService: IndexPatternsContract; expressionRenderer: ReactExpressionRendererType; timefilter: TimefilterContract; basePath: IBasePath; getTrigger?: UiActionsStart['getTrigger'] | undefined; getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions']; + capabilities: { canSaveVisualizations: boolean; canSaveDashboards: boolean }; } export class Embeddable @@ -129,7 +129,6 @@ export class Embeddable initialInput, { editApp: 'lens', - editable: deps.editable, }, parent ); @@ -326,7 +325,7 @@ export class Embeddable hasCompatibleActions={this.hasCompatibleActions} className={input.className} style={input.style} - canEdit={this.deps.editable && input.viewMode === 'edit'} + canEdit={this.getIsEditable() && input.viewMode === 'edit'} />, domNode ); @@ -451,6 +450,7 @@ export class Embeddable this.updateOutput({ ...this.getOutput(), defaultTitle: this.savedVis.title, + editable: this.getIsEditable(), title, editPath: getEditPath(savedObjectId), editUrl: this.deps.basePath.prepend(`/app/lens${getEditPath(savedObjectId)}`), @@ -458,6 +458,13 @@ export class Embeddable }); } + private getIsEditable() { + return ( + this.deps.capabilities.canSaveVisualizations || + (!this.inputIsRefType(this.getInput()) && this.deps.capabilities.canSaveDashboards) + ); + } + public inputIsRefType = ( input: LensByValueInput | LensByReferenceInput ): input is LensByReferenceInput => { diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index a676b7283671c..1a4962bd1fe8e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -53,7 +53,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { public isEditable = async () => { const { capabilities } = await this.getStartServices(); - return capabilities.visualize.save as boolean; + return Boolean(capabilities.visualize.save || capabilities.dashboard?.showWriteControls); }; canCreateNew() { @@ -86,6 +86,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { coreHttp, attributeService, indexPatternService, + capabilities, } = await this.getStartServices(); const { Embeddable } = await import('../../async_services'); @@ -96,11 +97,14 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { indexPatternService, timefilter, expressionRenderer, - editable: await this.isEditable(), basePath: coreHttp.basePath, getTrigger: uiActions?.getTrigger, getTriggerCompatibleActions: uiActions?.getTriggerCompatibleActions, documentToExpression, + capabilities: { + canSaveDashboards: Boolean(capabilities.dashboard?.showWriteControls), + canSaveVisualizations: Boolean(capabilities.visualize.save), + }, }, input, parent diff --git a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx index 597cd8e9c4287..7e0aa59756876 100644 --- a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx @@ -201,7 +201,11 @@ export function getTopNavConfig({ options={tagSelector} /> ) : ( - + ); showSaveModal(saveModal, getCoreI18n().Context, PresentationUtilContext); diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/index.ts b/x-pack/test/functional/apps/dashboard/feature_controls/index.ts index 38d139c59430e..3b32ea031f6e2 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/index.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/index.ts @@ -11,6 +11,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { this.tags(['skipFirefox']); loadTestFile(require.resolve('./dashboard_security')); + loadTestFile(require.resolve('./time_to_visualize_security')); loadTestFile(require.resolve('./dashboard_spaces')); }); } diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts new file mode 100644 index 0000000000000..3ebc53cc7cf27 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts @@ -0,0 +1,233 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects([ + 'timeToVisualize', + 'timePicker', + 'dashboard', + 'visEditor', + 'visualize', + 'security', + 'common', + 'header', + 'lens', + ]); + + const dashboardVisualizations = getService('dashboardVisualizations'); + const dashboardPanelActions = getService('dashboardPanelActions'); + const dashboardExpect = getService('dashboardExpect'); + const testSubjects = getService('testSubjects'); + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const find = getService('find'); + + describe('dashboard time to visualize security', () => { + before(async () => { + await esArchiver.load('dashboard/feature_controls/security'); + await esArchiver.loadIfNeeded('logstash_functional'); + + // ensure we're logged out so we can login as the appropriate users + await PageObjects.security.forceLogout(); + + await security.role.create('dashboard_write_vis_read', { + elasticsearch: { + indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }], + }, + kibana: [ + { + feature: { + dashboard: ['all'], + visualize: ['read'], + }, + spaces: ['*'], + }, + ], + }); + + await security.user.create('dashboard_write_vis_read_user', { + password: 'dashboard_write_vis_read_user-password', + roles: ['dashboard_write_vis_read'], + full_name: 'test user', + }); + + await PageObjects.security.login( + 'dashboard_write_vis_read_user', + 'dashboard_write_vis_read_user-password', + { + expectSpaceSelector: false, + } + ); + }); + + after(async () => { + await security.role.delete('dashboard_write_vis_read'); + await security.user.delete('dashboard_write_vis_read_user'); + + await esArchiver.unload('dashboard/feature_controls/security'); + + // logout, so the other tests don't accidentally run as the custom users we're testing below + await PageObjects.security.forceLogout(); + }); + + describe('lens by value works without library save permissions', () => { + before(async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.preserveCrossAppState(); + await PageObjects.dashboard.clickNewDashboard(); + }); + + it('can add a lens panel by value', async () => { + await dashboardVisualizations.ensureNewVisualizationDialogIsShowing(); + await PageObjects.lens.createAndAddLensFromDashboard({}); + const newPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(newPanelCount).to.eql(1); + }); + + it('edits to a by value lens panel are properly applied', async () => { + await PageObjects.dashboard.waitForRenderComplete(); + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickEdit(); + await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.saveAndReturn(); + await PageObjects.dashboard.waitForRenderComplete(); + + const pieExists = await find.existsByCssSelector('.lnsPieExpression__container'); + expect(pieExists).to.be(true); + }); + + it('disables save to library button without visualize save permissions', async () => { + await PageObjects.dashboard.waitForRenderComplete(); + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickEdit(); + const saveButton = await testSubjects.find('lnsApp_saveButton'); + expect(await saveButton.getAttribute('disabled')).to.equal('true'); + await PageObjects.lens.saveAndReturn(); + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + + it('should allow new lens to be added by value, but not by reference', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'average', + field: 'bytes', + }); + + await PageObjects.lens.switchToVisualization('lnsMetric'); + + await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); + + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click('lnsApp_saveButton'); + + const libraryCheckbox = await find.byCssSelector('#add-to-library-checkbox'); + expect(await libraryCheckbox.getAttribute('disabled')).to.equal('true'); + + await PageObjects.timeToVisualize.saveFromModal('New Lens from Modal', { + addToDashboard: 'new', + saveAsNew: true, + saveToLibrary: false, + }); + + await PageObjects.dashboard.waitForRenderComplete(); + + await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); + const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists( + 'New Lens from Modal' + ); + expect(isLinked).to.be(false); + + const panelCount = await PageObjects.dashboard.getPanelCount(); + expect(panelCount).to.eql(1); + + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + }); + + describe('visualize by value works without library save permissions', () => { + const originalMarkdownText = 'Original markdown text'; + const modifiedMarkdownText = 'Modified markdown text'; + + before(async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.preserveCrossAppState(); + await PageObjects.dashboard.clickNewDashboard(); + }); + + it('can add a markdown panel by value', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.dashboard.waitForRenderComplete(); + + await testSubjects.click('dashboardAddNewPanelButton'); + await dashboardVisualizations.ensureNewVisualizationDialogIsShowing(); + await PageObjects.visualize.clickMarkdownWidget(); + await PageObjects.visEditor.setMarkdownTxt(originalMarkdownText); + await PageObjects.visEditor.clickGo(); + + await PageObjects.visualize.saveVisualizationAndReturn(); + const newPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(newPanelCount).to.eql(1); + }); + + it('edits to a by value visualize panel are properly applied', async () => { + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickEdit(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.visEditor.setMarkdownTxt(modifiedMarkdownText); + await PageObjects.visEditor.clickGo(); + await PageObjects.visualize.saveVisualizationAndReturn(); + + await PageObjects.dashboard.waitForRenderComplete(); + const markdownText = await testSubjects.find('markdownBody'); + expect(await markdownText.getVisibleText()).to.eql(modifiedMarkdownText); + + const newPanelCount = PageObjects.dashboard.getPanelCount(); + expect(newPanelCount).to.eql(1); + }); + + it('disables save to library button without visualize save permissions', async () => { + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickEdit(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.missingOrFail('visualizeSaveButton'); + await PageObjects.visualize.saveVisualizationAndReturn(); + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + + it('should allow new visualization to be added by value, but not by reference', async function () { + await PageObjects.visualize.navigateToNewAggBasedVisualization(); + await PageObjects.visualize.clickMetric(); + await PageObjects.visualize.clickNewSearch(); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + + await testSubjects.click('visualizeSaveButton'); + + await PageObjects.visualize.ensureSavePanelOpen(); + const libraryCheckbox = await find.byCssSelector('#add-to-library-checkbox'); + expect(await libraryCheckbox.getAttribute('disabled')).to.equal('true'); + + await PageObjects.timeToVisualize.saveFromModal('My New Vis 1', { + addToDashboard: 'new', + }); + + await PageObjects.dashboard.waitForRenderComplete(); + await dashboardExpect.metricValuesExist(['14,005']); + const panelCount = await PageObjects.dashboard.getPanelCount(); + expect(panelCount).to.eql(1); + }); + }); + }); +} From 03b104cc6144cd02aa4fc64a733622ee191e01bd Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Wed, 31 Mar 2021 15:33:19 -0400 Subject: [PATCH 10/26] [Security Solution][RAC][Timeline] Timeline plugin skeleton and test plugin harness (#95683) * [RAC][Security Solution] Initial timeline and test plugin harness * Change plugin name from timeline to timelines --- .github/CODEOWNERS | 1 + docs/developer/plugin-list.asciidoc | 4 ++ packages/kbn-optimizer/limits.yml | 1 + tsconfig.json | 1 + x-pack/.i18nrc.json | 1 + x-pack/plugins/timelines/.eslintrc.js | 7 +++ x-pack/plugins/timelines/.i18nrc.json | 7 +++ x-pack/plugins/timelines/README.md | 11 ++++ x-pack/plugins/timelines/common/index.ts | 2 + x-pack/plugins/timelines/kibana.json | 10 ++++ .../timelines/public/components/index.tsx | 22 +++++++ x-pack/plugins/timelines/public/index.scss | 0 x-pack/plugins/timelines/public/index.ts | 11 ++++ .../timelines/public/methods/index.tsx | 19 ++++++ x-pack/plugins/timelines/public/plugin.ts | 24 ++++++++ x-pack/plugins/timelines/public/types.ts | 9 +++ x-pack/plugins/timelines/server/config.ts | 13 ++++ x-pack/plugins/timelines/server/index.ts | 21 +++++++ x-pack/plugins/timelines/server/plugin.ts | 35 +++++++++++ .../plugins/timelines/server/routes/index.ts | 17 ++++++ x-pack/plugins/timelines/server/types.ts | 4 ++ x-pack/plugins/timelines/tsconfig.json | 19 ++++++ x-pack/test/plugin_functional/config.ts | 5 ++ .../plugins/timelines_test/kibana.json | 12 ++++ .../applications/timelines_test/index.tsx | 60 +++++++++++++++++++ .../plugins/timelines_test/public/index.ts | 20 +++++++ .../plugins/timelines_test/public/plugin.ts | 50 ++++++++++++++++ .../test_suites/timelines/index.ts | 25 ++++++++ 28 files changed, 411 insertions(+) create mode 100644 x-pack/plugins/timelines/.eslintrc.js create mode 100644 x-pack/plugins/timelines/.i18nrc.json create mode 100644 x-pack/plugins/timelines/README.md create mode 100644 x-pack/plugins/timelines/common/index.ts create mode 100644 x-pack/plugins/timelines/kibana.json create mode 100644 x-pack/plugins/timelines/public/components/index.tsx create mode 100644 x-pack/plugins/timelines/public/index.scss create mode 100644 x-pack/plugins/timelines/public/index.ts create mode 100644 x-pack/plugins/timelines/public/methods/index.tsx create mode 100644 x-pack/plugins/timelines/public/plugin.ts create mode 100644 x-pack/plugins/timelines/public/types.ts create mode 100644 x-pack/plugins/timelines/server/config.ts create mode 100644 x-pack/plugins/timelines/server/index.ts create mode 100644 x-pack/plugins/timelines/server/plugin.ts create mode 100644 x-pack/plugins/timelines/server/routes/index.ts create mode 100644 x-pack/plugins/timelines/server/types.ts create mode 100644 x-pack/plugins/timelines/tsconfig.json create mode 100644 x-pack/test/plugin_functional/plugins/timelines_test/kibana.json create mode 100644 x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx create mode 100644 x-pack/test/plugin_functional/plugins/timelines_test/public/index.ts create mode 100644 x-pack/test/plugin_functional/plugins/timelines_test/public/plugin.ts create mode 100644 x-pack/test/plugin_functional/test_suites/timelines/index.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f27885c1e32c3..d14556ea1dabf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -348,6 +348,7 @@ # Security Solution sub teams /x-pack/plugins/case @elastic/security-threat-hunting +/x-pack/plugins/timelines @elastic/security-threat-hunting /x-pack/test/case_api_integration @elastic/security-threat-hunting /x-pack/plugins/lists @elastic/security-detections-response diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index e1c2c40a31384..bcf74936077ec 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -537,6 +537,10 @@ Documentation: https://www.elastic.co/guide/en/kibana/master/task-manager-produc |Gathers all usage collection, retrieving them from both: OSS and X-Pack plugins. +|{kib-repo}blob/{branch}/x-pack/plugins/timelines/README.md[timelines] +|Timelines is a plugin that provides a grid component with accompanying server side apis to help users identify events of interest and perform root cause analysis within Kibana. + + |{kib-repo}blob/{branch}/x-pack/plugins/transform/readme.md[transform] |This plugin provides access to the transforms features provided by Elastic. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index c9c37108acef4..3c9fd4f59a406 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -108,3 +108,4 @@ pageLoadAssetSize: fileUpload: 25664 banners: 17946 mapsEms: 26072 + timelines: 28613 diff --git a/tsconfig.json b/tsconfig.json index 18647153acb0a..c852481518d54 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -123,6 +123,7 @@ { "path": "./x-pack/plugins/stack_alerts/tsconfig.json" }, { "path": "./x-pack/plugins/task_manager/tsconfig.json" }, { "path": "./x-pack/plugins/telemetry_collection_xpack/tsconfig.json" }, + { "path": "./x-pack/plugins/timelines/tsconfig.json" }, { "path": "./x-pack/plugins/transform/tsconfig.json" }, { "path": "./x-pack/plugins/translations/tsconfig.json" }, { "path": "./x-pack/plugins/triggers_actions_ui/tsconfig.json" }, diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 663ae32f9128a..6bbbf6cd6b82d 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -53,6 +53,7 @@ "xpack.spaces": "plugins/spaces", "xpack.savedObjectsTagging": ["plugins/saved_objects_tagging"], "xpack.taskManager": "legacy/plugins/task_manager", + "xpack.timelines": "plugins/timelines", "xpack.transform": "plugins/transform", "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.upgradeAssistant": "plugins/upgrade_assistant", diff --git a/x-pack/plugins/timelines/.eslintrc.js b/x-pack/plugins/timelines/.eslintrc.js new file mode 100644 index 0000000000000..b267018448ba6 --- /dev/null +++ b/x-pack/plugins/timelines/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + root: true, + extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'], + rules: { + '@kbn/eslint/require-license-header': 'off', + }, +}; diff --git a/x-pack/plugins/timelines/.i18nrc.json b/x-pack/plugins/timelines/.i18nrc.json new file mode 100644 index 0000000000000..4fe01ccc7bc69 --- /dev/null +++ b/x-pack/plugins/timelines/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "timelines", + "paths": { + "timelines": "." + }, + "translations": ["translations/ja-JP.json"] +} diff --git a/x-pack/plugins/timelines/README.md b/x-pack/plugins/timelines/README.md new file mode 100644 index 0000000000000..441a505903698 --- /dev/null +++ b/x-pack/plugins/timelines/README.md @@ -0,0 +1,11 @@ +# timelines +Timelines is a plugin that provides a grid component with accompanying server side apis to help users identify events of interest and perform root cause analysis within Kibana. + + +## Using timelines in another plugin +- Add `TimelinesPluginSetup` to Kibana plugin `SetupServices` dependencies: + +```ts +timelines: TimelinesPluginSetup; +``` +- Once `timelines` is added as a required plugin in the consuming plugin's kibana.json, timeline functionality will be available as any other kibana plugin, ie PluginSetupDependencies.timelines.getTimeline() diff --git a/x-pack/plugins/timelines/common/index.ts b/x-pack/plugins/timelines/common/index.ts new file mode 100644 index 0000000000000..2354c513f73b8 --- /dev/null +++ b/x-pack/plugins/timelines/common/index.ts @@ -0,0 +1,2 @@ +export const PLUGIN_ID = 'timelines'; +export const PLUGIN_NAME = 'timelines'; diff --git a/x-pack/plugins/timelines/kibana.json b/x-pack/plugins/timelines/kibana.json new file mode 100644 index 0000000000000..552ddfd25ce73 --- /dev/null +++ b/x-pack/plugins/timelines/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "timelines", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "timelines"], + "server": true, + "ui": true, + "requiredPlugins": [], + "optionalPlugins": [] +} diff --git a/x-pack/plugins/timelines/public/components/index.tsx b/x-pack/plugins/timelines/public/components/index.tsx new file mode 100644 index 0000000000000..3388b3c44baff --- /dev/null +++ b/x-pack/plugins/timelines/public/components/index.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; + +import { PLUGIN_NAME } from '../../common'; +import { TimelineProps } from '../types'; + +export const Timeline = (props: TimelineProps) => { + return ( + +
+ +
+
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export { Timeline as default }; diff --git a/x-pack/plugins/timelines/public/index.scss b/x-pack/plugins/timelines/public/index.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/x-pack/plugins/timelines/public/index.ts b/x-pack/plugins/timelines/public/index.ts new file mode 100644 index 0000000000000..b535def809de3 --- /dev/null +++ b/x-pack/plugins/timelines/public/index.ts @@ -0,0 +1,11 @@ +import './index.scss'; + +import { PluginInitializerContext } from 'src/core/public'; +import { TimelinesPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin(initializerContext: PluginInitializerContext) { + return new TimelinesPlugin(initializerContext); +} +export { TimelinesPluginSetup } from './types'; diff --git a/x-pack/plugins/timelines/public/methods/index.tsx b/x-pack/plugins/timelines/public/methods/index.tsx new file mode 100644 index 0000000000000..f999e14ce910c --- /dev/null +++ b/x-pack/plugins/timelines/public/methods/index.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { TimelineProps } from '../types'; + +export const getTimelineLazy = (props: TimelineProps) => { + const TimelineLazy = lazy(() => import('../components')); + return ( + }> + + + ); +}; diff --git a/x-pack/plugins/timelines/public/plugin.ts b/x-pack/plugins/timelines/public/plugin.ts new file mode 100644 index 0000000000000..7e90d9467fefd --- /dev/null +++ b/x-pack/plugins/timelines/public/plugin.ts @@ -0,0 +1,24 @@ +import { CoreSetup, Plugin, PluginInitializerContext } from '../../../../src/core/public'; +import { TimelinesPluginSetup, TimelineProps } from './types'; +import { getTimelineLazy } from './methods'; + +export class TimelinesPlugin implements Plugin { + constructor(private readonly initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup): TimelinesPluginSetup { + const config = this.initializerContext.config.get<{ enabled: boolean }>(); + if (!config.enabled) { + return {}; + } + + return { + getTimeline: (props: TimelineProps) => { + return getTimelineLazy(props); + }, + }; + } + + public start() {} + + public stop() {} +} diff --git a/x-pack/plugins/timelines/public/types.ts b/x-pack/plugins/timelines/public/types.ts new file mode 100644 index 0000000000000..b199b45902718 --- /dev/null +++ b/x-pack/plugins/timelines/public/types.ts @@ -0,0 +1,9 @@ +import { ReactElement } from 'react'; + +export interface TimelinesPluginSetup { + getTimeline?: (props: TimelineProps) => ReactElement; +} + +export interface TimelineProps { + timelineId: string; +} diff --git a/x-pack/plugins/timelines/server/config.ts b/x-pack/plugins/timelines/server/config.ts new file mode 100644 index 0000000000000..633a95b8f91a7 --- /dev/null +++ b/x-pack/plugins/timelines/server/config.ts @@ -0,0 +1,13 @@ +/* + * 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 { TypeOf, schema } from '@kbn/config-schema'; + +export const ConfigSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +export type ConfigType = TypeOf; diff --git a/x-pack/plugins/timelines/server/index.ts b/x-pack/plugins/timelines/server/index.ts new file mode 100644 index 0000000000000..32de97be2704a --- /dev/null +++ b/x-pack/plugins/timelines/server/index.ts @@ -0,0 +1,21 @@ +/* + * 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 { PluginInitializerContext } from '../../../../src/core/server'; +import { TimelinesPlugin } from './plugin'; +import { ConfigSchema } from './config'; + +export const config = { + schema: ConfigSchema, + exposeToBrowser: { + enabled: true, + }, +}; +export function plugin(initializerContext: PluginInitializerContext) { + return new TimelinesPlugin(initializerContext); +} + +export { TimelinesPluginSetup, TimelinesPluginStart } from './types'; diff --git a/x-pack/plugins/timelines/server/plugin.ts b/x-pack/plugins/timelines/server/plugin.ts new file mode 100644 index 0000000000000..3e330b19b7fdb --- /dev/null +++ b/x-pack/plugins/timelines/server/plugin.ts @@ -0,0 +1,35 @@ +import { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + Logger, +} from '../../../../src/core/server'; + +import { TimelinesPluginSetup, TimelinesPluginStart } from './types'; +import { defineRoutes } from './routes'; + +export class TimelinesPlugin implements Plugin { + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('timelines: Setup'); + const router = core.http.createRouter(); + + // Register server side APIs + defineRoutes(router); + + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('timelines: Started'); + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/timelines/server/routes/index.ts b/x-pack/plugins/timelines/server/routes/index.ts new file mode 100644 index 0000000000000..edb10c579b30b --- /dev/null +++ b/x-pack/plugins/timelines/server/routes/index.ts @@ -0,0 +1,17 @@ +import { IRouter } from '../../../../../src/core/server'; + +export function defineRoutes(router: IRouter) { + router.get( + { + path: '/api/timeline/example', + validate: false, + }, + async (context, request, response) => { + return response.ok({ + body: { + time: new Date().toISOString(), + }, + }); + } + ); +} diff --git a/x-pack/plugins/timelines/server/types.ts b/x-pack/plugins/timelines/server/types.ts new file mode 100644 index 0000000000000..cb544562b79b4 --- /dev/null +++ b/x-pack/plugins/timelines/server/types.ts @@ -0,0 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface TimelinesPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface TimelinesPluginStart {} diff --git a/x-pack/plugins/timelines/tsconfig.json b/x-pack/plugins/timelines/tsconfig.json new file mode 100644 index 0000000000000..67e606e798c03 --- /dev/null +++ b/x-pack/plugins/timelines/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + // add all the folders contains files to be compiled + "common/**/*", + "public/**/*", + "server/**/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + ] +} diff --git a/x-pack/test/plugin_functional/config.ts b/x-pack/test/plugin_functional/config.ts index 5b846e414bd4c..104d11eb87f7c 100644 --- a/x-pack/test/plugin_functional/config.ts +++ b/x-pack/test/plugin_functional/config.ts @@ -30,6 +30,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { testFiles: [ resolve(__dirname, './test_suites/resolver'), resolve(__dirname, './test_suites/global_search'), + resolve(__dirname, './test_suites/timelines'), ], services, @@ -47,6 +48,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { KIBANA_ROOT, 'test/plugin_functional/plugins/core_provider_plugin' )}`, + '--xpack.timelines.enabled=true', ...plugins.map((pluginDir) => `--plugin-path=${resolve(__dirname, 'plugins', pluginDir)}`), ], }, @@ -60,6 +62,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { resolverTest: { pathname: '/app/resolverTest', }, + timelineTest: { + pathname: '/app/timelinesTest', + }, }, // choose where esArchiver should load archives from diff --git a/x-pack/test/plugin_functional/plugins/timelines_test/kibana.json b/x-pack/test/plugin_functional/plugins/timelines_test/kibana.json new file mode 100644 index 0000000000000..85c2639ef7d47 --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/timelines_test/kibana.json @@ -0,0 +1,12 @@ +{ + "id": "timelinesTest", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "timelinesTest"], + "requiredPlugins": ["timelines"], + "requiredBundles": [ + "kibanaReact" + ], + "server": false, + "ui": true +} diff --git a/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx b/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx new file mode 100644 index 0000000000000..a6772c3b0bb5b --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Router } from 'react-router-dom'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppMountParameters, CoreStart } from 'kibana/public'; +import { I18nProvider } from '@kbn/i18n/react'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; +import { TimelinesPluginSetup } from '../../../../../../../plugins/timelines/public'; + +/** + * Render the Timeline Test app. Returns a cleanup function. + */ +export function renderApp( + coreStart: CoreStart, + parameters: AppMountParameters, + timelinesPluginSetup: TimelinesPluginSetup +) { + ReactDOM.render( + , + parameters.element + ); + + return () => { + ReactDOM.unmountComponentAtNode(parameters.element); + }; +} + +const AppRoot = React.memo( + ({ + coreStart, + parameters, + timelinesPluginSetup, + }: { + coreStart: CoreStart; + parameters: AppMountParameters; + timelinesPluginSetup: TimelinesPluginSetup; + }) => { + return ( + + + + {(timelinesPluginSetup.getTimeline && + timelinesPluginSetup.getTimeline({ timelineId: 'test' })) ?? + null} + + + + ); + } +); diff --git a/x-pack/test/plugin_functional/plugins/timelines_test/public/index.ts b/x-pack/test/plugin_functional/plugins/timelines_test/public/index.ts new file mode 100644 index 0000000000000..5f038b5b933e6 --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/timelines_test/public/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializer } from 'kibana/public'; +import { + TimelinesTestPlugin, + TimelinesTestPluginSetupDependencies, + TimelinesTestPluginStartDependencies, +} from './plugin'; + +export const plugin: PluginInitializer< + void, + void, + TimelinesTestPluginSetupDependencies, + TimelinesTestPluginStartDependencies +> = () => new TimelinesTestPlugin(); diff --git a/x-pack/test/plugin_functional/plugins/timelines_test/public/plugin.ts b/x-pack/test/plugin_functional/plugins/timelines_test/public/plugin.ts new file mode 100644 index 0000000000000..5cf900e194d0c --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/timelines_test/public/plugin.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { TimelinesPluginSetup } from '../../../../../plugins/timelines/public'; +import { renderApp } from './applications/timelines_test'; + +export type TimelinesTestPluginSetup = void; +export type TimelinesTestPluginStart = void; +export interface TimelinesTestPluginSetupDependencies { + timelines: TimelinesPluginSetup; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface TimelinesTestPluginStartDependencies {} + +export class TimelinesTestPlugin + implements + Plugin< + TimelinesTestPluginSetup, + void, + TimelinesTestPluginSetupDependencies, + TimelinesTestPluginStartDependencies + > { + public setup( + core: CoreSetup, + setupDependencies: TimelinesTestPluginSetupDependencies + ) { + core.application.register({ + id: 'timelinesTest', + title: i18n.translate('xpack.timelinesTest.pluginTitle', { + defaultMessage: 'Timelines Test', + }), + mount: async (params: AppMountParameters) => { + const startServices = await core.getStartServices(); + const [coreStart] = startServices; + const { timelines } = setupDependencies; + + return renderApp(coreStart, params, timelines); + }, + }); + } + + public start() {} +} diff --git a/x-pack/test/plugin_functional/test_suites/timelines/index.ts b/x-pack/test/plugin_functional/test_suites/timelines/index.ts new file mode 100644 index 0000000000000..655ed9dc3898a --- /dev/null +++ b/x-pack/test/plugin_functional/test_suites/timelines/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + describe('Timelines plugin API', function () { + this.tags('ciGroup7'); + const pageObjects = getPageObjects(['common']); + const testSubjects = getService('testSubjects'); + + describe('timelines plugin rendering', function () { + before(async () => { + await pageObjects.common.navigateToApp('timelineTest'); + }); + it('shows the timeline component on navigation', async () => { + await testSubjects.existOrFail('timeline-wrapper'); + }); + }); + }); +} From b13181a53957f67ee5d2e255adfc9bb8bdbf899f Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 31 Mar 2021 15:53:28 -0400 Subject: [PATCH 11/26] [Fleet] Fleet server invalidate api keys for agent < 7.13 (#95789) --- .../fleet_server/saved_object_migrations.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/fleet_server/saved_object_migrations.ts b/x-pack/plugins/fleet/server/services/fleet_server/saved_object_migrations.ts index f078b214e4dfd..7af2b791f3707 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server/saved_object_migrations.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server/saved_object_migrations.ts @@ -25,6 +25,7 @@ import { listEnrollmentApiKeys, getEnrollmentAPIKey } from '../api_keys/enrollme import { appContextService } from '../app_context'; import { isAgentsSetup } from '../agents'; import { agentPolicyService } from '../agent_policy'; +import { invalidateAPIKeys } from '../api_keys'; export async function runFleetServerMigration() { // If Agents are not setup skip as there is nothing to migrate @@ -56,6 +57,7 @@ function getInternalUserSOClient() { async function migrateAgents() { const esClient = appContextService.getInternalUserESClient(); const soClient = getInternalUserSOClient(); + const logger = appContextService.getLogger(); let hasMore = true; while (hasMore) { const res = await soClient.find({ @@ -75,11 +77,20 @@ async function migrateAgents() { .getEncryptedSavedObjects() .getDecryptedAsInternalUser(AGENT_SAVED_OBJECT_TYPE, so.id); + await invalidateAPIKeys( + soClient, + [attributes.access_api_key_id, attributes.default_api_key_id].filter( + (keyId): keyId is string => keyId !== undefined + ) + ).catch((error) => { + logger.error(`Invalidating API keys for agent ${so.id} failed: ${error.message}`); + }); + const body: FleetServerAgent = { type: attributes.type, - active: attributes.active, + active: false, enrolled_at: attributes.enrolled_at, - unenrolled_at: attributes.unenrolled_at, + unenrolled_at: new Date().toISOString(), unenrollment_started_at: attributes.unenrollment_started_at, upgraded_at: attributes.upgraded_at, upgrade_started_at: attributes.upgrade_started_at, From 2a3edb0adb8c9d3214f35fdddc5f11931daa7cd7 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 31 Mar 2021 13:06:04 -0700 Subject: [PATCH 12/26] skip flaky suite (#95591) --- .../apps/triggers_actions_ui/alerts_list.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index 4c085cbb77045..7b760dfb8b6a1 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -54,7 +54,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('rulesTab'); } - describe('alerts list', function () { + // FLAKY: https://github.com/elastic/kibana/issues/95591 + describe.skip('alerts list', function () { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('rulesTab'); From 0fce2427e5c24ddcaf07ec0049a597fff138f2ef Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Wed, 31 Mar 2021 13:12:19 -0700 Subject: [PATCH 13/26] [Reporting] CSV Export: fix and unskip failing test (#95824) * [Reporting] CSV Export: fix and unskip failing test * fix snapshot extra records Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../functional/apps/discover/__snapshots__/reporting.snap | 4 ++-- x-pack/test/functional/apps/discover/reporting.ts | 3 +-- .../__snapshots__/csv_searchsource_immediate.snap | 8 ++++---- .../reporting_and_security/csv_searchsource_immediate.ts | 3 +-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap b/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap index 5ddef936b41ae..baa49cb6f9d81 100644 --- a/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap +++ b/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap @@ -71,7 +71,7 @@ exports[`discover Discover CSV Export Generate CSV: new search generates a repor 24.5 ], \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan +}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.375, 33, 10.344, 6.109\\",\\"80, 60, 21.984, 11.992\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",174,174,4,4,order,sultan " `; @@ -83,6 +83,6 @@ exports[`discover Discover CSV Export Generate CSV: new search generates a repor 24.5 ], \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan +}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.375, 33, 10.344, 6.109\\",\\"80, 60, 21.984, 11.992\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",174,174,4,4,order,sultan " `; diff --git a/x-pack/test/functional/apps/discover/reporting.ts b/x-pack/test/functional/apps/discover/reporting.ts index 9acb4c311c113..d7dd961e2f103 100644 --- a/x-pack/test/functional/apps/discover/reporting.ts +++ b/x-pack/test/functional/apps/discover/reporting.ts @@ -21,8 +21,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.uiSettings.update({ 'discover:searchFieldsFromSource': setValue }); }; - // Failing: See https://github.com/elastic/kibana/issues/95592 - describe.skip('Discover CSV Export', () => { + describe('Discover CSV Export', () => { before('initialize tests', async () => { log.debug('ReportingPage:initTests'); await esArchiver.load('reporting/ecommerce'); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap b/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap index c7ef39f65f552..094d72942353d 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap +++ b/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap @@ -8,28 +8,28 @@ exports[`Reporting APIs CSV Generation from SearchSource Exports CSV with all fi 24.5 ], \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan +}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.375, 33, 10.344, 6.109\\",\\"80, 60, 21.984, 11.992\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",174,174,4,4,order,sultan 9gMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Shoes, Women's Clothing\\",\\"Women's Shoes, Women's Clothing\\",EUR,Pia,Pia,\\"Pia Richards\\",\\"Pia Richards\\",FEMALE,45,Richards,Richards,,Saturday,5,\\"pia@richards-family.zzz\\",Cannes,Europe,FR,\\"{ \\"\\"coordinates\\"\\": [ 7, 43.6 ], \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Alpes-Maritimes\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591503,\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"20.99, 20.99\\",\\"20.99, 20.99\\",\\"Women's Shoes, Women's Clothing\\",\\"Women's Shoes, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"10.7, 9.87\\",\\"20.99, 20.99\\",\\"14,761, 11,632\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"1, 1\\",\\"ZO0006400064, ZO0150601506\\",\\"0, 0\\",\\"20.99, 20.99\\",\\"20.99, 20.99\\",\\"0, 0\\",\\"ZO0006400064, ZO0150601506\\",\\"41.98\\",\\"41.98\\",2,2,order,pia +}\\",\\"Alpes-Maritimes\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591503,\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"20.984, 20.984\\",\\"20.984, 20.984\\",\\"Women's Shoes, Women's Clothing\\",\\"Women's Shoes, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"10.703, 9.867\\",\\"20.984, 20.984\\",\\"14,761, 11,632\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"1, 1\\",\\"ZO0006400064, ZO0150601506\\",\\"0, 0\\",\\"20.984, 20.984\\",\\"20.984, 20.984\\",\\"0, 0\\",\\"ZO0006400064, ZO0150601506\\",\\"41.969\\",\\"41.969\\",2,2,order,pia BgMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Clothing\\",\\"Women's Clothing\\",EUR,Brigitte,Brigitte,\\"Brigitte Meyer\\",\\"Brigitte Meyer\\",FEMALE,12,Meyer,Meyer,,Saturday,5,\\"brigitte@meyer-family.zzz\\",\\"New York\\",\\"North America\\",US,\\"{ \\"\\"coordinates\\"\\": [ -74, 40.8 ], \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"New York\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591709,\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"7.99, 32.99\\",\\"7.99, 32.99\\",\\"Women's Clothing, Women's Clothing\\",\\"Women's Clothing, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"3.6, 17.48\\",\\"7.99, 32.99\\",\\"20,734, 7,539\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"1, 1\\",\\"ZO0638206382, ZO0038800388\\",\\"0, 0\\",\\"7.99, 32.99\\",\\"7.99, 32.99\\",\\"0, 0\\",\\"ZO0638206382, ZO0038800388\\",\\"40.98\\",\\"40.98\\",2,2,order,brigitte +}\\",\\"New York\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591709,\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"7.988, 33\\",\\"7.988, 33\\",\\"Women's Clothing, Women's Clothing\\",\\"Women's Clothing, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"3.6, 17.484\\",\\"7.988, 33\\",\\"20,734, 7,539\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"1, 1\\",\\"ZO0638206382, ZO0038800388\\",\\"0, 0\\",\\"7.988, 33\\",\\"7.988, 33\\",\\"0, 0\\",\\"ZO0638206382, ZO0038800388\\",\\"40.969\\",\\"40.969\\",2,2,order,brigitte KQMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Clothing\\",\\"Men's Clothing\\",EUR,Abd,Abd,\\"Abd Mccarthy\\",\\"Abd Mccarthy\\",MALE,52,Mccarthy,Mccarthy,,Saturday,5,\\"abd@mccarthy-family.zzz\\",Cairo,Africa,EG,\\"{ \\"\\"coordinates\\"\\": [ 31.3, 30.1 ], \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Cairo Governorate\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"Jul 12, 2019 @ 00:00:00.000\\",590937,\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"28.99, 12.99\\",\\"28.99, 12.99\\",\\"Men's Clothing, Men's Clothing\\",\\"Men's Clothing, Men's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"13.34, 6.11\\",\\"28.99, 12.99\\",\\"14,438, 23,607\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"1, 1\\",\\"ZO0297602976, ZO0565605656\\",\\"0, 0\\",\\"28.99, 12.99\\",\\"28.99, 12.99\\",\\"0, 0\\",\\"ZO0297602976, ZO0565605656\\",\\"41.98\\",\\"41.98\\",2,2,order,abd +}\\",\\"Cairo Governorate\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"Jul 12, 2019 @ 00:00:00.000\\",590937,\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"28.984, 12.992\\",\\"28.984, 12.992\\",\\"Men's Clothing, Men's Clothing\\",\\"Men's Clothing, Men's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"13.344, 6.109\\",\\"28.984, 12.992\\",\\"14,438, 23,607\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"1, 1\\",\\"ZO0297602976, ZO0565605656\\",\\"0, 0\\",\\"28.984, 12.992\\",\\"28.984, 12.992\\",\\"0, 0\\",\\"ZO0297602976, ZO0565605656\\",\\"41.969\\",\\"41.969\\",2,2,order,abd " `; diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts b/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts index ffaa4cb2f8fb6..27c6a05f740bf 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts @@ -31,8 +31,7 @@ export default function ({ getService }: FtrProviderContext) { }, }; - // Failing: See https://github.com/elastic/kibana/issues/95594 - describe.skip('CSV Generation from SearchSource', () => { + describe('CSV Generation from SearchSource', () => { before(async () => { await kibanaServer.uiSettings.update({ 'csv:quoteValues': false, From 3eb099692085d4dbaa7c12c7f32541a368ea4bdb Mon Sep 17 00:00:00 2001 From: Adam Locke Date: Wed, 31 Mar 2021 16:33:21 -0400 Subject: [PATCH 14/26] [DOCS] Add deprecation message for scripted fields (#95964) * [DOCS] Add deprecation message for scripted fields * Change deprecation to full block admonition. --- docs/management/managing-fields.asciidoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/management/managing-fields.asciidoc b/docs/management/managing-fields.asciidoc index 5cd5c1ffd6248..505f6853c7906 100644 --- a/docs/management/managing-fields.asciidoc +++ b/docs/management/managing-fields.asciidoc @@ -78,6 +78,7 @@ include::field-formatters/color-formatter.asciidoc[] [[scripted-fields]] === Scripted fields +deprecated::[7.13,Use {ref}/runtime.html[runtime fields] instead of scripted fields. Runtime fields support Painless scripts and provide greater flexibility.] Scripted fields compute data on the fly from the data in your {es} indices. The data is shown on the Discover tab as part of the document data, and you can use scripted fields in your visualizations. You query scripted fields with the <>, and can filter them using the filter bar. The scripted field values are computed at query time, so they aren't indexed and cannot be searched using the {kib} default @@ -87,7 +88,7 @@ WARNING: Computing data on the fly with scripted fields can be very resource int {kib} performance. Keep in mind that there's no built-in validation of a scripted field. If your scripts are buggy, you'll get exceptions whenever you try to view the dynamically generated data. -When you define a scripted field in {kib}, you have a choice of the {ref}/modules-scripting-expression.html[Lucene expressions] or the +When you define a scripted field in {kib}, you have a choice of the {ref}/modules-scripting-expression.html[Lucene expressions] or the {ref}/modules-scripting-painless.html[Painless] scripting language. You can reference any single value numeric field in your expressions, for example: From 96ccd3147735522b2811bd1e199f7552c01e620b Mon Sep 17 00:00:00 2001 From: Phillip Burch Date: Wed, 31 Mar 2021 15:55:46 -0500 Subject: [PATCH 15/26] Add anomaly detection telemetry (#95802) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ml/anomaly_detection/job_setup_screen.tsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx index a210831eef865..f6d739078002e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx @@ -24,6 +24,7 @@ import { FixedDatePicker } from '../../../../../../components/fixed_datepicker'; import { DEFAULT_K8S_PARTITION_FIELD } from '../../../../../../containers/ml/modules/metrics_k8s/module_descriptor'; import { MetricsExplorerKueryBar } from '../../../../metrics_explorer/components/kuery_bar'; import { convertKueryToElasticSearchQuery } from '../../../../../../utils/kuery'; +import { useUiTracker } from '../../../../../../../../observability/public'; interface Props { jobType: 'hosts' | 'kubernetes'; @@ -40,6 +41,7 @@ export const JobSetupScreen = (props: Props) => { const k = useMetricK8sModuleContext(); const [filter, setFilter] = useState(''); const [filterQuery, setFilterQuery] = useState(''); + const trackMetric = useUiTracker({ app: 'infra_metrics' }); const { createDerivedIndexPattern } = useSourceViaHttp({ sourceId: 'default', }); @@ -137,9 +139,25 @@ export const JobSetupScreen = (props: Props) => { useEffect(() => { if (setupStatus.type === 'succeeded') { + if (props.jobType === 'kubernetes') { + trackMetric({ metric: 'metrics_ml_anomaly_detection_k8s_enabled' }); + if ( + partitionField && + (partitionField.length !== 1 || partitionField[0] !== DEFAULT_K8S_PARTITION_FIELD) + ) { + trackMetric({ metric: 'metrics_ml_anomaly_detection_k8s_partition_changed' }); + } + } else { + trackMetric({ metric: 'metrics_ml_anomaly_detection_hosts_enabled' }); + if (partitionField) { + trackMetric({ metric: 'metrics_ml_anomaly_detection_hosts_partition_changed' }); + } + trackMetric({ metric: 'metrics_ml_anomaly_detection_hosts_enabled' }); + } + goHome(); } - }, [setupStatus, goHome]); + }, [setupStatus, props.jobType, partitionField, trackMetric, goHome]); return ( <> From 1f5ee4c869432e17a6c2c157cebdce4d931e074f Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 31 Mar 2021 14:03:22 -0700 Subject: [PATCH 16/26] [ci-stats] move ui-shared-deps metrics to page load assets group (#95957) Co-authored-by: spalger --- packages/kbn-ui-shared-deps/webpack.config.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index 135884fbf13e7..76e6843bea2f8 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -177,22 +177,22 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ compiler.hooks.emit.tap('MetricsPlugin', (compilation) => { const metrics = [ { - group: '@kbn/ui-shared-deps asset size', - id: 'kbn-ui-shared-deps.js', + group: 'page load bundle size', + id: 'kbnUiSharedDeps-js', value: compilation.assets['kbn-ui-shared-deps.js'].size(), }, { - group: '@kbn/ui-shared-deps asset size', - id: 'kbn-ui-shared-deps.@elastic.js', - value: compilation.assets['kbn-ui-shared-deps.@elastic.js'].size(), - }, - { - group: '@kbn/ui-shared-deps asset size', - id: 'css', + group: 'page load bundle size', + id: 'kbnUiSharedDeps-css', value: compilation.assets['kbn-ui-shared-deps.css'].size() + compilation.assets['kbn-ui-shared-deps.v7.light.css'].size(), }, + { + group: 'page load bundle size', + id: 'kbnUiSharedDeps-elastic', + value: compilation.assets['kbn-ui-shared-deps.@elastic.js'].size(), + }, ]; compilation.emitAsset( From 16a176af98e46a074d939a73c7cca3ef91aa0a39 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 31 Mar 2021 14:04:42 -0700 Subject: [PATCH 17/26] never run webpack with --progress on CI (#95967) Co-authored-by: spalger --- packages/kbn-pm/package.json | 2 +- x-pack/plugins/canvas/scripts/shareable_runtime.js | 4 ++-- x-pack/plugins/canvas/scripts/storybook.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 0fa79fff6e0d9..050aadd402d8a 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -9,7 +9,7 @@ }, "scripts": { "build": "../../node_modules/.bin/webpack", - "kbn:watch": "../../node_modules/.bin/webpack --watch --progress", + "kbn:watch": "../../node_modules/.bin/webpack --watch", "prettier": "../../node_modules/.bin/prettier --write './src/**/*.ts'" }, "devDependencies": { diff --git a/x-pack/plugins/canvas/scripts/shareable_runtime.js b/x-pack/plugins/canvas/scripts/shareable_runtime.js index 7f7f6d235c984..b760a92811b8e 100644 --- a/x-pack/plugins/canvas/scripts/shareable_runtime.js +++ b/x-pack/plugins/canvas/scripts/shareable_runtime.js @@ -56,7 +56,7 @@ run( 'webpack-dev-server', '--config', webpackConfig, - ...(process.stdout.isTTY ? ['--progress'] : []), + ...(process.stdout.isTTY && !process.env.CI ? ['--progress'] : []), '--hide-modules', '--display-entrypoints', 'false', @@ -93,7 +93,7 @@ run( '--config', webpackConfig, '--hide-modules', - ...(process.stdout.isTTY ? ['--progress'] : []), + ...(process.stdout.isTTY && !process.env.CI ? ['--progress'] : []), ], { ...options, diff --git a/x-pack/plugins/canvas/scripts/storybook.js b/x-pack/plugins/canvas/scripts/storybook.js index 88af1cf6d38bb..e6b8a66b9026f 100644 --- a/x-pack/plugins/canvas/scripts/storybook.js +++ b/x-pack/plugins/canvas/scripts/storybook.js @@ -44,7 +44,7 @@ run( 'webpack', '--config', 'x-pack/plugins/canvas/storybook/webpack.dll.config.js', - '--progress', + ...(process.stdout.isTTY && !process.env.CI ? ['--progress'] : []), '--hide-modules', '--display-entrypoints', 'false', From e285c7c214c72a31cdd7cec6fce8dc07c505d430 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 31 Mar 2021 14:36:48 -0700 Subject: [PATCH 18/26] [DOCS] Adds deprecation admonitions (#95847) --- .../legacy/create.asciidoc | 2 +- .../legacy/delete.asciidoc | 2 +- .../legacy/execute.asciidoc | 2 +- .../legacy/get.asciidoc | 2 +- .../legacy/get_all.asciidoc | 2 +- .../legacy/list.asciidoc | 2 +- .../legacy/update.asciidoc | 2 +- docs/api/alerting/legacy/create.asciidoc | 2 +- docs/api/alerting/legacy/delete.asciidoc | 2 +- docs/api/alerting/legacy/disable.asciidoc | 2 +- docs/api/alerting/legacy/enable.asciidoc | 2 +- docs/api/alerting/legacy/find.asciidoc | 2 +- docs/api/alerting/legacy/get.asciidoc | 2 +- docs/api/alerting/legacy/health.asciidoc | 2 +- docs/api/alerting/legacy/list.asciidoc | 2 +- docs/api/alerting/legacy/mute.asciidoc | 2 +- docs/api/alerting/legacy/mute_all.asciidoc | 2 +- docs/api/alerting/legacy/unmute.asciidoc | 2 +- docs/api/alerting/legacy/unmute_all.asciidoc | 2 +- docs/api/alerting/legacy/update.asciidoc | 2 +- docs/settings/reporting-settings.asciidoc | 18 ++++---- docs/setup/settings.asciidoc | 44 +++++++++++-------- docs/user/dashboard/timelion.asciidoc | 12 +---- 23 files changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/api/actions-and-connectors/legacy/create.asciidoc b/docs/api/actions-and-connectors/legacy/create.asciidoc index af4feddcb80fb..0361c4222986b 100644 --- a/docs/api/actions-and-connectors/legacy/create.asciidoc +++ b/docs/api/actions-and-connectors/legacy/create.asciidoc @@ -4,7 +4,7 @@ Legacy Create connector ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Creates a connector. diff --git a/docs/api/actions-and-connectors/legacy/delete.asciidoc b/docs/api/actions-and-connectors/legacy/delete.asciidoc index 170fceba2d157..9ec2c0d392a96 100644 --- a/docs/api/actions-and-connectors/legacy/delete.asciidoc +++ b/docs/api/actions-and-connectors/legacy/delete.asciidoc @@ -4,7 +4,7 @@ Legacy Delete connector ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Deletes a connector by ID. diff --git a/docs/api/actions-and-connectors/legacy/execute.asciidoc b/docs/api/actions-and-connectors/legacy/execute.asciidoc index 200844ab72f17..f01aa1585b192 100644 --- a/docs/api/actions-and-connectors/legacy/execute.asciidoc +++ b/docs/api/actions-and-connectors/legacy/execute.asciidoc @@ -4,7 +4,7 @@ Legacy Execute connector ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Executes a connector by ID. diff --git a/docs/api/actions-and-connectors/legacy/get.asciidoc b/docs/api/actions-and-connectors/legacy/get.asciidoc index 1b138fb7032e0..6413fce558f5b 100644 --- a/docs/api/actions-and-connectors/legacy/get.asciidoc +++ b/docs/api/actions-and-connectors/legacy/get.asciidoc @@ -4,7 +4,7 @@ Legacy Get connector ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Retrieves a connector by ID. diff --git a/docs/api/actions-and-connectors/legacy/get_all.asciidoc b/docs/api/actions-and-connectors/legacy/get_all.asciidoc index ba235955c005e..191eccb6f8d39 100644 --- a/docs/api/actions-and-connectors/legacy/get_all.asciidoc +++ b/docs/api/actions-and-connectors/legacy/get_all.asciidoc @@ -4,7 +4,7 @@ Legacy Get all connector ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Retrieves all connectors. diff --git a/docs/api/actions-and-connectors/legacy/list.asciidoc b/docs/api/actions-and-connectors/legacy/list.asciidoc index 8acfd5415af57..d78838dcbe974 100644 --- a/docs/api/actions-and-connectors/legacy/list.asciidoc +++ b/docs/api/actions-and-connectors/legacy/list.asciidoc @@ -4,7 +4,7 @@ Legacy List all connector types ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Retrieves a list of all connector types. diff --git a/docs/api/actions-and-connectors/legacy/update.asciidoc b/docs/api/actions-and-connectors/legacy/update.asciidoc index 517daf9a40dca..6a33e765cf063 100644 --- a/docs/api/actions-and-connectors/legacy/update.asciidoc +++ b/docs/api/actions-and-connectors/legacy/update.asciidoc @@ -4,7 +4,7 @@ Legacy Update connector ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Updates the attributes for an existing connector. diff --git a/docs/api/alerting/legacy/create.asciidoc b/docs/api/alerting/legacy/create.asciidoc index 5c594d64a3f45..8363569541356 100644 --- a/docs/api/alerting/legacy/create.asciidoc +++ b/docs/api/alerting/legacy/create.asciidoc @@ -4,7 +4,7 @@ Legacy create alert ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Create {kib} alerts. diff --git a/docs/api/alerting/legacy/delete.asciidoc b/docs/api/alerting/legacy/delete.asciidoc index 68851973cab5b..2af420f2bc34e 100644 --- a/docs/api/alerting/legacy/delete.asciidoc +++ b/docs/api/alerting/legacy/delete.asciidoc @@ -4,7 +4,7 @@ Legacy delete alert ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Permanently remove an alert. diff --git a/docs/api/alerting/legacy/disable.asciidoc b/docs/api/alerting/legacy/disable.asciidoc index 56e06371570c2..1a9b928bfba78 100644 --- a/docs/api/alerting/legacy/disable.asciidoc +++ b/docs/api/alerting/legacy/disable.asciidoc @@ -4,7 +4,7 @@ Legacy disable alert ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Disable an alert. diff --git a/docs/api/alerting/legacy/enable.asciidoc b/docs/api/alerting/legacy/enable.asciidoc index 913d96a84352b..da4b466d6fda4 100644 --- a/docs/api/alerting/legacy/enable.asciidoc +++ b/docs/api/alerting/legacy/enable.asciidoc @@ -4,7 +4,7 @@ Legacy enable alert ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Enable an alert. diff --git a/docs/api/alerting/legacy/find.asciidoc b/docs/api/alerting/legacy/find.asciidoc index 94d9bc425bd21..7c493e9c8eb5b 100644 --- a/docs/api/alerting/legacy/find.asciidoc +++ b/docs/api/alerting/legacy/find.asciidoc @@ -4,7 +4,7 @@ Legacy find alerts ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Retrieve a paginated set of alerts based on condition. diff --git a/docs/api/alerting/legacy/get.asciidoc b/docs/api/alerting/legacy/get.asciidoc index f1014d18e8774..ee0f52f51005a 100644 --- a/docs/api/alerting/legacy/get.asciidoc +++ b/docs/api/alerting/legacy/get.asciidoc @@ -4,7 +4,7 @@ Legacy get alert ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Retrieve an alert by ID. diff --git a/docs/api/alerting/legacy/health.asciidoc b/docs/api/alerting/legacy/health.asciidoc index b25307fb5efd1..68f04cc715bd7 100644 --- a/docs/api/alerting/legacy/health.asciidoc +++ b/docs/api/alerting/legacy/health.asciidoc @@ -4,7 +4,7 @@ Legacy get Alerting framework health ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Retrieve the health status of the Alerting framework. diff --git a/docs/api/alerting/legacy/list.asciidoc b/docs/api/alerting/legacy/list.asciidoc index e9ef3bbc27cd9..be37be36cd0e8 100644 --- a/docs/api/alerting/legacy/list.asciidoc +++ b/docs/api/alerting/legacy/list.asciidoc @@ -4,7 +4,7 @@ Legacy list all alert types ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Retrieve a list of all alert types. diff --git a/docs/api/alerting/legacy/mute.asciidoc b/docs/api/alerting/legacy/mute.asciidoc index dff42f5911e53..cf7adc446a2fd 100644 --- a/docs/api/alerting/legacy/mute.asciidoc +++ b/docs/api/alerting/legacy/mute.asciidoc @@ -4,7 +4,7 @@ Legacy mute alert instance ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Mute an alert instance. diff --git a/docs/api/alerting/legacy/mute_all.asciidoc b/docs/api/alerting/legacy/mute_all.asciidoc index df89fa15d1590..bc865480340e2 100644 --- a/docs/api/alerting/legacy/mute_all.asciidoc +++ b/docs/api/alerting/legacy/mute_all.asciidoc @@ -4,7 +4,7 @@ Legacy mute all alert instances ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Mute all alert instances. diff --git a/docs/api/alerting/legacy/unmute.asciidoc b/docs/api/alerting/legacy/unmute.asciidoc index 0be7e40dc1a19..300cf71b57a01 100644 --- a/docs/api/alerting/legacy/unmute.asciidoc +++ b/docs/api/alerting/legacy/unmute.asciidoc @@ -4,7 +4,7 @@ Legacy unmute alert instance ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Unmute an alert instance. diff --git a/docs/api/alerting/legacy/unmute_all.asciidoc b/docs/api/alerting/legacy/unmute_all.asciidoc index 8687c2d2fe8bb..3b0a7afe5f44d 100644 --- a/docs/api/alerting/legacy/unmute_all.asciidoc +++ b/docs/api/alerting/legacy/unmute_all.asciidoc @@ -4,7 +4,7 @@ Legacy unmute all alert instances ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Unmute all alert instances. diff --git a/docs/api/alerting/legacy/update.asciidoc b/docs/api/alerting/legacy/update.asciidoc index bffdf26c31400..b9cce995660e6 100644 --- a/docs/api/alerting/legacy/update.asciidoc +++ b/docs/api/alerting/legacy/update.asciidoc @@ -4,7 +4,7 @@ Legacy update alert ++++ -WARNING: Deprecated in 7.13.0. Use <> instead. +deprecated::[7.13.0,Use <> instead.] Update the attributes for an existing alert. diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index cef5a953fded4..9bb11f3f99a15 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -260,19 +260,21 @@ For information about {kib} memory limits, see <> setting. Defaults to `.reporting`. - | `xpack.reporting.capture.networkPolicy` | Capturing a screenshot from a {kib} page involves sending out requests for all the linked web assets. For example, a Markdown visualization can show an image from a remote server. You can configure what type of requests to allow or filter by setting a <> for Reporting. +| `xpack.reporting.index` + | deprecated:[7.11.0,This setting will be removed in 8.0.] Multitenancy by + changing `kibana.index` will not be supported starting in 8.0. See + https://ela.st/kbn-remove-legacy-multitenancy[8.0 Breaking Changes] for more + details. Reporting uses a weekly index in {es} to store the reporting job and + the report content. The index is automatically created if it does not already + exist. Configure this to a unique value, beginning with `.reporting-`, for + every {kib} instance that has a unique <> + setting. Defaults to `.reporting`. + | `xpack.reporting.roles.allow` | Specifies the roles in addition to superusers that can use reporting. Defaults to `[ "reporting_user" ]`. + diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index e5cbc2c7ea6db..73b268e1e48b3 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -25,12 +25,14 @@ which may cause a delay before pages start being served. Set to `false` to disable Console. *Default: `true`* | `cpu.cgroup.path.override:` - | *deprecated* This setting has been renamed to <> -and the old name will no longer be supported as of 8.0. + | deprecated:[7.10.0,"This setting will no longer be supported as of 8.0."] + This setting has been renamed to + <>. | `cpuacct.cgroup.path.override:` - | *deprecated* This setting has been renamed to <> -and the old name will no longer be supported as of 8.0. + | deprecated:[7.10.0,"This setting will no longer be supported as of 8.0."] + This setting has been renamed to + <>. | `csp.rules:` | A https://w3c.github.io/webappsec-csp/[content-security-policy] template @@ -64,10 +66,12 @@ To enable SSL/TLS for outbound connections to {es}, use the `https` protocol in this setting. | `elasticsearch.logQueries:` - | *deprecated* This setting is no longer used and will get removed in Kibana 8.0. Instead, configure the `elasticsearch.query` logger. -This is useful for seeing the query DSL generated by applications that -currently do not have an inspector, for example Timelion and Monitoring. -*Default: `false`* + | deprecated:[7.12.0,"This setting is no longer used and will be removed in Kibana 8.0."] + Instead, configure the `elasticsearch.query` logger. + + + This is useful for seeing the query DSL generated by applications that + currently do not have an inspector, for example Timelion and Monitoring. + *Default: `false`* The following example shows a valid `elasticsearch.query` logger configuration: |=== @@ -240,18 +244,22 @@ on the {kib} index at startup. {kib} users still need to authenticate with | Enables use of interpreter in Visualize. *Default: `true`* | `kibana.defaultAppId:` - | *deprecated* This setting is deprecated and will get removed in Kibana 8.0. -Please use the `defaultRoute` advanced setting instead. -The default application to load. *Default: `"home"`* + | deprecated:[7.9.0,This setting will be removed in Kibana 8.0.] + Instead, use the <>. + + + The default application to load. *Default: `"home"`* |[[kibana-index]] `kibana.index:` - | *deprecated* This setting is deprecated and will be removed in 8.0. Multitenancy by changing -`kibana.index` will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy[8.0 Breaking Changes] -for more details. {kib} uses an index in {es} to store saved searches, visualizations, and -dashboards. {kib} creates a new index if the index doesn’t already exist. -If you configure a custom index, the name must be lowercase, and conform to the -{es} {ref}/indices-create-index.html[index name limitations]. -*Default: `".kibana"`* + | deprecated:[7.11.0,This setting will be removed in 8.0.] Multitenancy by + changing `kibana.index` will not be supported starting in 8.0. See + https://ela.st/kbn-remove-legacy-multitenancy[8.0 Breaking Changes] for more + details. + + + {kib} uses an index in {es} to store saved searches, visualizations, and + dashboards. {kib} creates a new index if the index doesn’t already exist. If + you configure a custom index, the name must be lowercase, and conform to the + {es} {ref}/indices-create-index.html[index name limitations]. + *Default: `".kibana"`* | `kibana.autocompleteTimeout:` {ess-icon} | Time in milliseconds to wait for autocomplete suggestions from {es}. diff --git a/docs/user/dashboard/timelion.asciidoc b/docs/user/dashboard/timelion.asciidoc index 676c46368a6ee..80ce77f30c75e 100644 --- a/docs/user/dashboard/timelion.asciidoc +++ b/docs/user/dashboard/timelion.asciidoc @@ -4,17 +4,7 @@ Instead of using a visual editor to create charts, you define a graph by chaining functions together, using the *Timelion*-specific syntax. The syntax enables some features that classical point series charts don't offer, such as pulling data from different indices or data sources into one graph. -[NOTE] -==== -Timelion app deprecation - -*Timelion* is still supported, the *Timelion app* is deprecated in 7.0, replaced by -dashboard features. In 8.0 and later, the *Timelion app* is removed from {kib}. -To prepare for the removal of *Timelion app*, you must migrate *Timelion app* worksheets to a dashboard. - -For information on how to migrate *Timelion app* worksheets, refer to the -link:https://www.elastic.co/guide/en/kibana/7.10/release-notes-7.10.0.html#deprecation-v7.10.0[7.10.0 Release Notes]. -==== +deprecated::[7.0.0,"*Timelion* is still supported. The *Timelion app* is deprecated in 7.0, replaced by dashboard features. In 8.0 and later, the *Timelion app* is removed from {kib}. To prepare for the removal of *Timelion app*, you must migrate *Timelion app* worksheets to a dashboard. For information on how to migrate *Timelion app* worksheets, refer to the link:https://www.elastic.co/guide/en/kibana/7.10/release-notes-7.10.0.html#deprecation-v7.10.0[7.10.0 Release Notes]."] [float] ==== Timelion expressions From 65deb5a46be1c3f8c37fa2288fe0e659662d7388 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 31 Mar 2021 17:02:42 -0500 Subject: [PATCH 19/26] Index pattern scripted field / runtime field usage collection (#95366) * add index pattern telemetry --- ...ata-server.indexpatternsserviceprovider.md | 2 +- ...rver.indexpatternsserviceprovider.setup.md | 6 +- ...rver.indexpatternsserviceprovider.start.md | 4 +- .../index_patterns/index_patterns_service.ts | 66 +++--- ...ter_index_pattern_usage_collection.test.ts | 140 +++++++++++++ ...register_index_pattern_usage_collection.ts | 193 ++++++++++++++++++ src/plugins/data/server/plugin.ts | 12 +- .../data/server/search/search_service.test.ts | 4 +- .../data/server/search/search_service.ts | 4 +- src/plugins/data/server/server.api.md | 12 +- src/plugins/telemetry/schema/oss_plugins.json | 75 +++++++ 11 files changed, 476 insertions(+), 42 deletions(-) create mode 100644 src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.test.ts create mode 100644 src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.ts diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.md index d408f00e33c9e..b5c7d8931ad4b 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.md @@ -14,6 +14,6 @@ export declare class IndexPatternsServiceProvider implements PluginSignature: ```typescript -setup(core: CoreSetup, { expressions }: IndexPatternsServiceSetupDeps): void; +setup(core: CoreSetup, { expressions, usageCollection }: IndexPatternsServiceSetupDeps): void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| core | CoreSetup<DataPluginStartDependencies, DataPluginStart> | | -| { expressions } | IndexPatternsServiceSetupDeps | | +| core | CoreSetup<IndexPatternsServiceStartDeps, DataPluginStart> | | +| { expressions, usageCollection } | IndexPatternsServiceSetupDeps | | Returns: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md index 98f9310c6d98c..88079bb2fa3cb 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md @@ -8,7 +8,7 @@ ```typescript start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): { - indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract, elasticsearchClient: ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient) => Promise; }; ``` @@ -22,6 +22,6 @@ start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): Returns: `{ - indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract, elasticsearchClient: ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient) => Promise; }` diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index 5d703021b94da..c4cc2073ef78f 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -13,9 +13,11 @@ import { Logger, SavedObjectsClientContract, ElasticsearchClient, + UiSettingsServiceStart, } from 'kibana/server'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; -import { DataPluginStartDependencies, DataPluginStart } from '../plugin'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { DataPluginStart } from '../plugin'; import { registerRoutes } from './routes'; import { indexPatternSavedObjectType } from '../saved_objects'; import { capabilitiesProvider } from './capabilities_provider'; @@ -25,6 +27,7 @@ import { getIndexPatternLoad } from './expressions'; import { UiSettingsServerToCommon } from './ui_settings_wrapper'; import { IndexPatternsApiServer } from './index_patterns_api_client'; import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper'; +import { registerIndexPatternsUsageCollector } from './register_index_pattern_usage_collection'; export interface IndexPatternsServiceStart { indexPatternsServiceFactory: ( @@ -35,6 +38,8 @@ export interface IndexPatternsServiceStart { export interface IndexPatternsServiceSetupDeps { expressions: ExpressionsServerSetup; + logger: Logger; + usageCollection?: UsageCollectionSetup; } export interface IndexPatternsServiceStartDeps { @@ -42,10 +47,39 @@ export interface IndexPatternsServiceStartDeps { logger: Logger; } +export const indexPatternsServiceFactory = ({ + logger, + uiSettings, + fieldFormats, +}: { + logger: Logger; + uiSettings: UiSettingsServiceStart; + fieldFormats: FieldFormatsStart; +}) => async ( + savedObjectsClient: SavedObjectsClientContract, + elasticsearchClient: ElasticsearchClient +) => { + const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); + const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient); + + return new IndexPatternsCommonService({ + uiSettings: new UiSettingsServerToCommon(uiSettingsClient), + savedObjectsClient: new SavedObjectsClientServerToCommon(savedObjectsClient), + apiClient: new IndexPatternsApiServer(elasticsearchClient), + fieldFormats: formats, + onError: (error) => { + logger.error(error); + }, + onNotification: ({ title, text }) => { + logger.warn(`${title} : ${text}`); + }, + }); +}; + export class IndexPatternsServiceProvider implements Plugin { public setup( - core: CoreSetup, - { expressions }: IndexPatternsServiceSetupDeps + core: CoreSetup, + { expressions, usageCollection }: IndexPatternsServiceSetupDeps ) { core.savedObjects.registerType(indexPatternSavedObjectType); core.capabilities.registerProvider(capabilitiesProvider); @@ -53,32 +87,18 @@ export class IndexPatternsServiceProvider implements Plugin { - const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); - const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient); - - return new IndexPatternsCommonService({ - uiSettings: new UiSettingsServerToCommon(uiSettingsClient), - savedObjectsClient: new SavedObjectsClientServerToCommon(savedObjectsClient), - apiClient: new IndexPatternsApiServer(elasticsearchClient), - fieldFormats: formats, - onError: (error) => { - logger.error(error); - }, - onNotification: ({ title, text }) => { - logger.warn(`${title} : ${text}`); - }, - }); - }, + indexPatternsServiceFactory: indexPatternsServiceFactory({ + logger, + uiSettings, + fieldFormats, + }), }; } } diff --git a/src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.test.ts b/src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.test.ts new file mode 100644 index 0000000000000..c43431e10731a --- /dev/null +++ b/src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + minMaxAvgLoC, + updateMin, + updateMax, + getIndexPatternTelemetry, +} from './register_index_pattern_usage_collection'; +import { IndexPatternsCommonService } from '..'; + +const scriptA = 'emit(0);'; +const scriptB = 'emit(1);\nemit(2);'; +const scriptC = 'emit(3);\nemit(4)\nemit(5)'; + +const scriptedFieldA = { script: scriptA }; +const scriptedFieldB = { script: scriptB }; +const scriptedFieldC = { script: scriptC }; + +const runtimeFieldA = { runtimeField: { script: { source: scriptA } } }; +const runtimeFieldB = { runtimeField: { script: { source: scriptB } } }; +const runtimeFieldC = { runtimeField: { script: { source: scriptC } } }; + +const indexPatterns = ({ + getIds: async () => [1, 2, 3], + get: jest.fn().mockResolvedValue({ + getScriptedFields: () => [], + fields: [], + }), +} as any) as IndexPatternsCommonService; + +describe('index pattern usage collection', () => { + it('minMaxAvgLoC calculates min, max, and average ', () => { + const scripts = [scriptA, scriptB, scriptC]; + expect(minMaxAvgLoC(scripts)).toEqual({ min: 1, max: 3, avg: 2 }); + expect(minMaxAvgLoC([undefined, undefined, undefined])).toEqual({ min: 0, max: 0, avg: 0 }); + }); + + it('updateMin returns minimum value', () => { + expect(updateMin(undefined, 1)).toEqual(1); + expect(updateMin(1, 0)).toEqual(0); + }); + + it('updateMax returns maximum value', () => { + expect(updateMax(undefined, 1)).toEqual(1); + expect(updateMax(1, 0)).toEqual(1); + }); + + describe('calculates index pattern usage', () => { + const countSummaryDefault = { + min: undefined, + max: undefined, + avg: undefined, + }; + + it('when there are no runtime fields or scripted fields', async () => { + expect(await getIndexPatternTelemetry(indexPatterns)).toEqual({ + indexPatternsCount: 3, + indexPatternsWithScriptedFieldCount: 0, + indexPatternsWithRuntimeFieldCount: 0, + scriptedFieldCount: 0, + runtimeFieldCount: 0, + perIndexPattern: { + scriptedFieldCount: countSummaryDefault, + runtimeFieldCount: countSummaryDefault, + scriptedFieldLineCount: countSummaryDefault, + runtimeFieldLineCount: countSummaryDefault, + }, + }); + }); + + it('when there are both runtime fields or scripted fields', async () => { + indexPatterns.get = jest.fn().mockResolvedValue({ + getScriptedFields: () => [scriptedFieldA, scriptedFieldB, scriptedFieldC], + fields: [runtimeFieldA, runtimeFieldB, runtimeFieldC], + }); + + expect(await getIndexPatternTelemetry(indexPatterns)).toEqual({ + indexPatternsCount: 3, + indexPatternsWithScriptedFieldCount: 3, + indexPatternsWithRuntimeFieldCount: 3, + scriptedFieldCount: 9, + runtimeFieldCount: 9, + perIndexPattern: { + scriptedFieldCount: { min: 3, max: 3, avg: 3 }, + runtimeFieldCount: { min: 3, max: 3, avg: 3 }, + scriptedFieldLineCount: { min: 1, max: 3, avg: 2 }, + runtimeFieldLineCount: { min: 1, max: 3, avg: 2 }, + }, + }); + }); + + it('when there are only runtime fields', async () => { + indexPatterns.get = jest.fn().mockResolvedValue({ + getScriptedFields: () => [], + fields: [runtimeFieldA, runtimeFieldB, runtimeFieldC], + }); + + expect(await getIndexPatternTelemetry(indexPatterns)).toEqual({ + indexPatternsCount: 3, + indexPatternsWithScriptedFieldCount: 0, + indexPatternsWithRuntimeFieldCount: 3, + scriptedFieldCount: 0, + runtimeFieldCount: 9, + perIndexPattern: { + scriptedFieldCount: countSummaryDefault, + runtimeFieldCount: { min: 3, max: 3, avg: 3 }, + scriptedFieldLineCount: countSummaryDefault, + runtimeFieldLineCount: { min: 1, max: 3, avg: 2 }, + }, + }); + }); + + it('when there are only scripted fields', async () => { + indexPatterns.get = jest.fn().mockResolvedValue({ + getScriptedFields: () => [scriptedFieldA, scriptedFieldB, scriptedFieldC], + fields: [], + }); + + expect(await getIndexPatternTelemetry(indexPatterns)).toEqual({ + indexPatternsCount: 3, + indexPatternsWithScriptedFieldCount: 3, + indexPatternsWithRuntimeFieldCount: 0, + scriptedFieldCount: 9, + runtimeFieldCount: 0, + perIndexPattern: { + scriptedFieldCount: { min: 3, max: 3, avg: 3 }, + runtimeFieldCount: countSummaryDefault, + scriptedFieldLineCount: { min: 1, max: 3, avg: 2 }, + runtimeFieldLineCount: countSummaryDefault, + }, + }); + }); + }); +}); diff --git a/src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.ts b/src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.ts new file mode 100644 index 0000000000000..36c2a59ce2753 --- /dev/null +++ b/src/plugins/data/server/index_patterns/register_index_pattern_usage_collection.ts @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { StartServicesAccessor } from 'src/core/server'; +import { IndexPatternsCommonService } from '..'; +import { SavedObjectsClient } from '../../../../core/server'; +import { DataPluginStartDependencies, DataPluginStart } from '../plugin'; + +interface CountSummary { + min?: number; + max?: number; + avg?: number; +} + +interface IndexPatternUsage { + indexPatternsCount: number; + indexPatternsWithScriptedFieldCount: number; + indexPatternsWithRuntimeFieldCount: number; + scriptedFieldCount: number; + runtimeFieldCount: number; + perIndexPattern: { + scriptedFieldCount: CountSummary; + runtimeFieldCount: CountSummary; + scriptedFieldLineCount: CountSummary; + runtimeFieldLineCount: CountSummary; + }; +} + +export const minMaxAvgLoC = (scripts: Array) => { + const lengths = scripts.map((script) => script?.split(/\r\n|\r|\n/).length || 0).sort(); + return { + min: lengths[0], + max: lengths[lengths.length - 1], + avg: lengths.reduce((col, count) => col + count, 0) / lengths.length, + }; +}; + +export const updateMin = (currentMin: number | undefined, newVal: number): number => { + if (currentMin === undefined || currentMin > newVal) { + return newVal; + } else { + return currentMin; + } +}; + +export const updateMax = (currentMax: number | undefined, newVal: number): number => { + if (currentMax === undefined || currentMax < newVal) { + return newVal; + } else { + return currentMax; + } +}; + +export async function getIndexPatternTelemetry(indexPatterns: IndexPatternsCommonService) { + const ids = await indexPatterns.getIds(); + + const countSummaryDefaults: CountSummary = { + min: undefined, + max: undefined, + avg: undefined, + }; + + const results = { + indexPatternsCount: ids.length, + indexPatternsWithScriptedFieldCount: 0, + indexPatternsWithRuntimeFieldCount: 0, + scriptedFieldCount: 0, + runtimeFieldCount: 0, + perIndexPattern: { + scriptedFieldCount: { ...countSummaryDefaults }, + runtimeFieldCount: { ...countSummaryDefaults }, + scriptedFieldLineCount: { ...countSummaryDefaults }, + runtimeFieldLineCount: { ...countSummaryDefaults }, + }, + }; + + await ids.reduce(async (col, id) => { + await col; + const ip = await indexPatterns.get(id); + + const scriptedFields = ip.getScriptedFields(); + const runtimeFields = ip.fields.filter((fld) => !!fld.runtimeField); + + if (scriptedFields.length > 0) { + // increment counts + results.indexPatternsWithScriptedFieldCount++; + results.scriptedFieldCount += scriptedFields.length; + + // calc LoC + results.perIndexPattern.scriptedFieldLineCount = minMaxAvgLoC( + scriptedFields.map((fld) => fld.script || '') + ); + + // calc field counts + results.perIndexPattern.scriptedFieldCount.min = updateMin( + results.perIndexPattern.scriptedFieldCount.min, + scriptedFields.length + ); + results.perIndexPattern.scriptedFieldCount.max = updateMax( + results.perIndexPattern.scriptedFieldCount.max, + scriptedFields.length + ); + results.perIndexPattern.scriptedFieldCount.avg = + results.scriptedFieldCount / results.indexPatternsWithScriptedFieldCount; + } + + if (runtimeFields.length > 0) { + // increment counts + results.indexPatternsWithRuntimeFieldCount++; + results.runtimeFieldCount += runtimeFields.length; + + // calc LoC + const runtimeFieldScripts = runtimeFields.map( + (fld) => fld.runtimeField?.script?.source || '' + ); + results.perIndexPattern.runtimeFieldLineCount = minMaxAvgLoC(runtimeFieldScripts); + + // calc field counts + results.perIndexPattern.runtimeFieldCount.min = updateMin( + results.perIndexPattern.runtimeFieldCount.min, + runtimeFields.length + ); + results.perIndexPattern.runtimeFieldCount.max = updateMax( + results.perIndexPattern.runtimeFieldCount.max, + runtimeFields.length + ); + results.perIndexPattern.runtimeFieldCount.avg = + results.runtimeFieldCount / results.indexPatternsWithRuntimeFieldCount; + } + }, Promise.resolve()); + + return results; +} + +export function registerIndexPatternsUsageCollector( + getStartServices: StartServicesAccessor, + usageCollection?: UsageCollectionSetup +): void { + if (!usageCollection) { + return; + } + + const indexPatternUsageCollector = usageCollection.makeUsageCollector({ + type: 'index-patterns', + isReady: () => true, + fetch: async () => { + const [{ savedObjects, elasticsearch }, , { indexPatterns }] = await getStartServices(); + const indexPatternService = await indexPatterns.indexPatternsServiceFactory( + new SavedObjectsClient(savedObjects.createInternalRepository()), + elasticsearch.client.asInternalUser + ); + + return await getIndexPatternTelemetry(indexPatternService); + }, + schema: { + indexPatternsCount: { type: 'long' }, + indexPatternsWithScriptedFieldCount: { type: 'long' }, + indexPatternsWithRuntimeFieldCount: { type: 'long' }, + scriptedFieldCount: { type: 'long' }, + runtimeFieldCount: { type: 'long' }, + perIndexPattern: { + scriptedFieldCount: { + min: { type: 'long' }, + max: { type: 'long' }, + avg: { type: 'float' }, + }, + runtimeFieldCount: { + min: { type: 'long' }, + max: { type: 'long' }, + avg: { type: 'float' }, + }, + scriptedFieldLineCount: { + min: { type: 'long' }, + max: { type: 'long' }, + avg: { type: 'float' }, + }, + runtimeFieldLineCount: { + min: { type: 'long' }, + max: { type: 'long' }, + avg: { type: 'float' }, + }, + }, + }, + }); + + usageCollection.registerCollector(indexPatternUsageCollector); +} diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index a7a7663d6981c..7b73802f1a34d 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -46,8 +46,10 @@ export interface DataPluginSetupDependencies { usageCollection?: UsageCollectionSetup; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DataPluginStartDependencies {} +export interface DataPluginStartDependencies { + fieldFormats: FieldFormatsStart; + logger: Logger; +} export class DataServerPlugin implements @@ -82,7 +84,11 @@ export class DataServerPlugin this.queryService.setup(core); this.autocompleteService.setup(core); this.kqlTelemetryService.setup(core, { usageCollection }); - this.indexPatterns.setup(core, { expressions }); + this.indexPatterns.setup(core, { + expressions, + logger: this.logger.get('indexPatterns'), + usageCollection, + }); core.uiSettings.register(getUiSettings()); diff --git a/src/plugins/data/server/search/search_service.test.ts b/src/plugins/data/server/search/search_service.test.ts index 192c133c94a04..d5a83efcc215f 100644 --- a/src/plugins/data/server/search/search_service.test.ts +++ b/src/plugins/data/server/search/search_service.test.ts @@ -10,7 +10,7 @@ import type { MockedKeys } from '@kbn/utility-types/jest'; import { CoreSetup, CoreStart, SavedObject } from '../../../../core/server'; import { coreMock } from '../../../../core/server/mocks'; -import { DataPluginStart } from '../plugin'; +import { DataPluginStart, DataPluginStartDependencies } from '../plugin'; import { createFieldFormatsStartMock } from '../field_formats/mocks'; import { createIndexPatternsStartMock } from '../index_patterns/mocks'; @@ -32,7 +32,7 @@ import { createSearchSessionsClientMock } from './mocks'; describe('Search service', () => { let plugin: SearchService; - let mockCoreSetup: MockedKeys>; + let mockCoreSetup: MockedKeys>; let mockCoreStart: MockedKeys; beforeEach(() => { diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index b62c5d26f691d..e53244fa7ff26 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -38,7 +38,7 @@ import { FieldFormatsStart } from '../field_formats'; import { IndexPatternsServiceStart } from '../index_patterns'; import { getCallMsearch, registerMsearchRoute, registerSearchRoute } from './routes'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; -import { DataPluginStart } from '../plugin'; +import { DataPluginStart, DataPluginStartDependencies } from '../plugin'; import { UsageCollectionSetup } from '../../../usage_collection/server'; import { registerUsageCollector } from './collectors/register'; import { usageProvider } from './collectors/usage'; @@ -114,7 +114,7 @@ export class SearchService implements Plugin { } public setup( - core: CoreSetup<{}, DataPluginStart>, + core: CoreSetup, { bfetch, expressions, usageCollection }: SearchServiceSetupDependencies ): ISearchSetup { const usage = usageCollection ? usageProvider(core) : undefined; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 1f043cc9aab4e..29a5a67239171 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -73,6 +73,7 @@ import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { UiCounterMetricType } from '@kbn/analytics'; import { Unit } from '@elastic/datemath'; +import { UsageCollectionSetup as UsageCollectionSetup_2 } from 'src/plugins/usage_collection/server'; // Warning: (ae-forgotten-export) The symbol "AggConfigSerialized" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "AggConfigOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -958,16 +959,14 @@ export { IndexPatternsService } // // @public (undocumented) export class IndexPatternsServiceProvider implements Plugin_3 { - // Warning: (ae-forgotten-export) The symbol "DataPluginStartDependencies" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStartDeps" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceSetupDeps" needs to be exported by the entry point index.d.ts // // (undocumented) - setup(core: CoreSetup_2, { expressions }: IndexPatternsServiceSetupDeps): void; - // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStartDeps" needs to be exported by the entry point index.d.ts - // + setup(core: CoreSetup_2, { expressions, usageCollection }: IndexPatternsServiceSetupDeps): void; // (undocumented) start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): { - indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract_2, elasticsearchClient: ElasticsearchClient_2) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient_2) => Promise; }; } @@ -1213,6 +1212,7 @@ export type ParsedInterval = ReturnType; export function parseInterval(interval: string): moment.Duration | null; // Warning: (ae-forgotten-export) The symbol "DataPluginSetupDependencies" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DataPluginStartDependencies" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "DataServerPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1522,7 +1522,7 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/plugin.ts:79:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:81:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts // src/plugins/data/server/search/types.ts:114:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 0ce727830ffdd..05ac1eb84089d 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -24,6 +24,81 @@ } } }, + "index-patterns": { + "properties": { + "indexPatternsCount": { + "type": "long" + }, + "indexPatternsWithScriptedFieldCount": { + "type": "long" + }, + "indexPatternsWithRuntimeFieldCount": { + "type": "long" + }, + "scriptedFieldCount": { + "type": "long" + }, + "runtimeFieldCount": { + "type": "long" + }, + "perIndexPattern": { + "properties": { + "scriptedFieldCount": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + }, + "runtimeFieldCount": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + }, + "scriptedFieldLineCount": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + }, + "runtimeFieldLineCount": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + } + } + } + } + }, "kql": { "properties": { "optInCount": { From aa81dc52f67952ef171b37b3b3801e062590cb11 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 31 Mar 2021 15:13:06 -0700 Subject: [PATCH 20/26] skip flaky suite (#96000) --- .../test/functional/apps/dashboard/reporting/download_csv.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts b/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts index d4a909f6a0474..c437cfaa8f5dc 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts +++ b/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts @@ -50,7 +50,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('csvDownloadStarted'); // validate toast panel }; - describe('Download CSV', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/96000 + describe.skip('Download CSV', () => { before('initialize tests', async () => { log.debug('ReportingPage:initTests'); await browser.setWindowSize(1600, 850); From c66937d4c69acbb4dafda45c0296d374bcaaa2b0 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 31 Mar 2021 15:19:58 -0700 Subject: [PATCH 21/26] skip suite blocking es promotion (#96001) --- x-pack/test/functional/apps/security/users.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/security/users.js b/x-pack/test/functional/apps/security/users.js index 0cab12bc6672f..250a2d4ed71f9 100644 --- a/x-pack/test/functional/apps/security/users.js +++ b/x-pack/test/functional/apps/security/users.js @@ -12,7 +12,8 @@ export default function ({ getService, getPageObjects }) { const config = getService('config'); const log = getService('log'); - describe('users', function () { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/96001 + describe.skip('users', function () { before(async () => { log.debug('users'); await PageObjects.settings.navigateTo(); From 0cdf44571867e561214b81e6472f608da080df80 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 31 Mar 2021 15:24:17 -0700 Subject: [PATCH 22/26] skip suite failing es promotion (#96002) --- x-pack/test/api_integration/apis/management/rollup/rollup.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/management/rollup/rollup.js b/x-pack/test/api_integration/apis/management/rollup/rollup.js index 4cb2ef6ea0fa0..a556c8071ca80 100644 --- a/x-pack/test/api_integration/apis/management/rollup/rollup.js +++ b/x-pack/test/api_integration/apis/management/rollup/rollup.js @@ -24,7 +24,8 @@ export default function ({ getService }) { cleanUp, } = registerHelpers(getService); - describe('jobs', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/96002 + describe.skip('jobs', () => { after(() => cleanUp()); describe('indices', () => { From e0a1fe18bee2aea20a1d708aa3954de9da886e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Thu, 1 Apr 2021 00:43:20 +0200 Subject: [PATCH 23/26] [APM] Fix spaces issue for static index patterns (#95799) --- x-pack/plugins/apm/kibana.json | 1 + .../create_static_index_pattern.test.ts | 21 ++++++++++++++++--- .../create_static_index_pattern.ts | 9 ++++++-- x-pack/plugins/apm/server/plugin.ts | 8 +++---- .../apm/server/routes/index_pattern.ts | 5 ++++- x-pack/plugins/apm/server/routes/typings.ts | 2 ++ 6 files changed, 35 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index a9a0149e72ce7..e340f8bf19126 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -12,6 +12,7 @@ "infra" ], "optionalPlugins": [ + "spaces", "cloud", "usageCollection", "taskManager", diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts index 1c33fcbd71dac..19163da449b90 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts @@ -36,7 +36,12 @@ describe('createStaticIndexPattern', () => { 'xpack.apm.autocreateApmIndexPattern': false, }); const savedObjectsClient = getMockSavedObjectsClient(); - await createStaticIndexPattern(setup, context, savedObjectsClient); + await createStaticIndexPattern( + setup, + context, + savedObjectsClient, + 'default' + ); expect(savedObjectsClient.create).not.toHaveBeenCalled(); }); @@ -53,7 +58,12 @@ describe('createStaticIndexPattern', () => { const savedObjectsClient = getMockSavedObjectsClient(); - await createStaticIndexPattern(setup, context, savedObjectsClient); + await createStaticIndexPattern( + setup, + context, + savedObjectsClient, + 'default' + ); expect(savedObjectsClient.create).not.toHaveBeenCalled(); }); @@ -70,7 +80,12 @@ describe('createStaticIndexPattern', () => { const savedObjectsClient = getMockSavedObjectsClient(); - await createStaticIndexPattern(setup, context, savedObjectsClient); + await createStaticIndexPattern( + setup, + context, + savedObjectsClient, + 'default' + ); expect(savedObjectsClient.create).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts index 0b7f82c0b8388..b91fb8342a212 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts @@ -20,7 +20,8 @@ import { getApmIndexPatternTitle } from './get_apm_index_pattern_title'; export async function createStaticIndexPattern( setup: Setup, context: APMRequestHandlerContext, - savedObjectsClient: InternalSavedObjectsClient + savedObjectsClient: InternalSavedObjectsClient, + spaceId: string | undefined ): Promise { return withApmSpan('create_static_index_pattern', async () => { const { config } = context; @@ -46,7 +47,11 @@ export async function createStaticIndexPattern( ...apmIndexPattern.attributes, title: apmIndexPatternTitle, }, - { id: APM_STATIC_INDEX_PATTERN_ID, overwrite: false } + { + id: APM_STATIC_INDEX_PATTERN_ID, + overwrite: false, + namespace: spaceId, + } ) ); return true; diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index f556374179c51..db96794627519 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -16,6 +16,7 @@ import { Plugin, PluginInitializerContext, } from 'src/core/server'; +import { SpacesPluginSetup } from '../../spaces/server'; import { APMConfig, APMXPackConfig } from '.'; import { mergeConfigs } from './index'; import { APMOSSPluginSetup } from '../../../../src/plugins/apm_oss/server'; @@ -65,6 +66,7 @@ export class APMPlugin implements Plugin { public setup( core: CoreSetup, plugins: { + spaces?: SpacesPluginSetup; apmOss: APMOSSPluginSetup; home: HomeServerPluginSetup; licensing: LicensingPluginSetup; @@ -148,11 +150,7 @@ export class APMPlugin implements Plugin { createApmApi().init(core, { config$: mergedConfig$, logger: this.logger!, - plugins: { - observability: plugins.observability, - security: plugins.security, - ml: plugins.ml, - }, + plugins, }); const boundGetApmIndices = async () => diff --git a/x-pack/plugins/apm/server/routes/index_pattern.ts b/x-pack/plugins/apm/server/routes/index_pattern.ts index fd7d2120ab6f5..3b800c23135ce 100644 --- a/x-pack/plugins/apm/server/routes/index_pattern.ts +++ b/x-pack/plugins/apm/server/routes/index_pattern.ts @@ -21,10 +21,13 @@ export const staticIndexPatternRoute = createRoute((core) => ({ getInternalSavedObjectsClient(core), ]); + const spaceId = context.plugins.spaces?.spacesService.getSpaceId(request); + const didCreateIndexPattern = await createStaticIndexPattern( setup, context, - savedObjectsClient + savedObjectsClient, + spaceId ); return { created: didCreateIndexPattern }; diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 1575041fb2f45..3ba24b4ed5268 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -14,6 +14,7 @@ import { } from 'src/core/server'; import { Observable } from 'rxjs'; import { RequiredKeys, DeepPartial } from 'utility-types'; +import { SpacesPluginStart } from '../../../spaces/server'; import { ObservabilityPluginSetup } from '../../../observability/server'; import { LicensingApiRequestHandlerContext } from '../../../licensing/server'; import { SecurityPluginSetup } from '../../../security/server'; @@ -93,6 +94,7 @@ export type APMRequestHandlerContext< config: APMConfig; logger: Logger; plugins: { + spaces?: SpacesPluginStart; observability?: ObservabilityPluginSetup; security?: SecurityPluginSetup; ml?: MlPluginSetup; From 5db802765726cf4d4a58f9ba26bba9b68f2e7df9 Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Wed, 31 Mar 2021 21:15:42 -0400 Subject: [PATCH 24/26] [Maps] Show empty list when all saved maps in list deleted (#95126) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../table_list_view/table_list_view.tsx | 19 ++----------------- .../translations/translations/ja-JP.json | 2 -- .../translations/translations/zh-CN.json | 2 -- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index fa0a32fc3d542..0054bb9c01b41 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -347,20 +347,6 @@ class TableListView extends React.Component - ); - } - } - renderToolsLeft() { const selection = this.state.selectedIds; @@ -473,10 +459,9 @@ class TableListView extends React.Component Date: Wed, 31 Mar 2021 18:18:22 -0700 Subject: [PATCH 25/26] skip suite failing es promotion (#96000) --- .../reporting_and_security/csv_searchsource_immediate.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts b/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts index 27c6a05f740bf..ebc7badd88f42 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts @@ -31,7 +31,8 @@ export default function ({ getService }: FtrProviderContext) { }, }; - describe('CSV Generation from SearchSource', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/96000 + describe.skip('CSV Generation from SearchSource', () => { before(async () => { await kibanaServer.uiSettings.update({ 'csv:quoteValues': false, From 07a3f9eb8dbeaf41e152ca536e03d639dd7ab536 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 31 Mar 2021 18:36:52 -0700 Subject: [PATCH 26/26] [kbn/optimizer] import source-map-support in script to avoid breaking jest snapshots (#96011) Co-authored-by: spalger --- packages/kbn-optimizer/src/cli.ts | 2 -- scripts/build_kibana_platform_plugins.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index 6e3106dbc2af7..d5b9996dfb2cd 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import 'source-map-support/register'; - import Path from 'path'; import { REPO_ROOT } from '@kbn/utils'; diff --git a/scripts/build_kibana_platform_plugins.js b/scripts/build_kibana_platform_plugins.js index fa630e0bb1808..9038d08364400 100644 --- a/scripts/build_kibana_platform_plugins.js +++ b/scripts/build_kibana_platform_plugins.js @@ -7,6 +7,7 @@ */ require('../src/setup_node_env/ensure_node_preserve_symlinks'); +require('source-map-support/register'); require('@kbn/optimizer').runKbnOptimizerCli({ defaultLimitsPath: require.resolve('../packages/kbn-optimizer/limits.yml'), });