From be285b6a86879562047fe59579657bfe57983231 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Mon, 22 Feb 2021 16:52:22 -0700 Subject: [PATCH 01/70] [core.logging] Remove meta from default pattern layout. (#92104) --- src/core/server/logging/README.mdx | 4 ++-- src/core/server/logging/layouts/pattern_layout.test.ts | 2 +- src/core/server/logging/layouts/pattern_layout.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/server/logging/README.mdx b/src/core/server/logging/README.mdx index 6fd730f47dc07..8c093d0231585 100644 --- a/src/core/server/logging/README.mdx +++ b/src/core/server/logging/README.mdx @@ -82,8 +82,8 @@ There are two types of layout supported at the moment: `pattern` and `json`. ### Pattern layout With `pattern` layout it's possible to define a string pattern with special placeholders `%conversion_pattern` (see the table below) that -will be replaced with data from the actual log message. By default the following pattern is used: -`[%date][%level][%logger]%meta %message`. Also `highlight` option can be enabled for `pattern` layout so that +will be replaced with data from the actual log message. By default the following pattern is used: +`[%date][%level][%logger] %message`. Also `highlight` option can be enabled for `pattern` layout so that some parts of the log message are highlighted with different colors that may be quite handy if log messages are forwarded to the terminal with color support. `pattern` layout uses a sub-set of [log4j2 pattern syntax](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout) diff --git a/src/core/server/logging/layouts/pattern_layout.test.ts b/src/core/server/logging/layouts/pattern_layout.test.ts index abdc2f4fb929c..bb66722d4c2d0 100644 --- a/src/core/server/logging/layouts/pattern_layout.test.ts +++ b/src/core/server/logging/layouts/pattern_layout.test.ts @@ -108,7 +108,7 @@ test('`format()` correctly formats record with custom pattern.', () => { }); test('`format()` correctly formats record with meta data.', () => { - const layout = new PatternLayout(); + const layout = new PatternLayout('[%date][%level][%logger]%meta %message'); expect( layout.format({ diff --git a/src/core/server/logging/layouts/pattern_layout.ts b/src/core/server/logging/layouts/pattern_layout.ts index a5dc41786c440..c2fcc55b830b9 100644 --- a/src/core/server/logging/layouts/pattern_layout.ts +++ b/src/core/server/logging/layouts/pattern_layout.ts @@ -22,7 +22,7 @@ import { /** * Default pattern used by PatternLayout if it's not overridden in the configuration. */ -const DEFAULT_PATTERN = `[%date][%level][%logger]%meta %message`; +const DEFAULT_PATTERN = `[%date][%level][%logger] %message`; export const patternSchema = schema.string({ validate: (string) => { From 13624d5624bf647ddde4b4ebb1c6ca5a0b97119b Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 22 Feb 2021 17:05:28 -0700 Subject: [PATCH 02/70] [ci] call chrome and firefox scripts from security cypress job (#92318) Co-authored-by: spalger --- .ci/Jenkinsfile_security_cypress | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/Jenkinsfile_security_cypress b/.ci/Jenkinsfile_security_cypress index f7b02cd1c4ab1..9202af60c1571 100644 --- a/.ci/Jenkinsfile_security_cypress +++ b/.ci/Jenkinsfile_security_cypress @@ -17,7 +17,8 @@ kibanaPipeline(timeoutMinutes: 180) { workers.ci(name: job, size: 'l', ramDisk: true) { kibanaPipeline.bash('test/scripts/jenkins_xpack_build_kibana.sh', 'Build Default Distributable') - kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress.sh')() + kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress_chrome.sh')() + kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress_firefox.sh')() } } } From 3c29f38adfaeafc5a73e7a42a58fa39be9e29db0 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:21:25 -0500 Subject: [PATCH 03/70] [Security Solution][Case] Adding tests for updating the status of alerts (#91301) * Adding tests for updating the status of alerts * Adding more alert tests and unskipping --- .../case/server/connectors/case/index.ts | 8 +- .../plugins/case/server/connectors/index.ts | 11 +- .../basic/tests/cases/comments/migrations.ts | 4 +- .../basic/tests/cases/migrations.ts | 4 +- .../basic/tests/cases/patch_cases.ts | 637 ++-- .../tests/cases/sub_cases/patch_sub_cases.ts | 373 +- .../tests/cases/user_actions/migrations.ts | 4 +- .../basic/tests/configure/migrations.ts | 4 +- .../basic/tests/connectors/case.ts | 20 +- .../case_api_integration/common/lib/utils.ts | 52 + .../cases/{ => migrations}/data.json | 0 .../cases/{ => migrations}/mappings.json | 0 .../cases/signals/default/data.json.gz | Bin 0 -> 5586 bytes .../cases/signals/default/mappings.json | 3313 +++++++++++++++++ 14 files changed, 4150 insertions(+), 280 deletions(-) rename x-pack/test/functional/es_archives/cases/{ => migrations}/data.json (100%) rename x-pack/test/functional/es_archives/cases/{ => migrations}/mappings.json (100%) create mode 100644 x-pack/test/functional/es_archives/cases/signals/default/data.json.gz create mode 100644 x-pack/test/functional/es_archives/cases/signals/default/mappings.json diff --git a/x-pack/plugins/case/server/connectors/case/index.ts b/x-pack/plugins/case/server/connectors/case/index.ts index a64cba567ce46..fd2fc7389f9ca 100644 --- a/x-pack/plugins/case/server/connectors/case/index.ts +++ b/x-pack/plugins/case/server/connectors/case/index.ts @@ -24,7 +24,7 @@ import { } from './types'; import * as i18n from './translations'; -import { GetActionTypeParams, isCommentGeneratedAlert } from '..'; +import { GetActionTypeParams, isCommentGeneratedAlert, separator } from '..'; import { nullUser } from '../../common'; const supportedSubActions: string[] = ['create', 'update', 'addComment']; @@ -136,8 +136,8 @@ export const transformConnectorComment = (comment: CommentSchemaType): CommentRe ruleId: string | undefined; ruleName: string | undefined; }> = JSON.parse( - `${comment.alerts.substring(0, comment.alerts.lastIndexOf('__SEPARATOR__'))}]`.replace( - /__SEPARATOR__/gi, + `${comment.alerts.substring(0, comment.alerts.lastIndexOf(separator))}]`.replace( + new RegExp(separator, 'g'), ',' ) ); @@ -148,7 +148,7 @@ export const transformConnectorComment = (comment: CommentSchemaType): CommentRe // Mutation usually leads to side effects but for this scenario it's ok to do it. acc.ids.push(_id); acc.indices.push(_index); - // We assume one rule per batch of alerts + // We assume one rule per batch of alerts, this will use the rule information from the last entry in the array acc.rule = { id: ruleId ?? null, name: ruleName ?? null }; return acc; }, diff --git a/x-pack/plugins/case/server/connectors/index.ts b/x-pack/plugins/case/server/connectors/index.ts index 898d61301a140..a6b6e193361be 100644 --- a/x-pack/plugins/case/server/connectors/index.ts +++ b/x-pack/plugins/case/server/connectors/index.ts @@ -22,6 +22,12 @@ import { CommentRequest, CommentType } from '../../common/api'; export * from './types'; export { transformConnectorComment } from './case'; +/** + * Separator used for creating a json parsable array from the mustache syntax that the alerting framework + * sends. + */ +export const separator = '__SEPARATOR__'; + export const registerConnectors = ({ actionsRegisterType, logger, @@ -66,11 +72,6 @@ export const isCommentAlert = ( return comment.type === CommentType.alert; }; -/** - * Separator field for the case connector alerts string parser. - */ -const separator = '__SEPARATOR__'; - interface AlertIDIndex { _id: string; _index: string; diff --git a/x-pack/test/case_api_integration/basic/tests/cases/comments/migrations.ts b/x-pack/test/case_api_integration/basic/tests/cases/comments/migrations.ts index c6210c0cf6a41..9309881ba3cf8 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/comments/migrations.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/comments/migrations.ts @@ -16,11 +16,11 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe('migrations', () => { before(async () => { - await esArchiver.load('cases'); + await esArchiver.load('cases/migrations'); }); after(async () => { - await esArchiver.unload('cases'); + await esArchiver.unload('cases/migrations'); }); it('7.11.0 migrates cases comments', async () => { diff --git a/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts b/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts index 01718f5bbb418..1d1b56b23342a 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts @@ -16,11 +16,11 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe('migrations', () => { before(async () => { - await esArchiver.load('cases'); + await esArchiver.load('cases/migrations'); }); after(async () => { - await esArchiver.unload('cases'); + await esArchiver.unload('cases/migrations'); }); it('7.10.0 migrates cases connector', async () => { diff --git a/x-pack/test/case_api_integration/basic/tests/cases/patch_cases.ts b/x-pack/test/case_api_integration/basic/tests/cases/patch_cases.ts index 43d6be196da0d..b51b728df7155 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/patch_cases.ts @@ -10,7 +10,12 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { CASES_URL } from '../../../../../plugins/case/common/constants'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../plugins/security_solution/common/constants'; -import { CaseType, CommentType } from '../../../../../plugins/case/common/api'; +import { + CasesResponse, + CaseStatuses, + CaseType, + CommentType, +} from '../../../../../plugins/case/common/api'; import { defaultUser, postCaseReq, @@ -20,7 +25,7 @@ import { postCommentUserReq, removeServerGeneratedPropertiesFromCase, } from '../../../common/lib/mock'; -import { deleteAllCaseItems } from '../../../common/lib/utils'; +import { deleteAllCaseItems, getSignalsWithES, setStatus } from '../../../common/lib/utils'; import { createSignalsIndex, deleteSignalsIndex, @@ -367,265 +372,399 @@ export default ({ getService }: FtrProviderContext): void => { }); describe('alerts', () => { - beforeEach(async () => { - await esArchiver.load('auditbeat/hosts'); - await createSignalsIndex(supertest); - }); - - afterEach(async () => { - await deleteSignalsIndex(supertest); - await deleteAllAlerts(supertest); - await esArchiver.unload('auditbeat/hosts'); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/87988 - it.skip('updates alert status when the status is updated and syncAlerts=true', async () => { - const rule = getRuleForSignalTesting(['auditbeat-*']); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); - const signals = await getSignalsByIds(supertest, [id]); - - const alert = signals.hits.hits[0]; - expect(alert._source.signal.status).eql('open'); - - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - rule: { - id: 'id', - name: 'name', - }, - type: CommentType.alert, - }) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseUpdated.id, - version: caseUpdated.version, - status: 'in-progress', + describe('esArchiver', () => { + const defaultSignalsIndex = '.siem-signals-default-000001'; + + beforeEach(async () => { + await esArchiver.load('cases/signals/default'); + }); + afterEach(async () => { + await esArchiver.unload('cases/signals/default'); + await deleteAllCaseItems(es); + }); + + it('should update the status of multiple alerts attached to multiple cases', async () => { + const signalID = '5f2b9ec41f8febb1c06b5d1045aeabb9874733b7617e88a370510f2fb3a41a5d'; + const signalID2 = '4d0f4b1533e46b66b43bdd0330d23f39f2cf42a7253153270e38d30cce9ff0c6'; + + const { body: individualCase1 } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + ...postCaseReq, + settings: { + syncAlerts: false, }, - ], - }) - .expect(200); - - const { body: updatedAlert } = await supertest - .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) - .set('kbn-xsrf', 'true') - .send(getQuerySignalIds([alert._id])) - .expect(200); - - expect(updatedAlert.hits.hits[0]._source.signal.status).eql('in-progress'); - }); - - it('does NOT updates alert status when the status is updated and syncAlerts=false', async () => { - const rule = getRuleForSignalTesting(['auditbeat-*']); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, settings: { syncAlerts: false } }) - .expect(200); - - const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); - const signals = await getSignalsByIds(supertest, [id]); - - const alert = signals.hits.hits[0]; - expect(alert._source.signal.status).eql('open'); - - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - type: CommentType.alert, - rule: { - id: 'id', - name: 'name', - }, - }) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseUpdated.id, - version: caseUpdated.version, - status: 'in-progress', + }); + + const { body: updatedInd1WithComment } = await supertest + .post(`${CASES_URL}/${individualCase1.id}/comments`) + .set('kbn-xsrf', 'true') + .send({ + alertId: signalID, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + }) + .expect(200); + + const { body: individualCase2 } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + ...postCaseReq, + settings: { + syncAlerts: false, }, - ], - }) - .expect(200); - - const { body: updatedAlert } = await supertest - .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) - .set('kbn-xsrf', 'true') - .send(getQuerySignalIds([alert._id])) - .expect(200); - - expect(updatedAlert.hits.hits[0]._source.signal.status).eql('open'); - }); - - // Failing: See https://github.com/elastic/kibana/issues/88130 - it.skip('it updates alert status when syncAlerts is turned on', async () => { - const rule = getRuleForSignalTesting(['auditbeat-*']); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, settings: { syncAlerts: false } }) - .expect(200); - - const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); - const signals = await getSignalsByIds(supertest, [id]); - - const alert = signals.hits.hits[0]; - expect(alert._source.signal.status).eql('open'); - - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - rule: { - id: 'id', - name: 'name', - }, - type: CommentType.alert, - }) - .expect(200); - - // Update the status of the case with sync alerts off - const { body: caseStatusUpdated } = await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ + }); + + const { body: updatedInd2WithComment } = await supertest + .post(`${CASES_URL}/${individualCase2.id}/comments`) + .set('kbn-xsrf', 'true') + .send({ + alertId: signalID2, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + }) + .expect(200); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + let signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // There should be no change in their status since syncing is disabled + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.open); + + const updatedIndWithStatus: CasesResponse = (await setStatus({ + supertest, cases: [ { - id: caseUpdated.id, - version: caseUpdated.version, - status: 'in-progress', + id: updatedInd1WithComment.id, + version: updatedInd1WithComment.version, + status: CaseStatuses.closed, }, - ], - }) - .expect(200); - - // Turn sync alerts on - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ { - id: caseStatusUpdated[0].id, - version: caseStatusUpdated[0].version, - settings: { syncAlerts: true }, + id: updatedInd2WithComment.id, + version: updatedInd2WithComment.version, + status: CaseStatuses['in-progress'], }, ], - }) - .expect(200); - - const { body: updatedAlert } = await supertest - .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) - .set('kbn-xsrf', 'true') - .send(getQuerySignalIds([alert._id])) - .expect(200); - - expect(updatedAlert.hits.hits[0]._source.signal.status).eql('in-progress'); + type: 'case', + })) as CasesResponse; + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // There should still be no change in their status since syncing is disabled + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.open); + + // turn on the sync settings + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: updatedIndWithStatus.map((caseInfo) => ({ + id: caseInfo.id, + version: caseInfo.version, + settings: { syncAlerts: true }, + })), + }) + .expect(200); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // alerts should be updated now that the + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.closed); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses['in-progress']); + }); }); - it('it does NOT updates alert status when syncAlerts is turned off', async () => { - const rule = getRuleForSignalTesting(['auditbeat-*']); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); - const signals = await getSignalsByIds(supertest, [id]); - - const alert = signals.hits.hits[0]; - expect(alert._source.signal.status).eql('open'); - - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - type: CommentType.alert, - rule: { - id: 'id', - name: 'name', - }, - }) - .expect(200); - - // Turn sync alerts off - const { body: caseSettingsUpdated } = await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseUpdated.id, - version: caseUpdated.version, - settings: { syncAlerts: false }, + describe('detections rule', () => { + beforeEach(async () => { + await esArchiver.load('auditbeat/hosts'); + await createSignalsIndex(supertest); + }); + + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(supertest); + await esArchiver.unload('auditbeat/hosts'); + }); + + it('updates alert status when the status is updated and syncAlerts=true', async () => { + const rule = getRuleForSignalTesting(['auditbeat-*']); + + const { body: postedCase } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send(postCaseReq) + .expect(200); + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signals = await getSignalsByIds(supertest, [id]); + + const alert = signals.hits.hits[0]; + expect(alert._source.signal.status).eql('open'); + + const { body: caseUpdated } = await supertest + .post(`${CASES_URL}/${postedCase.id}/comments`) + .set('kbn-xsrf', 'true') + .send({ + alertId: alert._id, + index: alert._index, + rule: { + id: 'id', + name: 'name', }, - ], - }) - .expect(200); - - // Update the status of the case with sync alerts off - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseSettingsUpdated[0].id, - version: caseSettingsUpdated[0].version, - status: 'in-progress', + type: CommentType.alert, + }) + .expect(200); + + await es.indices.refresh({ index: alert._index }); + + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + status: 'in-progress', + }, + ], + }) + .expect(200); + + // force a refresh on the index that the signal is stored in so that we can search for it and get the correct + // status + await es.indices.refresh({ index: alert._index }); + + const { body: updatedAlert } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds([alert._id])) + .expect(200); + + expect(updatedAlert.hits.hits[0]._source.signal.status).eql('in-progress'); + }); + + it('does NOT updates alert status when the status is updated and syncAlerts=false', async () => { + const rule = getRuleForSignalTesting(['auditbeat-*']); + + const { body: postedCase } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ ...postCaseReq, settings: { syncAlerts: false } }) + .expect(200); + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signals = await getSignalsByIds(supertest, [id]); + + const alert = signals.hits.hits[0]; + expect(alert._source.signal.status).eql('open'); + + const { body: caseUpdated } = await supertest + .post(`${CASES_URL}/${postedCase.id}/comments`) + .set('kbn-xsrf', 'true') + .send({ + alertId: alert._id, + index: alert._index, + type: CommentType.alert, + rule: { + id: 'id', + name: 'name', }, - ], - }) - .expect(200); - - const { body: updatedAlert } = await supertest - .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) - .set('kbn-xsrf', 'true') - .send(getQuerySignalIds([alert._id])) - .expect(200); - - expect(updatedAlert.hits.hits[0]._source.signal.status).eql('open'); + }) + .expect(200); + + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + status: 'in-progress', + }, + ], + }) + .expect(200); + + const { body: updatedAlert } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds([alert._id])) + .expect(200); + + expect(updatedAlert.hits.hits[0]._source.signal.status).eql('open'); + }); + + it('it updates alert status when syncAlerts is turned on', async () => { + const rule = getRuleForSignalTesting(['auditbeat-*']); + + const { body: postedCase } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ ...postCaseReq, settings: { syncAlerts: false } }) + .expect(200); + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signals = await getSignalsByIds(supertest, [id]); + + const alert = signals.hits.hits[0]; + expect(alert._source.signal.status).eql('open'); + + const { body: caseUpdated } = await supertest + .post(`${CASES_URL}/${postedCase.id}/comments`) + .set('kbn-xsrf', 'true') + .send({ + alertId: alert._id, + index: alert._index, + rule: { + id: 'id', + name: 'name', + }, + type: CommentType.alert, + }) + .expect(200); + + // Update the status of the case with sync alerts off + const { body: caseStatusUpdated } = await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + status: 'in-progress', + }, + ], + }) + .expect(200); + + // Turn sync alerts on + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: caseStatusUpdated[0].id, + version: caseStatusUpdated[0].version, + settings: { syncAlerts: true }, + }, + ], + }) + .expect(200); + + // refresh the index because syncAlerts was set to true so the alert's status should have been updated + await es.indices.refresh({ index: alert._index }); + + const { body: updatedAlert } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds([alert._id])) + .expect(200); + + expect(updatedAlert.hits.hits[0]._source.signal.status).eql('in-progress'); + }); + + it('it does NOT updates alert status when syncAlerts is turned off', async () => { + const rule = getRuleForSignalTesting(['auditbeat-*']); + + const { body: postedCase } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send(postCaseReq) + .expect(200); + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signals = await getSignalsByIds(supertest, [id]); + + const alert = signals.hits.hits[0]; + expect(alert._source.signal.status).eql('open'); + + const { body: caseUpdated } = await supertest + .post(`${CASES_URL}/${postedCase.id}/comments`) + .set('kbn-xsrf', 'true') + .send({ + alertId: alert._id, + index: alert._index, + type: CommentType.alert, + rule: { + id: 'id', + name: 'name', + }, + }) + .expect(200); + + // Turn sync alerts off + const { body: caseSettingsUpdated } = await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + settings: { syncAlerts: false }, + }, + ], + }) + .expect(200); + + // Update the status of the case with sync alerts off + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: caseSettingsUpdated[0].id, + version: caseSettingsUpdated[0].version, + status: 'in-progress', + }, + ], + }) + .expect(200); + + const { body: updatedAlert } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds([alert._id])) + .expect(200); + + expect(updatedAlert.hits.hits[0]._source.signal.status).eql('open'); + }); }); }); }); diff --git a/x-pack/test/case_api_integration/basic/tests/cases/sub_cases/patch_sub_cases.ts b/x-pack/test/case_api_integration/basic/tests/cases/sub_cases/patch_sub_cases.ts index 66422724b5677..49b3c0b1f465b 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/sub_cases/patch_sub_cases.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/sub_cases/patch_sub_cases.ts @@ -7,21 +7,34 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { SUB_CASES_PATCH_DEL_URL } from '../../../../../../plugins/case/common/constants'; +import { + CASES_URL, + SUB_CASES_PATCH_DEL_URL, +} from '../../../../../../plugins/case/common/constants'; import { createCaseAction, createSubCase, deleteAllCaseItems, deleteCaseAction, + getSignalsWithES, setStatus, } from '../../../../common/lib/utils'; import { getSubCaseDetailsUrl } from '../../../../../../plugins/case/common/api/helpers'; -import { CaseStatuses, SubCaseResponse } from '../../../../../../plugins/case/common/api'; +import { + CaseStatuses, + CommentType, + SubCaseResponse, +} from '../../../../../../plugins/case/common/api'; +import { createAlertsString } from '../../../../../../plugins/case/server/connectors'; +import { postCaseReq, postCollectionReq } from '../../../../common/lib/mock'; + +const defaultSignalsIndex = '.siem-signals-default-000001'; // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const es = getService('es'); + const esArchiver = getService('esArchiver'); describe('patch_sub_cases', () => { let actionID: string; @@ -31,7 +44,11 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await deleteCaseAction(supertest, actionID); }); + beforeEach(async () => { + await esArchiver.load('cases/signals/default'); + }); afterEach(async () => { + await esArchiver.unload('cases/signals/default'); await deleteAllCaseItems(es); }); @@ -56,6 +73,358 @@ export default function ({ getService }: FtrProviderContext) { expect(subCase.status).to.eql(CaseStatuses['in-progress']); }); + it('should update the status of one alert attached to a sub case', async () => { + const signalID = '5f2b9ec41f8febb1c06b5d1045aeabb9874733b7617e88a370510f2fb3a41a5d'; + + const { newSubCaseInfo: caseInfo } = await createSubCase({ + supertest, + actionID, + comment: { + alerts: createAlertsString([ + { + _id: signalID, + _index: defaultSignalsIndex, + ruleId: 'id', + ruleName: 'name', + }, + ]), + type: CommentType.generatedAlert, + }, + }); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + let signals = await getSignalsWithES({ es, indices: defaultSignalsIndex, ids: signalID }); + + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + + await setStatus({ + supertest, + cases: [ + { + id: caseInfo.subCase!.id, + version: caseInfo.subCase!.version, + status: CaseStatuses['in-progress'], + }, + ], + type: 'sub_case', + }); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ es, indices: defaultSignalsIndex, ids: signalID }); + + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses['in-progress']); + }); + + it('should update the status of multiple alerts attached to a sub case', async () => { + const signalID = '5f2b9ec41f8febb1c06b5d1045aeabb9874733b7617e88a370510f2fb3a41a5d'; + + const signalID2 = '4d0f4b1533e46b66b43bdd0330d23f39f2cf42a7253153270e38d30cce9ff0c6'; + + const { newSubCaseInfo: caseInfo } = await createSubCase({ + supertest, + actionID, + comment: { + alerts: createAlertsString([ + { + _id: signalID, + _index: defaultSignalsIndex, + ruleId: 'id', + ruleName: 'name', + }, + { + _id: signalID2, + _index: defaultSignalsIndex, + ruleId: 'id', + ruleName: 'name', + }, + ]), + type: CommentType.generatedAlert, + }, + }); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + let signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.open); + + await setStatus({ + supertest, + cases: [ + { + id: caseInfo.subCase!.id, + version: caseInfo.subCase!.version, + status: CaseStatuses['in-progress'], + }, + ], + type: 'sub_case', + }); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses['in-progress']); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses['in-progress']); + }); + + it('should update the status of multiple alerts attached to multiple sub cases in one collection', async () => { + const signalID = '5f2b9ec41f8febb1c06b5d1045aeabb9874733b7617e88a370510f2fb3a41a5d'; + const signalID2 = '4d0f4b1533e46b66b43bdd0330d23f39f2cf42a7253153270e38d30cce9ff0c6'; + + const { newSubCaseInfo: initialCaseInfo } = await createSubCase({ + supertest, + actionID, + caseInfo: { + ...postCollectionReq, + settings: { + syncAlerts: false, + }, + }, + comment: { + alerts: createAlertsString([ + { + _id: signalID, + _index: defaultSignalsIndex, + ruleId: 'id', + ruleName: 'name', + }, + ]), + type: CommentType.generatedAlert, + }, + }); + + // This will close the first sub case and create a new one + const { newSubCaseInfo: collectionWithSecondSub } = await createSubCase({ + supertest, + actionID, + caseID: initialCaseInfo.id, + comment: { + alerts: createAlertsString([ + { + _id: signalID2, + _index: defaultSignalsIndex, + ruleId: 'id', + ruleName: 'name', + }, + ]), + type: CommentType.generatedAlert, + }, + }); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + let signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // There should be no change in their status since syncing is disabled + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.open); + + await setStatus({ + supertest, + cases: [ + { + id: collectionWithSecondSub.subCase!.id, + version: collectionWithSecondSub.subCase!.version, + status: CaseStatuses['in-progress'], + }, + ], + type: 'sub_case', + }); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // There still should be no change in their status since syncing is disabled + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.open); + + // Turn sync alerts on + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: collectionWithSecondSub.id, + version: collectionWithSecondSub.version, + settings: { syncAlerts: true }, + }, + ], + }) + .expect(200); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.closed); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses['in-progress']); + }); + + it('should update the status of alerts attached to a case and sub case when sync settings is turned on', async () => { + const signalID = '5f2b9ec41f8febb1c06b5d1045aeabb9874733b7617e88a370510f2fb3a41a5d'; + const signalID2 = '4d0f4b1533e46b66b43bdd0330d23f39f2cf42a7253153270e38d30cce9ff0c6'; + + const { body: individualCase } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + ...postCaseReq, + settings: { + syncAlerts: false, + }, + }); + + const { newSubCaseInfo: caseInfo } = await createSubCase({ + supertest, + actionID, + caseInfo: { + ...postCollectionReq, + settings: { + syncAlerts: false, + }, + }, + comment: { + alerts: createAlertsString([ + { + _id: signalID, + _index: defaultSignalsIndex, + ruleId: 'id', + ruleName: 'name', + }, + ]), + type: CommentType.generatedAlert, + }, + }); + + const { body: updatedIndWithComment } = await supertest + .post(`${CASES_URL}/${individualCase.id}/comments`) + .set('kbn-xsrf', 'true') + .send({ + alertId: signalID2, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + }) + .expect(200); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + let signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // There should be no change in their status since syncing is disabled + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.open); + + await setStatus({ + supertest, + cases: [ + { + id: caseInfo.subCase!.id, + version: caseInfo.subCase!.version, + status: CaseStatuses['in-progress'], + }, + ], + type: 'sub_case', + }); + + const updatedIndWithStatus = ( + await setStatus({ + supertest, + cases: [ + { + id: updatedIndWithComment.id, + version: updatedIndWithComment.version, + status: CaseStatuses.closed, + }, + ], + type: 'case', + }) + )[0]; // there should only be a single entry in the response + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // There should still be no change in their status since syncing is disabled + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses.open); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.open); + + // Turn sync alerts on + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: caseInfo.id, + version: caseInfo.version, + settings: { syncAlerts: true }, + }, + ], + }) + .expect(200); + + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: updatedIndWithStatus.id, + version: updatedIndWithStatus.version, + settings: { syncAlerts: true }, + }, + ], + }) + .expect(200); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + // alerts should be updated now that the + expect(signals.get(signalID)?._source.signal.status).to.be(CaseStatuses['in-progress']); + expect(signals.get(signalID2)?._source.signal.status).to.be(CaseStatuses.closed); + }); + it('404s when sub case id is invalid', async () => { await supertest .patch(`${SUB_CASES_PATCH_DEL_URL}`) diff --git a/x-pack/test/case_api_integration/basic/tests/cases/user_actions/migrations.ts b/x-pack/test/case_api_integration/basic/tests/cases/user_actions/migrations.ts index 713f6194f379a..14219e46b2bf6 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/user_actions/migrations.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/user_actions/migrations.ts @@ -16,11 +16,11 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe('migrations', () => { before(async () => { - await esArchiver.load('cases'); + await esArchiver.load('cases/migrations'); }); after(async () => { - await esArchiver.unload('cases'); + await esArchiver.unload('cases/migrations'); }); it('7.10.0 migrates user actions connector', async () => { diff --git a/x-pack/test/case_api_integration/basic/tests/configure/migrations.ts b/x-pack/test/case_api_integration/basic/tests/configure/migrations.ts index 68d0129efa1cb..8ee29d4364543 100644 --- a/x-pack/test/case_api_integration/basic/tests/configure/migrations.ts +++ b/x-pack/test/case_api_integration/basic/tests/configure/migrations.ts @@ -16,11 +16,11 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe('migrations', () => { before(async () => { - await esArchiver.load('cases'); + await esArchiver.load('cases/migrations'); }); after(async () => { - await esArchiver.unload('cases'); + await esArchiver.unload('cases/migrations'); }); it('7.10.0 migrates configure cases connector', async () => { diff --git a/x-pack/test/case_api_integration/basic/tests/connectors/case.ts b/x-pack/test/case_api_integration/basic/tests/connectors/case.ts index 4812ead2c4c78..62a5df7643337 100644 --- a/x-pack/test/case_api_integration/basic/tests/connectors/case.ts +++ b/x-pack/test/case_api_integration/basic/tests/connectors/case.ts @@ -775,9 +775,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - // TODO: Enable when the creation of comments of type alert is supported - // https://github.com/elastic/kibana/issues/85750 - it.skip('should respond with a 400 Bad Request when missing attributes of type alert', async () => { + it('should respond with a 400 Bad Request when missing attributes of type alert', async () => { const { body: createdAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'foo') @@ -803,7 +801,8 @@ export default ({ getService }: FtrProviderContext): void => { }, }; - for (const attribute of ['alertId', 'index']) { + // only omitting alertId here since the type for alertId and index differ, the messages will be different + for (const attribute of ['alertId']) { const requestAttributes = omit(attribute, comment); const caseConnector = await supertest .post(`/api/actions/action/${createdActionId}/_execute`) @@ -819,7 +818,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(caseConnector.body).to.eql({ status: 'error', actionId: createdActionId, - message: `error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [create]\n- [1.subAction]: expected value to equal [update]\n- [2.subActionParams.comment]: types that failed validation:\n - [subActionParams.comment.0.type]: expected value to equal [user]\n - [subActionParams.comment.1.${attribute}]: expected value of type [string] but got [undefined]`, + message: `error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [create]\n- [1.subAction]: expected value to equal [update]\n- [2.subActionParams.comment]: types that failed validation:\n - [subActionParams.comment.0.type]: expected value to equal [user]\n - [subActionParams.comment.1.${attribute}]: expected at least one defined value but got [undefined]\n - [subActionParams.comment.2.type]: expected value to equal [generated_alert]`, retry: false, }); } @@ -868,9 +867,7 @@ export default ({ getService }: FtrProviderContext): void => { } }); - // TODO: Enable when the creation of comments of type alert is supported - // https://github.com/elastic/kibana/issues/85750 - it.skip('should respond with a 400 Bad Request when adding excess attributes for type alert', async () => { + it('should respond with a 400 Bad Request when adding excess attributes for type alert', async () => { const { body: createdAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'foo') @@ -913,7 +910,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(caseConnector.body).to.eql({ status: 'error', actionId: createdActionId, - message: `error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [create]\n- [1.subAction]: expected value to equal [update]\n- [2.subActionParams.comment]: types that failed validation:\n - [subActionParams.comment.0.type]: expected value to equal [user]\n - [subActionParams.comment.1.${attribute}]: definition for this key is missing`, + message: `error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [create]\n- [1.subAction]: expected value to equal [update]\n- [2.subActionParams.comment]: types that failed validation:\n - [subActionParams.comment.0.type]: expected value to equal [user]\n - [subActionParams.comment.1.${attribute}]: definition for this key is missing\n - [subActionParams.comment.2.type]: expected value to equal [generated_alert]`, retry: false, }); } @@ -1007,9 +1004,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - // TODO: Enable when the creation of comments of type alert is supported - // https://github.com/elastic/kibana/issues/85750 - it.skip('should add a comment of type alert', async () => { + it('should add a comment of type alert', async () => { const { body: createdAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'foo') @@ -1059,6 +1054,7 @@ export default ({ getService }: FtrProviderContext): void => { ...postCaseResp(caseRes.body.id), comments, totalComment: 1, + totalAlerts: 1, updated_by: { email: null, full_name: null, diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 048c5c5d84098..7aee6170c3d5a 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { Client } from '@elastic/elasticsearch'; import * as st from 'supertest'; import supertestAsPromised from 'supertest-as-promised'; +import { Explanation, SearchResponse } from 'elasticsearch'; import { CASES_URL, SUB_CASES_PATCH_DEL_URL } from '../../../../plugins/case/common/constants'; import { CasesConfigureRequest, @@ -25,6 +26,57 @@ import { import { postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/case/common/api/helpers'; import { ContextTypeGeneratedAlertType } from '../../../../plugins/case/server/connectors'; +import { SignalHit } from '../../../../plugins/security_solution/server/lib/detection_engine/signals/types'; + +interface Hit { + _index: string; + _type: string; + _id: string; + _score: number; + _source: T; + _version?: number; + _explanation?: Explanation; + fields?: any; + highlight?: any; + inner_hits?: any; + matched_queries?: string[]; + sort?: string[]; +} + +/** + * Query Elasticsearch for a set of signals within a set of indices + */ +export const getSignalsWithES = async ({ + es, + indices, + ids, +}: { + es: Client; + indices: string | string[]; + ids: string | string[]; +}): Promise>> => { + const signals = await es.search>({ + index: indices, + body: { + size: ids.length, + query: { + bool: { + filter: [ + { + ids: { + values: ids, + }, + }, + ], + }, + }, + }, + }); + return signals.body.hits.hits.reduce((acc, hit) => { + acc.set(hit._id, hit); + return acc; + }, new Map>()); +}; interface SetStatusCasesParams { id: string; diff --git a/x-pack/test/functional/es_archives/cases/data.json b/x-pack/test/functional/es_archives/cases/migrations/data.json similarity index 100% rename from x-pack/test/functional/es_archives/cases/data.json rename to x-pack/test/functional/es_archives/cases/migrations/data.json diff --git a/x-pack/test/functional/es_archives/cases/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/mappings.json similarity index 100% rename from x-pack/test/functional/es_archives/cases/mappings.json rename to x-pack/test/functional/es_archives/cases/migrations/mappings.json diff --git a/x-pack/test/functional/es_archives/cases/signals/default/data.json.gz b/x-pack/test/functional/es_archives/cases/signals/default/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..6caae322450e44b439e17bc8b6cf6445a4a9940c GIT binary patch literal 5586 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 Date: Mon, 22 Feb 2021 20:44:39 -0500 Subject: [PATCH 04/70] [Console] Update docs (#92240) --- docs/dev-tools/console/console.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/dev-tools/console/console.asciidoc b/docs/dev-tools/console/console.asciidoc index 79f2b6c0902e0..7cef937633062 100644 --- a/docs/dev-tools/console/console.asciidoc +++ b/docs/dev-tools/console/console.asciidoc @@ -46,6 +46,8 @@ curl -XGET "http://localhost:9200/_search" -d' When you paste the command into Console, {kib} automatically converts it to Console syntax. Alternatively, if you want to see Console syntax in cURL, click the action icon (image:dev-tools/console/images/wrench.png[]) and select *Copy as cURL*. +Once copied, the username and password will need to be provided +for the calls to work from external environments. [float] [[console-autocomplete]] From 7f9e9e828d9f4d7cc9781bed822bceaa27d35a21 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 22 Feb 2021 18:48:37 -0700 Subject: [PATCH 05/70] [Maps] convert map redux reducer to typescript (#91955) * [Maps] convert map redux reducer to typescript * fix jest test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/actions/data_request_actions.ts | 1 - .../maps/public/classes/layers/layer.tsx | 2 +- .../classes/sources/es_source/es_source.ts | 2 +- .../maps/public/classes/sources/source.ts | 2 +- .../navigation_panel.test.tsx | 2 +- ....test.ts => copy_persistent_state.test.ts} | 2 +- .../{util.ts => copy_persistent_state.ts} | 0 .../plugins/maps/public/reducers/map.test.js | 57 --- .../reducers/map/data_request_utils.test.ts | 361 ++++++++++++++++++ .../public/reducers/map/data_request_utils.ts | 115 ++++++ .../{ => map}/default_map_settings.ts | 4 +- .../plugins/maps/public/reducers/map/index.ts | 12 + .../maps/public/reducers/map/layer_utils.ts | 128 +++++++ .../public/reducers/{map.js => map/map.ts} | 260 ++----------- .../reducers/{map.d.ts => map/types.ts} | 6 +- .../routes/map_page/saved_map/saved_map.ts | 2 +- .../maps/public/selectors/map_selectors.ts | 2 +- 17 files changed, 668 insertions(+), 290 deletions(-) rename x-pack/plugins/maps/public/reducers/{util.test.ts => copy_persistent_state.test.ts} (94%) rename x-pack/plugins/maps/public/reducers/{util.ts => copy_persistent_state.ts} (100%) delete mode 100644 x-pack/plugins/maps/public/reducers/map.test.js create mode 100644 x-pack/plugins/maps/public/reducers/map/data_request_utils.test.ts create mode 100644 x-pack/plugins/maps/public/reducers/map/data_request_utils.ts rename x-pack/plugins/maps/public/reducers/{ => map}/default_map_settings.ts (89%) create mode 100644 x-pack/plugins/maps/public/reducers/map/index.ts create mode 100644 x-pack/plugins/maps/public/reducers/map/layer_utils.ts rename x-pack/plugins/maps/public/reducers/{map.js => map/map.ts} (55%) rename x-pack/plugins/maps/public/reducers/{map.d.ts => map/types.ts} (90%) diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts index 5e8a18348ac5a..f5a09fdc8fffc 100644 --- a/x-pack/plugins/maps/public/actions/data_request_actions.ts +++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts @@ -299,7 +299,6 @@ function onDataLoadError( dispatch(cleanTooltipStateForLayer(layerId)); dispatch({ type: LAYER_DATA_LOAD_ERROR, - data: null, layerId, dataId, requestToken, diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index e3a21b596afe1..e3d5150c9cd09 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -26,7 +26,7 @@ import { SOURCE_TYPES, STYLE_TYPE, } from '../../../common/constants'; -import { copyPersistentState } from '../../reducers/util'; +import { copyPersistentState } from '../../reducers/copy_persistent_state'; import { AggDescriptor, ESTermSourceDescriptor, diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts index 0d73d8a29eb25..f6f0a234bcd67 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts @@ -16,7 +16,7 @@ import { getSearchService, } from '../../../kibana_services'; import { createExtentFilter } from '../../../../common/elasticsearch_util'; -import { copyPersistentState } from '../../../reducers/util'; +import { copyPersistentState } from '../../../reducers/copy_persistent_state'; import { DataRequestAbortError } from '../../util/data_request'; import { expandToTileBoundaries } from '../../../../common/geo_tile_utils'; import { search } from '../../../../../../../src/plugins/data/public'; diff --git a/x-pack/plugins/maps/public/classes/sources/source.ts b/x-pack/plugins/maps/public/classes/sources/source.ts index e4db965b2a75f..d9eda86428701 100644 --- a/x-pack/plugins/maps/public/classes/sources/source.ts +++ b/x-pack/plugins/maps/public/classes/sources/source.ts @@ -11,7 +11,7 @@ import { ReactElement } from 'react'; import { Adapters } from 'src/plugins/inspector/public'; import { GeoJsonProperties } from 'geojson'; -import { copyPersistentState } from '../../reducers/util'; +import { copyPersistentState } from '../../reducers/copy_persistent_state'; import { IField } from '../fields/field'; import { FieldFormatter, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx b/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx index 0e354fcd6b6b0..a01dfa3f43c9d 100644 --- a/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { NavigationPanel } from './navigation_panel'; -import { getDefaultMapSettings } from '../../reducers/default_map_settings'; +import { getDefaultMapSettings } from '../../reducers/map/default_map_settings'; import { INITIAL_LOCATION } from '../../../common/constants'; const defaultProps = { diff --git a/x-pack/plugins/maps/public/reducers/util.test.ts b/x-pack/plugins/maps/public/reducers/copy_persistent_state.test.ts similarity index 94% rename from x-pack/plugins/maps/public/reducers/util.test.ts rename to x-pack/plugins/maps/public/reducers/copy_persistent_state.test.ts index 7c7707939f0b6..260e84a41767f 100644 --- a/x-pack/plugins/maps/public/reducers/util.test.ts +++ b/x-pack/plugins/maps/public/reducers/copy_persistent_state.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { copyPersistentState } from './util'; +import { copyPersistentState } from './copy_persistent_state'; describe('reducers/util', () => { describe('copyPersistentState', () => { diff --git a/x-pack/plugins/maps/public/reducers/util.ts b/x-pack/plugins/maps/public/reducers/copy_persistent_state.ts similarity index 100% rename from x-pack/plugins/maps/public/reducers/util.ts rename to x-pack/plugins/maps/public/reducers/copy_persistent_state.ts diff --git a/x-pack/plugins/maps/public/reducers/map.test.js b/x-pack/plugins/maps/public/reducers/map.test.js deleted file mode 100644 index 77474cc59b6d1..0000000000000 --- a/x-pack/plugins/maps/public/reducers/map.test.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -jest.mock('../actions', () => ({})); - -import { resetDataRequest } from './map'; -import _ from 'lodash'; - -describe('reducers/map', () => { - it('Should clear datarequest without mutation store state', async () => { - const layerId = 'foobar'; - const requestToken = 'tokenId'; - const dataId = 'dataId'; - - const preState = { - layerList: [ - { - id: `not_${layerId}`, - }, - { - id: layerId, - __dataRequests: [ - { - dataRequestToken: `not_${requestToken}`, - dataId: `not_${dataId}`, - }, - { - dataRequestToken: requestToken, - dataId: dataId, - }, - ], - }, - ], - }; - - const preStateCopy = _.cloneDeep(preState); - - const action = { - layerId, - requestToken, - dataId, - }; - - const postState = resetDataRequest(preState, action); - - //Ensure previous state is not mutated. - expect(_.isEqual(preState, preStateCopy)).toEqual(true); - - //Ensure new state is set correctly. - expect(postState.layerList[1].__dataRequests[1].dataId).toEqual(dataId); - expect(postState.layerList[1].__dataRequests[1].dataRequestToken).toEqual(null); - }); -}); diff --git a/x-pack/plugins/maps/public/reducers/map/data_request_utils.test.ts b/x-pack/plugins/maps/public/reducers/map/data_request_utils.test.ts new file mode 100644 index 0000000000000..d12af4bdddb53 --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/map/data_request_utils.test.ts @@ -0,0 +1,361 @@ +/* + * 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. + */ + +jest.mock('../../actions', () => ({})); + +import { DataMeta, DataRequestDescriptor } from '../../../common/descriptor_types'; +import { + getDataRequest, + setDataRequest, + startDataRequest, + stopDataRequest, + updateSourceDataRequest, +} from './data_request_utils'; +import { MapState } from './types'; +import _ from 'lodash'; + +describe('getDataRequest', () => { + const REQUEST_TOKEN = Symbol('request'); + const SOURCE_DATA_REQUEST_DESCRIPTOR = { + dataRequestToken: REQUEST_TOKEN, + dataId: 'source', + } as DataRequestDescriptor; + test('Should return undefined if layer not found', () => { + const state = ({ + layerList: [], + } as unknown) as MapState; + + expect(getDataRequest(state, 'layer1', 'source')).toBeUndefined(); + }); + + test('Should return undefined if __dataRequests not provided for layer', () => { + const state = ({ + layerList: [{ id: 'layer1' }], + } as unknown) as MapState; + + expect(getDataRequest(state, 'layer1', 'source')).toBeUndefined(); + }); + + test('Should return undefined if no data requests matching dataRequestId', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [SOURCE_DATA_REQUEST_DESCRIPTOR], + }, + ], + } as unknown) as MapState; + + expect(getDataRequest(state, 'layer1', 'join')).toBeUndefined(); + }); + + test('Should return data request with dataRequestId match', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [SOURCE_DATA_REQUEST_DESCRIPTOR], + }, + ], + } as unknown) as MapState; + + expect(getDataRequest(state, 'layer1', 'source')).toEqual(SOURCE_DATA_REQUEST_DESCRIPTOR); + }); + + test('Should return undefined with dataRequestId match and requestToken mismatch', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [SOURCE_DATA_REQUEST_DESCRIPTOR], + }, + ], + } as unknown) as MapState; + + expect(getDataRequest(state, 'layer1', 'source', Symbol('another_request'))).toBeUndefined(); + }); + + test('Should return data request with dataRequestId match and requestToken match', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [SOURCE_DATA_REQUEST_DESCRIPTOR], + }, + ], + } as unknown) as MapState; + + expect(getDataRequest(state, 'layer1', 'source', REQUEST_TOKEN)).toEqual( + SOURCE_DATA_REQUEST_DESCRIPTOR + ); + }); +}); + +describe('setDataRequest', () => { + const UPDATED_DATA_REQUEST_DESCRIPTOR = { + dataId: 'source', + data: { value: 'a' }, + } as DataRequestDescriptor; + + test('Should return unmodified state if layer not found', () => { + const state = ({ + layerList: [], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(setDataRequest(state, 'layer1', UPDATED_DATA_REQUEST_DESCRIPTOR)).toEqual(state); + expect(state).toEqual(stateClone); + }); + + test('Should add data request if data request not found', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [], + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(setDataRequest(state, 'layer1', UPDATED_DATA_REQUEST_DESCRIPTOR)).toEqual({ + layerList: [ + { + id: 'layer1', + __dataRequests: [UPDATED_DATA_REQUEST_DESCRIPTOR], + }, + ], + }); + expect(state).toEqual(stateClone); + }); + + test('Should update data request', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [{ dataId: 'source' }], + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(setDataRequest(state, 'layer1', UPDATED_DATA_REQUEST_DESCRIPTOR)).toEqual({ + layerList: [ + { + id: 'layer1', + __dataRequests: [UPDATED_DATA_REQUEST_DESCRIPTOR], + }, + ], + }); + expect(state).toEqual(stateClone); + }); +}); + +describe('startDataRequest', () => { + const REQUEST_TOKEN = Symbol('request'); + const DATA_META_AT_START = { + prop1: 'value', + } as DataMeta; + + test('Should return unmodified state if layer not found', () => { + const state = ({ + layerList: [], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(startDataRequest(state, 'layer1', 'source', REQUEST_TOKEN, DATA_META_AT_START)).toEqual( + state + ); + expect(state).toEqual(stateClone); + }); + + test('Should add data request if no data requests for dataRequestId', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(startDataRequest(state, 'layer1', 'source', REQUEST_TOKEN, DATA_META_AT_START)).toEqual({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + dataMetaAtStart: DATA_META_AT_START, + dataRequestToken: REQUEST_TOKEN, + }, + ], + }, + ], + }); + expect(state).toEqual(stateClone); + }); + + test('Should update existing data request for onStartLoading event', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + dataMetaAtStart: { prop1: 'old value' }, + dataRequestToken: Symbol('request'), + }, + ], + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(startDataRequest(state, 'layer1', 'source', REQUEST_TOKEN, DATA_META_AT_START)).toEqual({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + dataMetaAtStart: DATA_META_AT_START, + dataRequestToken: REQUEST_TOKEN, + }, + ], + }, + ], + }); + expect(state).toEqual(stateClone); + }); +}); + +describe('stopDataRequest', () => { + const REQUEST_TOKEN = Symbol('request'); + + test('Should return unmodified state if layer not found', () => { + const state = ({ + layerList: [], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(stopDataRequest(state, 'layer1', 'source', REQUEST_TOKEN)).toEqual(state); + expect(state).toEqual(stateClone); + }); + + test('Should return unmodified state if data request not found (unmatching request token)', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + dataRequestToken: Symbol('request'), + }, + ], + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(stopDataRequest(state, 'layer1', 'source', REQUEST_TOKEN)).toEqual(state); + expect(state).toEqual(stateClone); + }); + + test('Should update data request with response meta and data', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + dataRequestToken: REQUEST_TOKEN, + dataMetaAtStart: { requestProp1: 'request' }, + data: { prop1: 'old data ' }, + }, + ], + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + const reponseMeta = { responseProp1: 'response' } as DataMeta; + const data = { prop1: 'new data' }; + expect(stopDataRequest(state, 'layer1', 'source', REQUEST_TOKEN, reponseMeta, data)).toEqual({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + dataMeta: { requestProp1: 'request', responseProp1: 'response' }, + data: { prop1: 'new data' }, + dataMetaAtStart: undefined, + dataRequestToken: undefined, + }, + ], + }, + ], + }); + expect(state).toEqual(stateClone); + }); +}); + +describe('updateSourceDataRequest', () => { + const NEW_DATA = { + prop1: 'new value', + }; + + test('Should return unmodified state if layer not found', () => { + const state = ({ + layerList: [], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(updateSourceDataRequest(state, 'layer1', NEW_DATA)).toEqual(state); + expect(state).toEqual(stateClone); + }); + + test('Should return unmodified state if source data request not found', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [], + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(updateSourceDataRequest(state, 'layer1', NEW_DATA)).toEqual(state); + expect(state).toEqual(stateClone); + }); + + test('Should update source data request', () => { + const state = ({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + data: { prop1: 'old value' }, + }, + ], + }, + ], + } as unknown) as MapState; + const stateClone = _.cloneDeep(state); + expect(updateSourceDataRequest(state, 'layer1', NEW_DATA)).toEqual({ + layerList: [ + { + id: 'layer1', + __dataRequests: [ + { + dataId: 'source', + data: NEW_DATA, + }, + ], + }, + ], + }); + expect(state).toEqual(stateClone); + }); +}); diff --git a/x-pack/plugins/maps/public/reducers/map/data_request_utils.ts b/x-pack/plugins/maps/public/reducers/map/data_request_utils.ts new file mode 100644 index 0000000000000..637f06f1d2e81 --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/map/data_request_utils.ts @@ -0,0 +1,115 @@ +/* + * 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 { SOURCE_DATA_REQUEST_ID } from '../../../common/constants'; +import { findLayerById, setLayer } from './layer_utils'; +import { DataMeta, DataRequestDescriptor } from '../../../common/descriptor_types'; +import { MapState } from './types'; + +export function startDataRequest( + state: MapState, + layerId: string, + dataRequestId: string, + requestToken: symbol, + requestMeta: DataMeta +): MapState { + const layerDescriptor = findLayerById(state, layerId); + if (!layerDescriptor) { + return state; + } + + const prevDataRequest = getDataRequest(state, layerId, dataRequestId); + const dataRequest = prevDataRequest + ? { + ...prevDataRequest, + } + : { + dataId: dataRequestId, + }; + dataRequest.dataMetaAtStart = requestMeta; + dataRequest.dataRequestToken = requestToken; + return setDataRequest(state, layerId, dataRequest); +} + +export function updateSourceDataRequest( + state: MapState, + layerId: string, + newData?: object +): MapState { + const dataRequest = getDataRequest(state, layerId, SOURCE_DATA_REQUEST_ID); + return dataRequest ? setDataRequest(state, layerId, { ...dataRequest, data: newData }) : state; +} + +export function stopDataRequest( + state: MapState, + layerId: string, + dataRequestId: string, + requestToken: symbol, + responseMeta?: DataMeta, + data?: object +): MapState { + const dataRequest = getDataRequest(state, layerId, dataRequestId, requestToken); + return dataRequest + ? setDataRequest(state, layerId, { + ...dataRequest, + data, + dataMeta: { + ...(dataRequest.dataMetaAtStart ? dataRequest.dataMetaAtStart : {}), + ...(responseMeta ? responseMeta : {}), + }, + dataMetaAtStart: undefined, + dataRequestToken: undefined, + }) + : state; +} + +export function setDataRequest( + state: MapState, + layerId: string, + dataRequest: DataRequestDescriptor +): MapState { + const layerDescriptor = findLayerById(state, layerId); + if (!layerDescriptor) { + return state; + } + + const updatedLayerDescriptor = { + ...layerDescriptor, + __dataRequests: layerDescriptor.__dataRequests ? [...layerDescriptor.__dataRequests] : [], + }; + + const dataRequestIndex = updatedLayerDescriptor.__dataRequests.findIndex( + ({ dataId }) => dataRequest.dataId === dataId + ); + if (dataRequestIndex === -1) { + updatedLayerDescriptor.__dataRequests.push(dataRequest); + } else { + updatedLayerDescriptor.__dataRequests[dataRequestIndex] = dataRequest; + } + return { + ...state, + layerList: setLayer(state.layerList, updatedLayerDescriptor), + }; +} + +export function getDataRequest( + state: MapState, + layerId: string, + dataRequestId: string, + requestToken?: symbol +): DataRequestDescriptor | undefined { + const layerDescriptor = findLayerById(state, layerId); + if (!layerDescriptor || !layerDescriptor.__dataRequests) { + return; + } + + return layerDescriptor.__dataRequests.find((dataRequest) => { + return requestToken + ? dataRequest.dataRequestToken === requestToken && dataRequest.dataId === dataRequestId + : dataRequest.dataId === dataRequestId; + }); +} diff --git a/x-pack/plugins/maps/public/reducers/default_map_settings.ts b/x-pack/plugins/maps/public/reducers/map/default_map_settings.ts similarity index 89% rename from x-pack/plugins/maps/public/reducers/default_map_settings.ts rename to x-pack/plugins/maps/public/reducers/map/default_map_settings.ts index 07f3aa33836dc..8ecaa8dfc2bf5 100644 --- a/x-pack/plugins/maps/public/reducers/default_map_settings.ts +++ b/x-pack/plugins/maps/public/reducers/map/default_map_settings.ts @@ -6,8 +6,8 @@ */ import { euiThemeVars } from '@kbn/ui-shared-deps/theme'; -import { INITIAL_LOCATION, MAX_ZOOM, MIN_ZOOM } from '../../common/constants'; -import { MapSettings } from './map'; +import { INITIAL_LOCATION, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; +import { MapSettings } from './types'; export function getDefaultMapSettings(): MapSettings { return { diff --git a/x-pack/plugins/maps/public/reducers/map/index.ts b/x-pack/plugins/maps/public/reducers/map/index.ts new file mode 100644 index 0000000000000..e9a459e4abc8b --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/map/index.ts @@ -0,0 +1,12 @@ +/* + * 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. + */ + +export * from './types'; + +export { DEFAULT_MAP_STATE, map } from './map'; + +export { getDefaultMapSettings } from './default_map_settings'; diff --git a/x-pack/plugins/maps/public/reducers/map/layer_utils.ts b/x-pack/plugins/maps/public/reducers/map/layer_utils.ts new file mode 100644 index 0000000000000..dfc3bf670867d --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/map/layer_utils.ts @@ -0,0 +1,128 @@ +/* + * 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 { LayerDescriptor } from '../../../common/descriptor_types'; +import { MapState } from './types'; +import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../copy_persistent_state'; + +export function getLayerIndex(list: LayerDescriptor[], layerId: string): number { + return list.findIndex(({ id }) => layerId === id); +} + +export function findLayerById(state: MapState, layerId: string): LayerDescriptor | undefined { + return state.layerList.find(({ id }) => layerId === id); +} + +export function updateLayerInList( + state: MapState, + layerId: string, + attribute: keyof LayerDescriptor, + newValue?: unknown +): MapState { + if (!layerId) { + return state; + } + + const { layerList } = state; + const layerIdx = getLayerIndex(layerList, layerId); + if (layerIdx === -1) { + return state; + } + + const updatedLayer = { + ...layerList[layerIdx], + // Update layer w/ new value. If no value provided, toggle boolean value + // allow empty strings, 0-value + [attribute]: + newValue || newValue === '' || newValue === 0 + ? newValue + : !(layerList[layerIdx][attribute] as boolean), + }; + const updatedList = [ + ...layerList.slice(0, layerIdx), + updatedLayer, + ...layerList.slice(layerIdx + 1), + ]; + return { ...state, layerList: updatedList }; +} + +export function updateLayerSourceDescriptorProp( + state: MapState, + layerId: string, + propName: string, + value: unknown +): MapState { + const { layerList } = state; + const layerIdx = getLayerIndex(layerList, layerId); + const updatedLayer = { + ...layerList[layerIdx], + sourceDescriptor: { + ...layerList[layerIdx].sourceDescriptor, + [propName]: value, + }, + }; + const updatedList = [ + ...layerList.slice(0, layerIdx), + updatedLayer, + ...layerList.slice(layerIdx + 1), + ] as LayerDescriptor[]; + return { ...state, layerList: updatedList }; +} + +export function trackCurrentLayerState(state: MapState, layerId: string): MapState { + const layer = findLayerById(state, layerId); + const layerCopy = copyPersistentState(layer); + return updateLayerInList(state, layerId, TRACKED_LAYER_DESCRIPTOR, layerCopy); +} + +export function removeTrackedLayerState(state: MapState, layerId: string): MapState { + const layer = findLayerById(state, layerId); + if (!layer) { + return state; + } + + const copyLayer = { ...layer }; + delete copyLayer[TRACKED_LAYER_DESCRIPTOR]; + + return { + ...state, + layerList: setLayer(state.layerList, copyLayer), + }; +} + +export function rollbackTrackedLayerState(state: MapState, layerId: string): MapState { + const layer = findLayerById(state, layerId); + if (!layer) { + return state; + } + + const trackedLayerDescriptor = layer[TRACKED_LAYER_DESCRIPTOR]; + + // this assumes that any nested temp-state in the layer-descriptor (e.g. of styles), is not relevant and can be recovered easily (e.g. this is not the case for __dataRequests) + // That assumption is true in the context of this app, but not generalizable. + // consider rewriting copyPersistentState to only strip the first level of temp state. + const rolledbackLayer = { ...layer, ...trackedLayerDescriptor }; + delete rolledbackLayer[TRACKED_LAYER_DESCRIPTOR]; + + return { + ...state, + layerList: setLayer(state.layerList, rolledbackLayer), + }; +} + +export function setLayer( + layerList: LayerDescriptor[], + layerDescriptor: LayerDescriptor +): LayerDescriptor[] { + const layerIndex = getLayerIndex(layerList, layerDescriptor.id); + if (layerIndex === -1) { + return layerList; + } + const newLayerList = [...layerList]; + newLayerList[layerIndex] = layerDescriptor; + return newLayerList; +} diff --git a/x-pack/plugins/maps/public/reducers/map.js b/x-pack/plugins/maps/public/reducers/map/map.ts similarity index 55% rename from x-pack/plugins/maps/public/reducers/map.js rename to x-pack/plugins/maps/public/reducers/map/map.ts index 9bf0df612bac4..9573f1ac80c6b 100644 --- a/x-pack/plugins/maps/public/reducers/map.js +++ b/x-pack/plugins/maps/public/reducers/map/map.ts @@ -45,75 +45,37 @@ import { ROLLBACK_MAP_SETTINGS, TRACK_MAP_SETTINGS, UPDATE_MAP_SETTING, -} from '../actions'; +} from '../../actions'; import { getDefaultMapSettings } from './default_map_settings'; -import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from './util'; -import { SOURCE_DATA_REQUEST_ID } from '../../common/constants'; - -const getLayerIndex = (list, layerId) => list.findIndex(({ id }) => layerId === id); - -const updateLayerInList = (state, layerId, attribute, newValue) => { - if (!layerId) { - return state; - } - - const { layerList } = state; - const layerIdx = getLayerIndex(layerList, layerId); - if (layerIdx === -1) { - return state; - } - - const updatedLayer = { - ...layerList[layerIdx], - // Update layer w/ new value. If no value provided, toggle boolean value - // allow empty strings, 0-value - [attribute]: - newValue || newValue === '' || newValue === 0 ? newValue : !layerList[layerIdx][attribute], - }; - const updatedList = [ - ...layerList.slice(0, layerIdx), - updatedLayer, - ...layerList.slice(layerIdx + 1), - ]; - return { ...state, layerList: updatedList }; -}; - -const updateLayerSourceDescriptorProp = (state, layerId, propName, value) => { - const { layerList } = state; - const layerIdx = getLayerIndex(layerList, layerId); - const updatedLayer = { - ...layerList[layerIdx], - sourceDescriptor: { - ...layerList[layerIdx].sourceDescriptor, - [propName]: value, - }, - }; - const updatedList = [ - ...layerList.slice(0, layerIdx), - updatedLayer, - ...layerList.slice(layerIdx + 1), - ]; - return { ...state, layerList: updatedList }; -}; - -export const DEFAULT_MAP_STATE = { +import { + getLayerIndex, + removeTrackedLayerState, + rollbackTrackedLayerState, + trackCurrentLayerState, + updateLayerInList, + updateLayerSourceDescriptorProp, +} from './layer_utils'; +import { startDataRequest, stopDataRequest, updateSourceDataRequest } from './data_request_utils'; +import { MapState } from './types'; + +export const DEFAULT_MAP_STATE: MapState = { ready: false, mapInitError: null, goto: null, openTooltips: [], mapState: { - zoom: null, // setting this value does not adjust map zoom, read only value used to store current map zoom for persisting between sessions - center: null, // setting this value does not adjust map view, read only value used to store current map center for persisting between sessions + zoom: undefined, // setting this value does not adjust map zoom, read only value used to store current map zoom for persisting between sessions + center: undefined, // setting this value does not adjust map view, read only value used to store current map center for persisting between sessions scrollZoom: true, - extent: null, - mouseCoordinates: null, - timeFilters: null, - query: null, + extent: undefined, + mouseCoordinates: undefined, + timeFilters: undefined, + query: undefined, filters: [], - refreshConfig: null, - refreshTimerLastTriggeredAt: null, - drawState: null, + refreshConfig: undefined, + refreshTimerLastTriggeredAt: undefined, + drawState: undefined, }, selectedLayerId: null, layerList: [], @@ -122,7 +84,7 @@ export const DEFAULT_MAP_STATE = { __rollbackSettings: null, }; -export function map(state = DEFAULT_MAP_STATE, action) { +export function map(state: MapState = DEFAULT_MAP_STATE, action: any) { switch (action.type) { case UPDATE_DRAW_STATE: return { @@ -223,11 +185,24 @@ export function map(state = DEFAULT_MAP_STATE, action) { case UPDATE_SOURCE_DATA_REQUEST: return updateSourceDataRequest(state, action); case LAYER_DATA_LOAD_STARTED: - return updateWithDataRequest(state, action); + return startDataRequest( + state, + action.layerId, + action.dataId, + action.requestToken, + action.meta + ); case LAYER_DATA_LOAD_ERROR: - return updateWithDataResponse(state, action); + return stopDataRequest(state, action.layerId, action.dataId, action.requestToken); case LAYER_DATA_LOAD_ENDED: - return updateWithDataResponse(state, action); + return stopDataRequest( + state, + action.layerId, + action.dataId, + action.requestToken, + action.meta, + action.data + ); case MAP_READY: return { ...state, ready: true }; case MAP_DESTROYED: @@ -279,7 +254,7 @@ export function map(state = DEFAULT_MAP_STATE, action) { case UPDATE_LAYER_ORDER: return { ...state, - layerList: action.newLayerOrder.map((layerNumber) => state.layerList[layerNumber]), + layerList: action.newLayerOrder.map((layerNumber: number) => state.layerList[layerNumber]), }; case UPDATE_LAYER_PROP: return updateLayerInList(state, action.id, action.propName, action.newValue); @@ -360,158 +335,3 @@ export function map(state = DEFAULT_MAP_STATE, action) { return state; } } - -function findDataRequest(layerDescriptor, dataRequestAction) { - if (!layerDescriptor.__dataRequests) { - return; - } - - return layerDescriptor.__dataRequests.find((dataRequest) => { - return dataRequest.dataId === dataRequestAction.dataId; - }); -} - -function updateWithDataRequest(state, action) { - let dataRequest = getValidDataRequest(state, action, false); - const layerRequestingData = findLayerById(state, action.layerId); - - if (!dataRequest) { - dataRequest = { - dataId: action.dataId, - }; - layerRequestingData.__dataRequests = [ - ...(layerRequestingData.__dataRequests ? layerRequestingData.__dataRequests : []), - dataRequest, - ]; - } - dataRequest.dataMetaAtStart = action.meta; - dataRequest.dataRequestToken = action.requestToken; - const layerList = [...state.layerList]; - return { ...state, layerList }; -} - -function updateSourceDataRequest(state, action) { - const layerDescriptor = findLayerById(state, action.layerId); - if (!layerDescriptor) { - return state; - } - const dataRequest = layerDescriptor.__dataRequests.find((dataRequest) => { - return dataRequest.dataId === SOURCE_DATA_REQUEST_ID; - }); - if (!dataRequest) { - return state; - } - - dataRequest.data = action.newData; - return resetDataRequest(state, action, dataRequest); -} - -function updateWithDataResponse(state, action) { - const dataRequest = getValidDataRequest(state, action); - if (!dataRequest) { - return state; - } - - dataRequest.data = action.data; - dataRequest.dataMeta = { ...dataRequest.dataMetaAtStart, ...action.meta }; - dataRequest.dataMetaAtStart = null; - return resetDataRequest(state, action, dataRequest); -} - -export function resetDataRequest(state, action, request) { - const dataRequest = request || getValidDataRequest(state, action); - if (!dataRequest) { - return state; - } - - const layer = findLayerById(state, action.layerId); - const dataRequestIndex = layer.__dataRequests.indexOf(dataRequest); - - const newDataRequests = [...layer.__dataRequests]; - newDataRequests[dataRequestIndex] = { - ...dataRequest, - dataRequestToken: null, - }; - - const layerIndex = state.layerList.indexOf(layer); - const newLayerList = [...state.layerList]; - newLayerList[layerIndex] = { - ...layer, - __dataRequests: newDataRequests, - }; - return { ...state, layerList: newLayerList }; -} - -function getValidDataRequest(state, action, checkRequestToken = true) { - const layer = findLayerById(state, action.layerId); - if (!layer) { - return; - } - - const dataRequest = findDataRequest(layer, action); - if (!dataRequest) { - return; - } - - if ( - checkRequestToken && - dataRequest.dataRequestToken && - dataRequest.dataRequestToken !== action.requestToken - ) { - // ignore responses to outdated requests - return; - } - return dataRequest; -} - -function findLayerById(state, id) { - return state.layerList.find((layer) => layer.id === id); -} - -function trackCurrentLayerState(state, layerId) { - const layer = findLayerById(state, layerId); - const layerCopy = copyPersistentState(layer); - return updateLayerInList(state, layerId, TRACKED_LAYER_DESCRIPTOR, layerCopy); -} - -function removeTrackedLayerState(state, layerId) { - const layer = findLayerById(state, layerId); - if (!layer) { - return state; - } - - const copyLayer = { ...layer }; - delete copyLayer[TRACKED_LAYER_DESCRIPTOR]; - - return { - ...state, - layerList: replaceInLayerList(state.layerList, layerId, copyLayer), - }; -} - -function rollbackTrackedLayerState(state, layerId) { - const layer = findLayerById(state, layerId); - if (!layer) { - return state; - } - - const trackedLayerDescriptor = layer[TRACKED_LAYER_DESCRIPTOR]; - - //this assumes that any nested temp-state in the layer-descriptor (e.g. of styles), is not relevant and can be recovered easily (e.g. this is not the case for __dataRequests) - //That assumption is true in the context of this app, but not generalizable. - //consider rewriting copyPersistentState to only strip the first level of temp state. - const rolledbackLayer = { ...layer, ...trackedLayerDescriptor }; - delete rolledbackLayer[TRACKED_LAYER_DESCRIPTOR]; - - return { - ...state, - layerList: replaceInLayerList(state.layerList, layerId, rolledbackLayer), - }; -} - -function replaceInLayerList(layerList, layerId, newLayerDescriptor) { - const layerIndex = getLayerIndex(layerList, layerId); - const newLayerList = [...layerList]; - newLayerList[layerIndex] = newLayerDescriptor; - return newLayerList; -} diff --git a/x-pack/plugins/maps/public/reducers/map.d.ts b/x-pack/plugins/maps/public/reducers/map/types.ts similarity index 90% rename from x-pack/plugins/maps/public/reducers/map.d.ts rename to x-pack/plugins/maps/public/reducers/map/types.ts index 1cf3756160964..6b10b4a66fb61 100644 --- a/x-pack/plugins/maps/public/reducers/map.d.ts +++ b/x-pack/plugins/maps/public/reducers/map/types.ts @@ -16,9 +16,9 @@ import { MapQuery, MapRefreshConfig, TooltipState, -} from '../../common/descriptor_types'; -import { INITIAL_LOCATION } from '../../common/constants'; -import { Filter, TimeRange } from '../../../../../src/plugins/data/public'; +} from '../../../common/descriptor_types'; +import { INITIAL_LOCATION } from '../../../common/constants'; +import { Filter, TimeRange } from '../../../../../../src/plugins/data/public'; export type MapContext = { zoom?: number; diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index b6ee5274f690d..0786c47d44c8e 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -42,7 +42,7 @@ import { } from '../../../kibana_services'; import { goToSpecifiedPath } from '../../../render_app'; import { LayerDescriptor } from '../../../../common/descriptor_types'; -import { copyPersistentState } from '../../../reducers/util'; +import { copyPersistentState } from '../../../reducers/copy_persistent_state'; import { getBreadcrumbs } from './get_breadcrumbs'; import { DEFAULT_IS_LAYER_TOC_OPEN } from '../../../reducers/ui'; import { createBasemapLayerDescriptor } from '../../../classes/layers/create_basemap_layer_descriptor'; diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index 35c73a2bd2f1c..2f87aa7d11394 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -22,7 +22,7 @@ import { getInspectorAdapters, } from '../reducers/non_serializable_instances'; import { TiledVectorLayer } from '../classes/layers/tiled_vector_layer/tiled_vector_layer'; -import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/util'; +import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/copy_persistent_state'; import { InnerJoin } from '../classes/joins/inner_join'; import { getSourceByType } from '../classes/sources/source_registry'; import { GeoJsonFileSource } from '../classes/sources/geojson_file_source'; From eb21f576f98ea851a533303f0b9b3942c0a6fa9c Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 22 Feb 2021 20:59:21 -0500 Subject: [PATCH 06/70] [Security Solution] Fix paradigm around our container for search strategy query (#90982) * fix paradigm around our serach strategy query * simplify logic to act with search strategy * miss to delete a declaration --- .../events/last_event_time/index.test.ts | 3 + .../events/last_event_time/index.ts | 56 ++++---- .../containers/matrix_histogram/index.ts | 66 ++++----- .../public/common/containers/source/index.tsx | 130 ++++++++---------- .../containers/sourcerer/index.test.tsx | 1 + .../common/lib/kibana/__mocks__/index.ts | 1 + .../common/lib/kibana/kibana_react.mock.ts | 1 + .../containers/authentications/index.tsx | 57 ++++---- .../hosts/containers/hosts/details/_index.tsx | 51 ++++--- .../hosts/first_last_seen/index.tsx | 60 ++++---- .../public/hosts/containers/hosts/index.tsx | 49 ++++--- .../kpi_hosts/authentications/index.tsx | 57 ++++---- .../containers/kpi_hosts/hosts/index.tsx | 54 ++++---- .../containers/kpi_hosts/unique_ips/index.tsx | 58 ++++---- .../containers/uncommon_processes/index.tsx | 56 ++++---- .../network/containers/details/index.tsx | 53 ++++--- .../containers/kpi_network/dns/index.tsx | 51 ++++--- .../kpi_network/network_events/index.tsx | 51 ++++--- .../kpi_network/tls_handshakes/index.tsx | 51 ++++--- .../kpi_network/unique_flows/index.tsx | 51 ++++--- .../kpi_network/unique_private_ips/index.tsx | 59 ++++---- .../network/containers/network_dns/index.tsx | 57 ++++---- .../network/containers/network_http/index.tsx | 55 ++++---- .../network_top_countries/index.tsx | 55 ++++---- .../containers/network_top_n_flow/index.tsx | 55 ++++---- .../public/network/containers/tls/index.tsx | 53 +++---- .../public/network/containers/users/index.tsx | 55 ++++---- .../containers/overview_host/index.tsx | 51 ++++--- .../containers/overview_network/index.tsx | 51 ++++--- .../timelines/containers/details/index.tsx | 38 +++-- .../public/timelines/containers/index.tsx | 91 ++++++------ .../timelines/containers/kpis/index.tsx | 33 ++--- 32 files changed, 752 insertions(+), 858 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts index 72edf8b58ab70..4f12ec2e5de2d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts @@ -26,6 +26,9 @@ const mockUseKibana = { next(mockData); /* eslint-disable no-empty */ } catch (e) {} + return { + unsubscribe: jest.fn(), + }; }), }), }, diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts index f3779870db404..0a7df66f6c1d5 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { useKibana } from '../../../../common/lib/kibana'; @@ -22,7 +23,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import * as i18n from './translations'; import { DocValueFields } from '../../../../../common/search_strategy'; @@ -48,6 +48,7 @@ export const useTimelineLastEventTime = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ TimelineLastEventTimeRequest, @@ -71,12 +72,11 @@ export const useTimelineLastEventTime = ({ const timelineLastEventTimeSearch = useCallback( (request: TimelineEventsLastEventTimeRequestOptions) => { - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search< TimelineEventsLastEventTimeRequestOptions, TimelineEventsLastEventTimeStrategyResponse @@ -87,46 +87,36 @@ export const useTimelineLastEventTime = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setTimelineLastEventTimeResponse((prevResponse) => ({ - ...prevResponse, - errorMessage: undefined, - lastSeen: response.lastSeen, - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setTimelineLastEventTimeResponse((prevResponse) => ({ + ...prevResponse, + errorMessage: undefined, + lastSeen: response.lastSeen, + refetch: refetch.current, + })); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_LAST_EVENT_TIME); - searchSubscription$.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_LAST_EVENT_TIME, - text: msg.message, - }); - setTimelineLastEventTimeResponse((prevResponse) => ({ - ...prevResponse, - errorMessage: msg.message, - })); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_LAST_EVENT_TIME, + text: msg.message, + }); + setTimelineLastEventTimeResponse((prevResponse) => ({ + ...prevResponse, + errorMessage: msg.message, + })); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts] ); @@ -149,6 +139,10 @@ export const useTimelineLastEventTime = ({ useEffect(() => { timelineLastEventTimeSearch(TimelineLastEventTimeRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [TimelineLastEventTimeRequest, timelineLastEventTimeSearch]); return [loading, timelineLastEventTimeResponse]; diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index 13c5d7779dbde..7884c7bd00263 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { getOr, isEmpty, noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types'; import { inputsModel } from '../../../common/store'; @@ -20,7 +21,6 @@ import { MatrixHistogramData, } from '../../../../common/search_strategy/security_solution'; import { isErrorResponse, isCompleteResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; import * as i18n from './translations'; @@ -63,6 +63,7 @@ export const useMatrixHistogram = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ matrixHistogramRequest, @@ -96,12 +97,11 @@ export const useMatrixHistogram = ({ const hostsSearch = useCallback( (request: MatrixHistogramRequestOptions) => { - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -109,51 +109,41 @@ export const useMatrixHistogram = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - const histogramBuckets: Buckets = getOr( - bucketEmpty, - 'rawResponse.aggregations.eventActionGroup.buckets', - response - ); - setLoading(false); - setMatrixHistogramResponse((prevResponse) => ({ - ...prevResponse, - data: response.matrixHistogramData, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - totalCount: response.totalCount, - buckets: histogramBuckets, - })); - } - searchSubscription$.unsubscribe(); + const histogramBuckets: Buckets = getOr( + bucketEmpty, + 'rawResponse.aggregations.eventActionGroup.buckets', + response + ); + setLoading(false); + setMatrixHistogramResponse((prevResponse) => ({ + ...prevResponse, + data: response.matrixHistogramData, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + totalCount: response.totalCount, + buckets: histogramBuckets, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_MATRIX_HISTOGRAM); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!didCancel) { - setLoading(false); - } - if (!(msg instanceof AbortError)) { - notifications.toasts.addError(msg, { - title: errorMessage ?? i18n.FAIL_MATRIX_HISTOGRAM, - }); - } + setLoading(false); + notifications.toasts.addError(msg, { + title: errorMessage ?? i18n.FAIL_MATRIX_HISTOGRAM, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, errorMessage, notifications.toasts] ); @@ -196,6 +186,10 @@ export const useMatrixHistogram = ({ if (!skip) { hostsSearch(matrixHistogramRequest); } + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [matrixHistogramRequest, hostsSearch, skip]); const runMatrixHistogramSearch = useCallback( diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx index b5b0746f3ea88..6daced08be282 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx @@ -10,6 +10,7 @@ import memoizeOne from 'memoize-one'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { IIndexPattern } from 'src/plugins/data/public'; +import { Subscription } from 'rxjs'; import { useKibana } from '../../lib/kibana'; import { @@ -19,7 +20,7 @@ import { BrowserField, BrowserFields, } from '../../../../common/search_strategy/index_fields'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; +import { isErrorResponse, isCompleteResponse } from '../../../../../../../src/plugins/data/common'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import * as i18n from './translations'; import { SourcererScopeName } from '../../store/sourcerer/model'; @@ -126,6 +127,7 @@ export const useFetchIndex = ( ): [boolean, FetchIndexReturn] => { const { data, notifications } = useKibana().services; const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const previousIndexesName = useRef([]); const [isLoading, setLoading] = useState(false); @@ -139,11 +141,10 @@ export const useFetchIndex = ( const indexFieldsSearch = useCallback( (iNames) => { - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( { indices: iNames, onlyCheckIfIndicesExist }, { @@ -153,46 +154,37 @@ export const useFetchIndex = ( ) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { - if (!didCancel) { - const stringifyIndices = response.indicesExist.sort().join(); - previousIndexesName.current = response.indicesExist; - setLoading(false); - setState({ - browserFields: getBrowserFields(stringifyIndices, response.indexFields), - docValueFields: getDocValueFields(stringifyIndices, response.indexFields), - indexes: response.indicesExist, - indexExists: response.indicesExist.length > 0, - indexPatterns: getIndexFields(stringifyIndices, response.indexFields), - }); - } - searchSubscription$.unsubscribe(); - } else if (!didCancel && response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { + const stringifyIndices = response.indicesExist.sort().join(); + previousIndexesName.current = response.indicesExist; + setLoading(false); + setState({ + browserFields: getBrowserFields(stringifyIndices, response.indexFields), + docValueFields: getDocValueFields(stringifyIndices, response.indexFields), + indexes: response.indicesExist, + indexExists: response.indicesExist.length > 0, + indexPatterns: getIndexFields(stringifyIndices, response.indexFields), + }); + searchSubscription$.current.unsubscribe(); + } else if (isErrorResponse(response)) { setLoading(false); notifications.toasts.addWarning(i18n.ERROR_BEAT_FIELDS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!didCancel) { - setLoading(false); - } - - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - text: msg.message, - title: i18n.FAIL_BEAT_FIELDS, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + text: msg.message, + title: i18n.FAIL_BEAT_FIELDS, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, onlyCheckIfIndicesExist] ); @@ -201,6 +193,10 @@ export const useFetchIndex = ( if (!isEmpty(indexNames) && !isEqual(previousIndexesName.current, indexNames)) { indexFieldsSearch(indexNames); } + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [indexNames, indexFieldsSearch, previousIndexesName]); return [isLoading, state]; @@ -209,6 +205,7 @@ export const useFetchIndex = ( export const useIndexFields = (sourcererScopeName: SourcererScopeName) => { const { data, notifications } = useKibana().services; const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const dispatch = useDispatch(); const indexNamesSelectedSelector = useMemo( () => sourcererSelectors.getIndexNamesSelectedSelector(), @@ -228,11 +225,10 @@ export const useIndexFields = (sourcererScopeName: SourcererScopeName) => { const indexFieldsSearch = useCallback( (indicesName) => { - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( { indices: indicesName, onlyCheckIfIndicesExist: false }, { @@ -242,52 +238,42 @@ export const useIndexFields = (sourcererScopeName: SourcererScopeName) => { ) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { - if (!didCancel) { - const stringifyIndices = response.indicesExist.sort().join(); - dispatch( - sourcererActions.setSource({ + if (isCompleteResponse(response)) { + const stringifyIndices = response.indicesExist.sort().join(); + dispatch( + sourcererActions.setSource({ + id: sourcererScopeName, + payload: { + browserFields: getBrowserFields(stringifyIndices, response.indexFields), + docValueFields: getDocValueFields(stringifyIndices, response.indexFields), + errorMessage: null, id: sourcererScopeName, - payload: { - browserFields: getBrowserFields(stringifyIndices, response.indexFields), - docValueFields: getDocValueFields(stringifyIndices, response.indexFields), - errorMessage: null, - id: sourcererScopeName, - indexPattern: getIndexFields(stringifyIndices, response.indexFields), - indicesExist: response.indicesExist.length > 0, - loading: false, - }, - }) - ); - } - searchSubscription$.unsubscribe(); - } else if (!didCancel && response.isPartial && !response.isRunning) { - // TODO: Make response error status clearer + indexPattern: getIndexFields(stringifyIndices, response.indexFields), + indicesExist: response.indicesExist.length > 0, + loading: false, + }, + }) + ); + searchSubscription$.current.unsubscribe(); + } else if (isErrorResponse(response)) { setLoading(false); notifications.toasts.addWarning(i18n.ERROR_BEAT_FIELDS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!didCancel) { - setLoading(false); - } - - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - text: msg.message, - title: i18n.FAIL_BEAT_FIELDS, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + text: msg.message, + title: i18n.FAIL_BEAT_FIELDS, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, dispatch, notifications.toasts, setLoading, sourcererScopeName] ); @@ -296,5 +282,9 @@ export const useIndexFields = (sourcererScopeName: SourcererScopeName) => { if (!isEmpty(indexNames) && previousIndexNames !== indexNames.sort().join()) { indexFieldsSearch(indexNames); } + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [indexNames, indexFieldsSearch, previousIndexNames]); }; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx index 0bd0ba1feba4e..7a20c98a8d4bf 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx @@ -71,6 +71,7 @@ jest.mock('../../lib/kibana', () => ({ subscribe: jest.fn().mockImplementation(() => ({ error: jest.fn(), next: jest.fn(), + unsubscribe: jest.fn(), })), })), }, diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts index 81a521ea68bb7..79c7b21158005 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts @@ -26,6 +26,7 @@ export const useKibana = jest.fn().mockReturnValue({ subscribe: jest.fn().mockImplementation(() => ({ error: jest.fn(), next: jest.fn(), + unsubscribe: jest.fn(), })), })), }, diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index 5ab68ccddaf58..17ab5026f757f 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -118,6 +118,7 @@ export const createStartServicesMock = (): StartServices => { subscribe: jest.fn().mockImplementation(() => ({ error: jest.fn(), next: jest.fn(), + unsubscribe: jest.fn(), })), })), }, diff --git a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx index cd094ec40d092..f1efdd2e3c432 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx @@ -8,10 +8,9 @@ import { noop, pick } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; - import { HostsQueries } from '../../../../common/search_strategy/security_solution'; import { HostAuthenticationsRequestOptions, @@ -75,6 +74,7 @@ export const useAuthentications = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ authenticationsRequest, @@ -121,13 +121,11 @@ export const useAuthentications = ({ if (request == null || skip) { return; } - - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -135,43 +133,36 @@ export const useAuthentications = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setAuthenticationsResponse((prevResponse) => ({ - ...prevResponse, - authentications: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setAuthenticationsResponse((prevResponse) => ({ + ...prevResponse, + authentications: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); notifications.toasts.addWarning(i18n.ERROR_AUTHENTICATIONS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_AUTHENTICATIONS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_AUTHENTICATIONS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -201,6 +192,10 @@ export const useAuthentications = ({ useEffect(() => { authenticationsSearch(authenticationsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [authenticationsRequest, authenticationsSearch]); return [loading, authenticationsResponse]; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx index d1545458f8b56..532b9f262e136 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx @@ -10,6 +10,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { useKibana } from '../../../../common/lib/kibana'; @@ -25,7 +26,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -60,6 +60,7 @@ export const useHostDetails = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [hostDetailsRequest, setHostDetailsRequest] = useState( null @@ -83,12 +84,11 @@ export const useHostDetails = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -96,42 +96,35 @@ export const useHostDetails = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setHostDetailsResponse((prevResponse) => ({ - ...prevResponse, - hostDetails: response.hostDetails, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setHostDetailsResponse((prevResponse) => ({ + ...prevResponse, + hostDetails: response.hostDetails, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_HOST_OVERVIEW); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_HOST_OVERVIEW, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_HOST_OVERVIEW, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -154,6 +147,10 @@ export const useHostDetails = ({ } return prevRequest; }); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [endDate, hostName, indexNames, startDate]); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx index da574dfa4ea44..bd49d6be34e5c 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx @@ -7,6 +7,7 @@ import deepEqual from 'fast-deep-equal'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { useKibana } from '../../../../common/lib/kibana'; import { @@ -21,7 +22,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; const ID = 'firstLastSeenHostQuery'; @@ -47,6 +47,7 @@ export const useFirstLastSeenHost = ({ }: UseHostFirstLastSeen): [boolean, FirstLastSeenHostArgs] => { const { data, notifications } = useKibana().services; const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ firstLastSeenHostRequest, @@ -71,12 +72,10 @@ export const useFirstLastSeenHost = ({ const firstLastSeenHostSearch = useCallback( (request: HostFirstLastSeenRequestOptions) => { - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -84,45 +83,38 @@ export const useFirstLastSeenHost = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setFirstLastSeenHostResponse((prevResponse) => ({ - ...prevResponse, - errorMessage: null, - firstSeen: response.firstSeen, - lastSeen: response.lastSeen, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setFirstLastSeenHostResponse((prevResponse) => ({ + ...prevResponse, + errorMessage: null, + firstSeen: response.firstSeen, + lastSeen: response.lastSeen, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_FIRST_LAST_SEEN_HOST); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - setFirstLastSeenHostResponse((prevResponse) => ({ - ...prevResponse, - errorMessage: msg, - })); - notifications.toasts.addDanger({ - title: i18n.FAIL_FIRST_LAST_SEEN_HOST, - text: msg.message, - }); - } + setLoading(false); + setFirstLastSeenHostResponse((prevResponse) => ({ + ...prevResponse, + errorMessage: msg, + })); + notifications.toasts.addDanger({ + title: i18n.FAIL_FIRST_LAST_SEEN_HOST, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts] ); @@ -144,6 +136,10 @@ export const useFirstLastSeenHost = ({ useEffect(() => { firstLastSeenHostSearch(firstLastSeenHostRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [firstLastSeenHostRequest, firstLastSeenHostSearch]); return [loading, firstLastSeenHostResponse]; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx index af432f4e64f80..383c4c233914f 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel, State } from '../../../common/store'; import { createFilter } from '../../../common/containers/helpers'; @@ -27,7 +28,6 @@ import { ESTermQuery } from '../../../../common/typed_json'; import * as i18n from './translations'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; @@ -73,6 +73,7 @@ export const useAllHost = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [hostsRequest, setHostRequest] = useState(null); @@ -118,12 +119,11 @@ export const useAllHost = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -131,41 +131,34 @@ export const useAllHost = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setHostsResponse((prevResponse) => ({ - ...prevResponse, - hosts: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setHostsResponse((prevResponse) => ({ + ...prevResponse, + hosts: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + searchSubscription.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_ALL_HOST); - searchSubscription$.unsubscribe(); + searchSubscription.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ title: i18n.FAIL_ALL_HOST, text: msg.message }); - } + setLoading(false); + notifications.toasts.addDanger({ title: i18n.FAIL_ALL_HOST, text: msg.message }); + searchSubscription.current.unsubscribe(); }, }); }; + searchSubscription.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -208,6 +201,10 @@ export const useAllHost = ({ useEffect(() => { hostsSearch(hostsRequest); + return () => { + searchSubscription.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [hostsRequest, hostsSearch]); return [loading, hostsResponse]; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx index 157020ec4ba8d..ad3c7e0e829fb 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -20,7 +21,6 @@ import { import { ESTermQuery } from '../../../../../common/typed_json'; import * as i18n from './translations'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -52,6 +52,7 @@ export const useHostsKpiAuthentications = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ hostsKpiAuthenticationsRequest, @@ -81,12 +82,11 @@ export const useHostsKpiAuthentications = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( request, { @@ -97,45 +97,38 @@ export const useHostsKpiAuthentications = ({ .subscribe({ next: (response) => { if (!response.isPartial && !response.isRunning) { - if (!didCancel) { - setLoading(false); - setHostsKpiAuthenticationsResponse((prevResponse) => ({ - ...prevResponse, - authenticationsSuccess: response.authenticationsSuccess, - authenticationsSuccessHistogram: response.authenticationsSuccessHistogram, - authenticationsFailure: response.authenticationsFailure, - authenticationsFailureHistogram: response.authenticationsFailureHistogram, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setHostsKpiAuthenticationsResponse((prevResponse) => ({ + ...prevResponse, + authenticationsSuccess: response.authenticationsSuccess, + authenticationsSuccessHistogram: response.authenticationsSuccessHistogram, + authenticationsFailure: response.authenticationsFailure, + authenticationsFailureHistogram: response.authenticationsFailureHistogram, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (response.isPartial && !response.isRunning) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_HOSTS_KPI_AUTHENTICATIONS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_HOSTS_KPI_AUTHENTICATIONS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_HOSTS_KPI_AUTHENTICATIONS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -162,6 +155,10 @@ export const useHostsKpiAuthentications = ({ useEffect(() => { hostsKpiAuthenticationsSearch(hostsKpiAuthenticationsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [hostsKpiAuthenticationsRequest, hostsKpiAuthenticationsSearch]); return [loading, hostsKpiAuthenticationsResponse]; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx index 8faafee8cfcc4..8ed1aaecb6f0e 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -20,7 +21,6 @@ import { import { ESTermQuery } from '../../../../../common/typed_json'; import * as i18n from './translations'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -51,6 +51,7 @@ export const useHostsKpiHosts = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ hostsKpiHostsRequest, @@ -74,13 +75,11 @@ export const useHostsKpiHosts = ({ if (request == null || skip) { return; } - - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -88,43 +87,36 @@ export const useHostsKpiHosts = ({ .subscribe({ next: (response) => { if (!response.isPartial && !response.isRunning) { - if (!didCancel) { - setLoading(false); - setHostsKpiHostsResponse((prevResponse) => ({ - ...prevResponse, - hosts: response.hosts, - hostsHistogram: response.hostsHistogram, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setHostsKpiHostsResponse((prevResponse) => ({ + ...prevResponse, + hosts: response.hosts, + hostsHistogram: response.hostsHistogram, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (response.isPartial && !response.isRunning) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_HOSTS_KPI_HOSTS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_HOSTS_KPI_HOSTS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_HOSTS_KPI_HOSTS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -151,6 +143,10 @@ export const useHostsKpiHosts = ({ useEffect(() => { hostsKpiHostsSearch(hostsKpiHostsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [hostsKpiHostsRequest, hostsKpiHostsSearch]); return [loading, hostsKpiHostsResponse]; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx index de29f4a60d9d2..b34de267f4519 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -20,7 +21,6 @@ import { import { ESTermQuery } from '../../../../../common/typed_json'; import * as i18n from './translations'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -52,6 +52,7 @@ export const useHostsKpiUniqueIps = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ hostsKpiUniqueIpsRequest, @@ -80,12 +81,10 @@ export const useHostsKpiUniqueIps = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -93,45 +92,38 @@ export const useHostsKpiUniqueIps = ({ .subscribe({ next: (response) => { if (!response.isPartial && !response.isRunning) { - if (!didCancel) { - setLoading(false); - setHostsKpiUniqueIpsResponse((prevResponse) => ({ - ...prevResponse, - uniqueSourceIps: response.uniqueSourceIps, - uniqueSourceIpsHistogram: response.uniqueSourceIpsHistogram, - uniqueDestinationIps: response.uniqueDestinationIps, - uniqueDestinationIpsHistogram: response.uniqueDestinationIpsHistogram, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setHostsKpiUniqueIpsResponse((prevResponse) => ({ + ...prevResponse, + uniqueSourceIps: response.uniqueSourceIps, + uniqueSourceIpsHistogram: response.uniqueSourceIpsHistogram, + uniqueDestinationIps: response.uniqueDestinationIps, + uniqueDestinationIpsHistogram: response.uniqueDestinationIpsHistogram, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (response.isPartial && !response.isRunning) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_HOSTS_KPI_UNIQUE_IPS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_HOSTS_KPI_UNIQUE_IPS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_HOSTS_KPI_UNIQUE_IPS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -158,6 +150,10 @@ export const useHostsKpiUniqueIps = ({ useEffect(() => { hostsKpiUniqueIpsSearch(hostsKpiUniqueIpsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [hostsKpiUniqueIpsRequest, hostsKpiUniqueIpsSearch]); return [loading, hostsKpiUniqueIpsResponse]; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx index 7f67d55003ecb..1e07b94b55b74 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx @@ -8,9 +8,9 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; @@ -75,6 +75,7 @@ export const useUncommonProcesses = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ uncommonProcessesRequest, @@ -122,13 +123,11 @@ export const useUncommonProcesses = ({ if (request == null || skip) { return; } - - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( request, { @@ -139,43 +138,36 @@ export const useUncommonProcesses = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setUncommonProcessesResponse((prevResponse) => ({ - ...prevResponse, - uncommonProcesses: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setUncommonProcessesResponse((prevResponse) => ({ + ...prevResponse, + uncommonProcesses: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); notifications.toasts.addWarning(i18n.ERROR_UNCOMMON_PROCESSES); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_UNCOMMON_PROCESSES, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_UNCOMMON_PROCESSES, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -205,6 +197,10 @@ export const useUncommonProcesses = ({ useEffect(() => { uncommonProcessesSearch(uncommonProcessesRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [uncommonProcessesRequest, uncommonProcessesSearch]); return [loading, uncommonProcessesResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/details/index.tsx b/x-pack/plugins/security_solution/public/network/containers/details/index.tsx index 4e81c1bc30c2a..6bbe7f8f43773 100644 --- a/x-pack/plugins/security_solution/public/network/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/details/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useState, useEffect, useCallback, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { ESTermQuery } from '../../../../common/typed_json'; import { inputsModel } from '../../../common/store'; @@ -20,7 +21,6 @@ import { NetworkDetailsStrategyResponse, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import * as i18n from './translations'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; @@ -55,6 +55,7 @@ export const useNetworkDetails = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ @@ -78,13 +79,10 @@ export const useNetworkDetails = ({ if (request == null || skip) { return; } - - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -92,42 +90,35 @@ export const useNetworkDetails = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkDetailsResponse((prevResponse) => ({ - ...prevResponse, - networkDetails: response.networkDetails, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkDetailsResponse((prevResponse) => ({ + ...prevResponse, + networkDetails: response.networkDetails, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_DETAILS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_DETAILS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_DETAILS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -151,6 +142,10 @@ export const useNetworkDetails = ({ useEffect(() => { networkDetailsSearch(networkDetailsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkDetailsRequest, networkDetailsSearch]); return [loading, networkDetailsResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx index 2a71f32ad2dc1..345aee4de2df2 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -24,7 +25,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -56,6 +56,7 @@ export const useNetworkKpiDns = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ networkKpiDnsRequest, @@ -79,12 +80,11 @@ export const useNetworkKpiDns = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -92,42 +92,35 @@ export const useNetworkKpiDns = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkKpiDnsResponse((prevResponse) => ({ - ...prevResponse, - dnsQueries: response.dnsQueries, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkKpiDnsResponse((prevResponse) => ({ + ...prevResponse, + dnsQueries: response.dnsQueries, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_KPI_DNS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_KPI_DNS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_KPI_DNS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -154,6 +147,10 @@ export const useNetworkKpiDns = ({ useEffect(() => { networkKpiDnsSearch(networkKpiDnsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkKpiDnsRequest, networkKpiDnsSearch]); return [loading, networkKpiDnsResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx index 4611be6e77829..6dd3df1055f9c 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -24,7 +25,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -56,6 +56,7 @@ export const useNetworkKpiNetworkEvents = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ networkKpiNetworkEventsRequest, @@ -82,12 +83,11 @@ export const useNetworkKpiNetworkEvents = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( request, { @@ -98,42 +98,35 @@ export const useNetworkKpiNetworkEvents = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkKpiNetworkEventsResponse((prevResponse) => ({ - ...prevResponse, - networkEvents: response.networkEvents, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkKpiNetworkEventsResponse((prevResponse) => ({ + ...prevResponse, + networkEvents: response.networkEvents, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_KPI_NETWORK_EVENTS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_KPI_NETWORK_EVENTS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_KPI_NETWORK_EVENTS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -160,6 +153,10 @@ export const useNetworkKpiNetworkEvents = ({ useEffect(() => { networkKpiNetworkEventsSearch(networkKpiNetworkEventsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkKpiNetworkEventsRequest, networkKpiNetworkEventsSearch]); return [loading, networkKpiNetworkEventsResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx index 5e414e22e036e..dfc7d0a28db79 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -24,7 +25,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -56,6 +56,7 @@ export const useNetworkKpiTlsHandshakes = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ networkKpiTlsHandshakesRequest, @@ -81,12 +82,11 @@ export const useNetworkKpiTlsHandshakes = ({ if (request == null || skip) { return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( request, { @@ -97,42 +97,35 @@ export const useNetworkKpiTlsHandshakes = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkKpiTlsHandshakesResponse((prevResponse) => ({ - ...prevResponse, - tlsHandshakes: response.tlsHandshakes, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkKpiTlsHandshakesResponse((prevResponse) => ({ + ...prevResponse, + tlsHandshakes: response.tlsHandshakes, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_KPI_TLS_HANDSHAKES); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_KPI_TLS_HANDSHAKES, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_KPI_TLS_HANDSHAKES, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -159,6 +152,10 @@ export const useNetworkKpiTlsHandshakes = ({ useEffect(() => { networkKpiTlsHandshakesSearch(networkKpiTlsHandshakesRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkKpiTlsHandshakesRequest, networkKpiTlsHandshakesSearch]); return [loading, networkKpiTlsHandshakesResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx index d7e650cadef1a..08c4d917f5da3 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -24,7 +25,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -56,6 +56,7 @@ export const useNetworkKpiUniqueFlows = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ networkKpiUniqueFlowsRequest, @@ -82,12 +83,11 @@ export const useNetworkKpiUniqueFlows = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( request, { @@ -98,42 +98,35 @@ export const useNetworkKpiUniqueFlows = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkKpiUniqueFlowsResponse((prevResponse) => ({ - ...prevResponse, - uniqueFlowId: response.uniqueFlowId, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkKpiUniqueFlowsResponse((prevResponse) => ({ + ...prevResponse, + uniqueFlowId: response.uniqueFlowId, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_KPI_UNIQUE_FLOWS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_KPI_UNIQUE_FLOWS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_KPI_UNIQUE_FLOWS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -160,6 +153,10 @@ export const useNetworkKpiUniqueFlows = ({ useEffect(() => { networkKpiUniqueFlowsSearch(networkKpiUniqueFlowsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkKpiUniqueFlowsRequest, networkKpiUniqueFlowsSearch]); return [loading, networkKpiUniqueFlowsResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx index bb33d399a9a49..a532f4f11a301 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx @@ -8,6 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; @@ -25,7 +26,6 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -60,6 +60,7 @@ export const useNetworkKpiUniquePrivateIps = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ networkKpiUniquePrivateIpsRequest, @@ -89,12 +90,11 @@ export const useNetworkKpiUniquePrivateIps = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search< NetworkKpiUniquePrivateIpsRequestOptions, NetworkKpiUniquePrivateIpsStrategyResponse @@ -105,46 +105,39 @@ export const useNetworkKpiUniquePrivateIps = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkKpiUniquePrivateIpsResponse((prevResponse) => ({ - ...prevResponse, - uniqueDestinationPrivateIps: response.uniqueDestinationPrivateIps, - uniqueDestinationPrivateIpsHistogram: - response.uniqueDestinationPrivateIpsHistogram, - uniqueSourcePrivateIps: response.uniqueSourcePrivateIps, - uniqueSourcePrivateIpsHistogram: response.uniqueSourcePrivateIpsHistogram, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkKpiUniquePrivateIpsResponse((prevResponse) => ({ + ...prevResponse, + uniqueDestinationPrivateIps: response.uniqueDestinationPrivateIps, + uniqueDestinationPrivateIpsHistogram: + response.uniqueDestinationPrivateIpsHistogram, + uniqueSourcePrivateIps: response.uniqueSourcePrivateIps, + uniqueSourcePrivateIpsHistogram: response.uniqueSourcePrivateIpsHistogram, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_KPI_UNIQUE_PRIVATE_IPS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_KPI_UNIQUE_PRIVATE_IPS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_KPI_UNIQUE_PRIVATE_IPS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -171,6 +164,10 @@ export const useNetworkKpiUniquePrivateIps = ({ useEffect(() => { networkKpiUniquePrivateIpsSearch(networkKpiUniquePrivateIpsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkKpiUniquePrivateIpsRequest, networkKpiUniquePrivateIpsSearch]); return [loading, networkKpiUniquePrivateIpsResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx index 84ec4ffac3f2c..5ce31bada520b 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { ESTermQuery } from '../../../../common/typed_json'; import { inputsModel } from '../../../common/store'; @@ -26,7 +27,6 @@ import { PageInfoPaginated, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import * as i18n from './translations'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; @@ -71,6 +71,7 @@ export const useNetworkDns = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [networkDnsRequest, setNetworkDnsRequest] = useState(null); @@ -116,12 +117,11 @@ export const useNetworkDns = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -129,45 +129,38 @@ export const useNetworkDns = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkDnsResponse((prevResponse) => ({ - ...prevResponse, - networkDns: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - histogram: response.histogram ?? prevResponse.histogram, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkDnsResponse((prevResponse) => ({ + ...prevResponse, + networkDns: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + histogram: response.histogram ?? prevResponse.histogram, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_DNS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_DNS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_DNS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -208,6 +201,10 @@ export const useNetworkDns = ({ useEffect(() => { networkDnsSearch(networkDnsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkDnsRequest, networkDnsSearch]); return [loading, networkDnsResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx index 9b7d768965556..d1ff9da1fa6c2 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { ESTermQuery } from '../../../../common/typed_json'; import { inputsModel } from '../../../common/store'; @@ -25,7 +26,6 @@ import { SortField, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import * as i18n from './translations'; import { InspectResponse } from '../../../types'; import { getInspectResponse } from '../../../helpers'; @@ -70,6 +70,7 @@ export const useNetworkHttp = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [networkHttpRequest, setHostRequest] = useState(null); @@ -114,12 +115,11 @@ export const useNetworkHttp = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -127,44 +127,37 @@ export const useNetworkHttp = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkHttpResponse((prevResponse) => ({ - ...prevResponse, - networkHttp: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkHttpResponse((prevResponse) => ({ + ...prevResponse, + networkHttp: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_HTTP); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_HTTP, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_HTTP, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -194,6 +187,10 @@ export const useNetworkHttp = ({ useEffect(() => { networkHttpSearch(networkHttpRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkHttpRequest, networkHttpSearch]); return [loading, networkHttpResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx index c06eafa725279..405957d98055e 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { ESTermQuery } from '../../../../common/typed_json'; import { inputsModel } from '../../../common/store'; @@ -25,7 +26,6 @@ import { PageInfoPaginated, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; import * as i18n from './translations'; @@ -71,6 +71,7 @@ export const useNetworkTopCountries = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const queryId = useMemo(() => `${ID}-${flowTarget}`, [flowTarget]); @@ -122,12 +123,11 @@ export const useNetworkTopCountries = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -135,44 +135,37 @@ export const useNetworkTopCountries = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkTopCountriesResponse((prevResponse) => ({ - ...prevResponse, - networkTopCountries: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkTopCountriesResponse((prevResponse) => ({ + ...prevResponse, + networkTopCountries: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_TOP_COUNTRIES); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_TOP_COUNTRIES, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_TOP_COUNTRIES, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -203,6 +196,10 @@ export const useNetworkTopCountries = ({ useEffect(() => { networkTopCountriesSearch(networkTopCountriesRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkTopCountriesRequest, networkTopCountriesSearch]); return [loading, networkTopCountriesResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx index 6fec8da8df998..9c6a4b3d1147f 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { ESTermQuery } from '../../../../common/typed_json'; import { inputsModel } from '../../../common/store'; @@ -25,7 +26,6 @@ import { PageInfoPaginated, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; import * as i18n from './translations'; @@ -71,6 +71,7 @@ export const useNetworkTopNFlow = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ @@ -118,12 +119,11 @@ export const useNetworkTopNFlow = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -131,44 +131,37 @@ export const useNetworkTopNFlow = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkTopNFlowResponse((prevResponse) => ({ - ...prevResponse, - networkTopNFlow: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkTopNFlowResponse((prevResponse) => ({ + ...prevResponse, + networkTopNFlow: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_TOP_N_FLOW); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_TOP_N_FLOW, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_TOP_N_FLOW, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -199,6 +192,10 @@ export const useNetworkTopNFlow = ({ useEffect(() => { networkTopNFlowSearch(networkTopNFlowRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkTopNFlowRequest, networkTopNFlowSearch]); return [loading, networkTopNFlowResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx b/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx index 631cdd9abf192..77f6d4575d8f7 100644 --- a/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { ESTermQuery } from '../../../../common/typed_json'; import { inputsModel } from '../../../common/store'; @@ -23,7 +24,6 @@ import { NetworkTlsStrategyResponse, } from '../../../../common/search_strategy/security_solution/network'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import * as i18n from './translations'; import { getInspectResponse } from '../../../helpers'; @@ -71,6 +71,7 @@ export const useNetworkTls = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [networkTlsRequest, setHostRequest] = useState(null); @@ -115,12 +116,11 @@ export const useNetworkTls = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -128,41 +128,38 @@ export const useNetworkTls = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkTlsResponse((prevResponse) => ({ - ...prevResponse, - tls: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkTlsResponse((prevResponse) => ({ + ...prevResponse, + tls: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_TLS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ title: i18n.FAIL_NETWORK_TLS, text: msg.message }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_TLS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -193,6 +190,10 @@ export const useNetworkTls = ({ useEffect(() => { networkTlsSearch(networkTlsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkTlsRequest, networkTlsSearch]); return [loading, networkTlsResponse]; diff --git a/x-pack/plugins/security_solution/public/network/containers/users/index.tsx b/x-pack/plugins/security_solution/public/network/containers/users/index.tsx index 648d05e8392c3..515ef3b8644ab 100644 --- a/x-pack/plugins/security_solution/public/network/containers/users/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/users/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { ESTermQuery } from '../../../../common/typed_json'; @@ -25,7 +26,6 @@ import { NetworkUsersStrategyResponse, } from '../../../../common/search_strategy/security_solution/network'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import * as i18n from './translations'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; @@ -68,6 +68,7 @@ export const useNetworkUsers = ({ const { data, notifications, uiSettings } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); @@ -115,12 +116,11 @@ export const useNetworkUsers = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -128,44 +128,37 @@ export const useNetworkUsers = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkUsersResponse((prevResponse) => ({ - ...prevResponse, - networkUsers: response.edges, - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - refetch: refetch.current, - totalCount: response.totalCount, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkUsersResponse((prevResponse) => ({ + ...prevResponse, + networkUsers: response.edges, + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + refetch: refetch.current, + totalCount: response.totalCount, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_USERS); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_USERS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_USERS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -196,6 +189,10 @@ export const useNetworkUsers = ({ useEffect(() => { networkUsersSearch(networkUsersRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [networkUsersRequest, networkUsersSearch]); return [loading, networkUsersResponse]; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx index 94796b06d2f12..8b17a7288eae3 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { HostsQueries, @@ -19,7 +20,6 @@ import { inputsModel } from '../../../common/store/inputs'; import { createFilter } from '../../../common/containers/helpers'; import { ESQuery } from '../../../../common/typed_json'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; import * as i18n from './translations'; @@ -52,6 +52,7 @@ export const useHostOverview = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [overviewHostRequest, setHostRequest] = useState(null); @@ -72,12 +73,11 @@ export const useHostOverview = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -85,42 +85,35 @@ export const useHostOverview = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setHostOverviewResponse((prevResponse) => ({ - ...prevResponse, - overviewHost: response.overviewHost, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setHostOverviewResponse((prevResponse) => ({ + ...prevResponse, + overviewHost: response.overviewHost, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_HOST_OVERVIEW); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_HOST_OVERVIEW, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_HOST_OVERVIEW, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -147,6 +140,10 @@ export const useHostOverview = ({ useEffect(() => { overviewHostSearch(overviewHostRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [overviewHostRequest, overviewHostSearch]); return [loading, overviewHostResponse]; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx index c35800bf23843..cf0774a02db3b 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { NetworkQueries, @@ -19,7 +20,6 @@ import { inputsModel } from '../../../common/store/inputs'; import { createFilter } from '../../../common/containers/helpers'; import { ESQuery } from '../../../../common/typed_json'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; import * as i18n from './translations'; @@ -52,6 +52,7 @@ export const useNetworkOverview = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ overviewNetworkRequest, @@ -75,12 +76,11 @@ export const useNetworkOverview = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -88,42 +88,35 @@ export const useNetworkOverview = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setNetworkOverviewResponse((prevResponse) => ({ - ...prevResponse, - overviewNetwork: response.overviewNetwork, - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - })); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setNetworkOverviewResponse((prevResponse) => ({ + ...prevResponse, + overviewNetwork: response.overviewNetwork, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning(i18n.ERROR_NETWORK_OVERVIEW); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger({ - title: i18n.FAIL_NETWORK_OVERVIEW, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_NETWORK_OVERVIEW, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts] ); @@ -150,6 +143,10 @@ export const useNetworkOverview = ({ useEffect(() => { overviewNetworkSearch(overviewNetworkRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [overviewNetworkRequest, overviewNetworkSearch]); return [loading, overviewNetworkResponse]; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx index 2dbdad2065f20..7e4924eacda4b 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx @@ -8,6 +8,7 @@ import { isEmpty, noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; @@ -19,7 +20,6 @@ import { TimelineEventsDetailsStrategyResponse, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/public'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; export interface EventsArgs { detailsData: TimelineEventsDetailsItem[] | null; } @@ -40,6 +40,7 @@ export const useTimelineEventsDetails = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [ timelineDetailsRequest, @@ -56,12 +57,11 @@ export const useTimelineEventsDetails = ({ return; } - let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search( request, { @@ -72,37 +72,27 @@ export const useTimelineEventsDetails = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - setTimelineDetailsResponse(response.data || []); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setTimelineDetailsResponse(response.data || []); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); - } + setLoading(false); // TODO: Make response error status clearer notifications.toasts.addWarning('An error has occurred'); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!didCancel) { - setLoading(false); - } - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger('Failed to run search'); - } + setLoading(false); + notifications.toasts.addDanger({ title: 'Failed to run search', text: msg.message }); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, notifications.toasts, skip] ); @@ -125,6 +115,10 @@ export const useTimelineEventsDetails = ({ useEffect(() => { timelineDetailsSearch(timelineDetailsRequest); + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [timelineDetailsRequest, timelineDetailsSearch]); return [loading, timelineDetailsResponse]; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index e8db51a8e9e02..38fa81a4fb7c2 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -9,6 +9,7 @@ import deepEqual from 'fast-deep-equal'; import { isEmpty, noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; +import { Subscription } from 'rxjs'; import { ESQuery } from '../../../common/typed_json'; import { isCompleteResponse, isErrorResponse } from '../../../../../../src/plugins/data/public'; @@ -117,6 +118,7 @@ export const useTimelineEvents = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [activePage, setActivePage] = useState( id === TimelineId.active ? activeTimeline.getActivePage() : 0 @@ -176,12 +178,12 @@ export const useTimelineEvents = ({ if (request == null || pageName === '' || skip) { return; } - let didCancel = false; + const asyncSearch = async () => { prevTimelineRequest.current = request; abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search, TimelineResponse>(request, { strategy: request.language === 'eql' @@ -191,53 +193,44 @@ export const useTimelineEvents = ({ }) .subscribe({ next: (response) => { - try { - if (isCompleteResponse(response)) { - if (!didCancel) { - setLoading(false); - - setTimelineResponse((prevResponse) => { - const newTimelineResponse = { - ...prevResponse, - events: getTimelineEvents(response.edges), - inspect: getInspectResponse(response, prevResponse.inspect), - pageInfo: response.pageInfo, - totalCount: response.totalCount, - updatedAt: Date.now(), - }; - if (id === TimelineId.active) { - activeTimeline.setExpandedDetail({}); - activeTimeline.setPageName(pageName); - if (request.language === 'eql') { - activeTimeline.setEqlRequest(request as TimelineEqlRequestOptions); - activeTimeline.setEqlResponse(newTimelineResponse); - } else { - activeTimeline.setRequest(request); - activeTimeline.setResponse(newTimelineResponse); - } - } - return newTimelineResponse; - }); - } - searchSubscription$.unsubscribe(); - } else if (isErrorResponse(response)) { - if (!didCancel) { - setLoading(false); + if (isCompleteResponse(response)) { + setLoading(false); + setTimelineResponse((prevResponse) => { + const newTimelineResponse = { + ...prevResponse, + events: getTimelineEvents(response.edges), + inspect: getInspectResponse(response, prevResponse.inspect), + pageInfo: response.pageInfo, + totalCount: response.totalCount, + updatedAt: Date.now(), + }; + if (id === TimelineId.active) { + activeTimeline.setExpandedDetail({}); + activeTimeline.setPageName(pageName); + if (request.language === 'eql') { + activeTimeline.setEqlRequest(request as TimelineEqlRequestOptions); + activeTimeline.setEqlResponse(newTimelineResponse); + } else { + activeTimeline.setRequest(request); + activeTimeline.setResponse(newTimelineResponse); + } } - notifications.toasts.addWarning(i18n.ERROR_TIMELINE_EVENTS); - searchSubscription$.unsubscribe(); - } - } catch { + return newTimelineResponse; + }); + searchSubscription$.current.unsubscribe(); + } else if (isErrorResponse(response)) { + setLoading(false); notifications.toasts.addWarning(i18n.ERROR_TIMELINE_EVENTS); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (msg.message !== 'Aborted') { - notifications.toasts.addDanger({ - title: i18n.FAIL_TIMELINE_EVENTS, - text: msg.message, - }); - } + setLoading(false); + notifications.toasts.addDanger({ + title: i18n.FAIL_TIMELINE_EVENTS, + text: msg.message, + }); + searchSubscription$.current.unsubscribe(); }, }); }; @@ -280,14 +273,10 @@ export const useTimelineEvents = ({ } } + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; - - return () => { - didCancel = true; - abortCtrl.current.abort(); - }; }, [data.search, id, notifications.toasts, pageName, refetchGrid, skip, wrappedLoadPage] ); @@ -400,6 +389,10 @@ export const useTimelineEvents = ({ ) { timelineSearch(timelineRequest); } + return () => { + searchSubscription$.current.unsubscribe(); + abortCtrl.current.abort(); + }; }, [id, timelineRequest, timelineSearch, timerangeKind]); /* diff --git a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx index 3b0d8756e0a6b..cf5f44a65ab96 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; +import { Subscription } from 'rxjs'; import { inputsModel } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; @@ -20,7 +21,6 @@ import { } from '../../../../common/search_strategy'; import { ESQuery } from '../../../../common/typed_json'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/public'; -import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; export interface UseTimelineKpiProps { timerange: TimerangeInput; @@ -40,7 +40,7 @@ export const useTimelineKpis = ({ const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const didCancel = useRef(false); + const searchSubscription$ = useRef(new Subscription()); const [loading, setLoading] = useState(false); const [timelineKpiRequest, setTimelineKpiRequest] = useState( null @@ -54,12 +54,11 @@ export const useTimelineKpis = ({ if (request == null) { return; } - didCancel.current = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); - const searchSubscription$ = data.search + searchSubscription$.current = data.search .search(request, { strategy: 'securitySolutionTimelineSearchStrategy', abortSignal: abortCtrl.current.signal, @@ -67,29 +66,23 @@ export const useTimelineKpis = ({ .subscribe({ next: (response) => { if (isCompleteResponse(response)) { - if (!didCancel.current) { - setLoading(false); - setTimelineKpiResponse(response); - } - searchSubscription$.unsubscribe(); + setLoading(false); + setTimelineKpiResponse(response); + searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { - if (!didCancel.current) { - setLoading(false); - } + setLoading(false); notifications.toasts.addWarning('An error has occurred'); - searchSubscription$.unsubscribe(); + searchSubscription$.current.unsubscribe(); } }, error: (msg) => { - if (!didCancel.current) { - setLoading(false); - } - if (!(msg instanceof AbortError)) { - notifications.toasts.addDanger('Failed to load KPIs'); - } + setLoading(false); + notifications.toasts.addDanger('Failed to load KPIs'); + searchSubscription$.current.unsubscribe(); }, }); }; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); asyncSearch(); refetch.current = asyncSearch; @@ -122,7 +115,7 @@ export const useTimelineKpis = ({ setTimelineKpiResponse(null); } return () => { - didCancel.current = true; + searchSubscription$.current.unsubscribe(); abortCtrl.current.abort(); }; }, [isBlankTimeline, timelineKpiRequest, timelineKpiSearch]); From 3c5b91f5d66ca4a785319f8da80d08c94ea06e45 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Mon, 22 Feb 2021 21:58:45 -0500 Subject: [PATCH 07/70] [APM] Fix stale correlations chart data for selected term (#91867) (#92109) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../app/correlations/correlations_table.tsx | 7 +++- .../app/correlations/error_correlations.tsx | 34 ++++++++++++++----- .../app/correlations/latency_correlations.tsx | 34 ++++++++++++++----- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx b/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx index c75c1fb6d96a6..17559755b12e1 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx @@ -31,11 +31,16 @@ type SignificantTerm = NonNullable< NonNullable['significantTerms'] >[0]; +export type SelectedSignificantTerm = Pick< + SignificantTerm, + 'fieldName' | 'fieldValue' +>; + interface Props { significantTerms?: T[]; status: FETCH_STATUS; percentageColumnName: string; - setSelectedSignificantTerm: (term: T | null) => void; + setSelectedSignificantTerm: (term: SelectedSignificantTerm | null) => void; onFilter: () => void; } diff --git a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx index 9b80ee6fc31b8..69f03f56b4c72 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx @@ -23,7 +23,10 @@ import { useUrlParams } from '../../../context/url_params_context/use_url_params import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; import { px } from '../../../style/variables'; -import { CorrelationsTable } from './correlations_table'; +import { + CorrelationsTable, + SelectedSignificantTerm, +} from './correlations_table'; import { ChartContainer } from '../../shared/charts/chart_container'; import { useTheme } from '../../../hooks/use_theme'; import { CustomFields } from './custom_fields'; @@ -35,10 +38,6 @@ type CorrelationsApiResponse = NonNullable< APIReturnType<'GET /api/apm/correlations/failed_transactions'> >; -type SignificantTerm = NonNullable< - CorrelationsApiResponse['significantTerms'] ->[0]; - interface Props { onClose: () => void; } @@ -47,7 +46,7 @@ export function ErrorCorrelations({ onClose }: Props) { const [ selectedSignificantTerm, setSelectedSignificantTerm, - ] = useState(null); + ] = useState(null); const { serviceName } = useParams<{ serviceName?: string }>(); const { urlParams, uiFilters } = useUrlParams(); @@ -148,13 +147,30 @@ export function ErrorCorrelations({ onClose }: Props) { ); } +function getSelectedTimeseries( + data: CorrelationsApiResponse, + selectedSignificantTerm: SelectedSignificantTerm +) { + const { significantTerms } = data; + if (!significantTerms) { + return []; + } + return ( + significantTerms.find( + ({ fieldName, fieldValue }) => + selectedSignificantTerm.fieldName === fieldName && + selectedSignificantTerm.fieldValue === fieldValue + )?.timeseries || [] + ); +} + function ErrorTimeseriesChart({ data, selectedSignificantTerm, status, }: { data?: CorrelationsApiResponse; - selectedSignificantTerm: SignificantTerm | null; + selectedSignificantTerm: SelectedSignificantTerm | null; status: FETCH_STATUS; }) { const theme = useTheme(); @@ -191,7 +207,7 @@ function ErrorTimeseriesChart({ curve={CurveType.CURVE_MONOTONE_X} /> - {selectedSignificantTerm !== null ? ( + {data && selectedSignificantTerm ? ( ) : null} diff --git a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx index 459df99a62f5a..ce88da64dabc4 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx @@ -21,7 +21,10 @@ import { getDurationFormatter } from '../../../../common/utils/formatters'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; -import { CorrelationsTable } from './correlations_table'; +import { + CorrelationsTable, + SelectedSignificantTerm, +} from './correlations_table'; import { ChartContainer } from '../../shared/charts/chart_container'; import { useTheme } from '../../../hooks/use_theme'; import { CustomFields, PercentileOption } from './custom_fields'; @@ -33,10 +36,6 @@ type CorrelationsApiResponse = NonNullable< APIReturnType<'GET /api/apm/correlations/slow_transactions'> >; -type SignificantTerm = NonNullable< - CorrelationsApiResponse['significantTerms'] ->[0]; - interface Props { onClose: () => void; } @@ -45,7 +44,7 @@ export function LatencyCorrelations({ onClose }: Props) { const [ selectedSignificantTerm, setSelectedSignificantTerm, - ] = useState(null); + ] = useState(null); const { serviceName } = useParams<{ serviceName?: string }>(); const { urlParams, uiFilters } = useUrlParams(); @@ -178,13 +177,30 @@ function getDistributionYMax(data?: CorrelationsApiResponse) { return Math.max(...yValues); } +function getSelectedDistribution( + data: CorrelationsApiResponse, + selectedSignificantTerm: SelectedSignificantTerm +) { + const { significantTerms } = data; + if (!significantTerms) { + return []; + } + return ( + significantTerms.find( + ({ fieldName, fieldValue }) => + selectedSignificantTerm.fieldName === fieldName && + selectedSignificantTerm.fieldValue === fieldValue + )?.distribution || [] + ); +} + function LatencyDistributionChart({ data, selectedSignificantTerm, status, }: { data?: CorrelationsApiResponse; - selectedSignificantTerm: SignificantTerm | null; + selectedSignificantTerm: SelectedSignificantTerm | null; status: FETCH_STATUS; }) { const theme = useTheme(); @@ -238,7 +254,7 @@ function LatencyDistributionChart({ tickFormat={(d) => `${roundFloat(d)}%`} /> - {selectedSignificantTerm !== null ? ( + {data && selectedSignificantTerm ? ( `${roundFloat(d)}%`} /> From 84e16ebcd025411ba2dced85f067ba41166e8d7b Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 22 Feb 2021 21:00:08 -0600 Subject: [PATCH 08/70] Pluralize "alerts" (#92261) --- docs/user/alerting/defining-alerts.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc index acfb26d2e11b3..d0a6b7f1c2c03 100644 --- a/docs/user/alerting/defining-alerts.asciidoc +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -19,7 +19,7 @@ image::images/alert-flyout-sections.png[The three sections of an alert definitio [[defining-alerts-general-details]] === General alert details -All alert share the following four properties in common: +All alerts share the following four properties in common: [role="screenshot"] image::images/alert-flyout-general-details.png[alt='All alerts have name, tags, check every, and notify properties in common'] From 6ad09a856a081f308886d5f78e11f923632d0a6c Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 22 Feb 2021 21:00:45 -0600 Subject: [PATCH 09/70] Change "whitelist" to "allowlist" on alerting getting started (#92260) --- docs/user/alerting/alerting-getting-started.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index 6186fce8a51c4..5bd54ae93be69 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -201,6 +201,6 @@ If an alert requires certain privileges to run such as index privileges, keep in [[alerting-restricting-actions]] === Restricting actions -For security reasons you may wish to limit the extent to which {kib} can connect to external services. <> allows you to disable certain <> and whitelist the hostnames that {kib} can connect with. +For security reasons you may wish to limit the extent to which {kib} can connect to external services. <> allows you to disable certain <> and allowlist the hostnames that {kib} can connect with. -- From 95516e65063e26d7af8506c6f91518f0401cc3d2 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Mon, 22 Feb 2021 22:11:26 -0500 Subject: [PATCH 10/70] adjust waterfall chart sidebar CSS to appropriately display border (#92191) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../monitor/synthetics/waterfall/components/styles.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts index c0a75e0e09b22..433f59d0e83af 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts @@ -9,7 +9,7 @@ import { FunctionComponent } from 'react'; import { StyledComponent } from 'styled-components'; import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, EuiPanelProps } from '@elastic/eui'; import { rgba } from 'polished'; -import { FIXED_AXIS_HEIGHT, SIDEBAR_GROW_SIZE } from './constants'; +import { FIXED_AXIS_HEIGHT } from './constants'; import { euiStyled, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common'; interface WaterfallChartOuterContainerProps { @@ -49,7 +49,7 @@ export const WaterfallChartFixedTopContainer = euiStyled(StyledScrollDiv)` `; export const WaterfallChartAxisOnlyContainer = euiStyled(EuiFlexItem)` - margin-left: -22px; + margin-left: -16px; `; export const WaterfallChartTopContainer = euiStyled(EuiFlexGroup)` @@ -83,9 +83,9 @@ interface WaterfallChartSidebarContainer { } export const WaterfallChartSidebarWrapper = euiStyled(EuiFlexItem)` - max-width: ${SIDEBAR_GROW_SIZE * 10}%; z-index: ${(props) => props.theme.eui.euiZLevel5}; -`; + min-width: 0; +`; // NOTE: min-width: 0 ensures flexbox and no-wrap children can co-exist export const WaterfallChartSidebarContainer = euiStyled.div` height: ${(props) => `${props.height}px`}; From 8988bdf78e83e6b143a78994a5edbafd93ba6e9e Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 23 Feb 2021 08:04:29 +0100 Subject: [PATCH 11/70] Some package.json cleanup (#92136) * some package.json cleanup * update lockfile --- package.json | 5 ++--- yarn.lock | 38 -------------------------------------- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index c68a8ae34eb81..833246f92ddc5 100644 --- a/package.json +++ b/package.json @@ -170,7 +170,6 @@ "brace": "0.11.1", "chalk": "^4.1.0", "check-disk-space": "^2.1.0", - "cheerio": "0.22.0", "chokidar": "^3.4.3", "chroma-js": "^1.4.1", "classnames": "2.2.6", @@ -280,7 +279,6 @@ "react-intl": "^2.8.0", "react-is": "^16.8.0", "react-moment-proptypes": "^1.7.0", - "react-portal": "^3.2.0", "react-redux": "^7.2.0", "react-resizable": "^1.7.5", "react-router": "^5.2.0", @@ -318,7 +316,6 @@ "utility-types": "^3.10.0", "uuid": "3.3.2", "vinyl": "^2.2.0", - "vscode-languageserver": "^5.2.1", "vt-pbf": "^3.1.1", "wellknown": "^0.5.0", "whatwg-fetch": "^3.0.0", @@ -386,6 +383,7 @@ "@octokit/rest": "^16.35.0", "@percy/agent": "^0.28.6", "@scant/router": "^0.1.0", + "@storybook/addons": "^6.0.16", "@storybook/addon-a11y": "^6.0.26", "@storybook/addon-actions": "^6.0.26", "@storybook/addon-docs": "^6.0.26", @@ -598,6 +596,7 @@ "callsites": "^3.1.0", "chai": "3.5.0", "chance": "1.0.18", + "cheerio": "0.22.0", "chromedriver": "^88.0.0", "clean-webpack-plugin": "^3.0.0", "cmd-shim": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index ee18a37a0e5e1..09b50a6740a39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24282,13 +24282,6 @@ react-popper@^1.3.7: typed-styles "^0.0.7" warning "^4.0.2" -react-portal@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-3.2.0.tgz#4224e19b2b05d5cbe730a7ba0e34ec7585de0043" - integrity sha512-avb1FreAZAVCvNNyS2dCpxZiPYPJnAasHYPxdVBTROgNFeI+KSb+OoMHNsC1GbDawESCriPwCX+qKua6WSPIFw== - dependencies: - prop-types "^15.5.8" - react-redux@^7.1.0, react-redux@^7.1.1, react-redux@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" @@ -30022,37 +30015,6 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== -vscode-jsonrpc@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" - integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== - -vscode-languageserver-protocol@3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" - integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== - dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" - -vscode-languageserver-types@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== - -vscode-languageserver@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz#0d2feddd33f92aadf5da32450df498d52f6f14eb" - integrity sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A== - dependencies: - vscode-languageserver-protocol "3.14.1" - vscode-uri "^1.0.6" - -vscode-uri@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" - integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== - vt-pbf@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.1.tgz#b0f627e39a10ce91d943b898ed2363d21899fb82" From da7aac3bec9d368ea06834a2dabc498759747421 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 23 Feb 2021 10:41:41 +0300 Subject: [PATCH 12/70] Prevent rewrite of date_histogram interval (#91408) * Prevent rewrite of date_histogram interval Closes:#91408 * fix PR comments * fix PR comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../lib/time_buckets/calc_es_interval.ts | 8 ++- .../buckets/lib/time_buckets/time_buckets.ts | 66 +++++++++---------- .../parse_interval.test.ts | 38 +++++++++-- .../date_interval_utils/parse_interval.ts | 30 +++++---- 4 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts index 755a08ed0a1d9..72d59af037d90 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts @@ -28,12 +28,18 @@ export interface EsInterval { * @param {moment.duration} duration * @return {object} */ -export function convertDurationToNormalizedEsInterval(duration: moment.Duration): EsInterval { +export function convertDurationToNormalizedEsInterval( + duration: moment.Duration, + targetUnit?: Unit +): EsInterval { for (let i = 0; i < unitsDesc.length; i++) { const unit = unitsDesc[i]; const val = duration.as(unit); // find a unit that rounds neatly if (val >= 1 && Math.floor(val) === val) { + if (val === 1 && targetUnit && unit !== targetUnit) { + continue; + } // if the unit is "large", like years, but // isn't set to 1 ES will puke. So keep going until // we get out of the "large" units diff --git a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts index c08c76891e1fd..ac58cea60a6ef 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts @@ -5,11 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import { Assign } from 'utility-types'; import { isString, isObject as isObjectLodash, isPlainObject, sortBy } from 'lodash'; import moment, { Moment } from 'moment'; -import { parseInterval } from '../../../utils'; +import { Unit } from '@elastic/datemath'; +import { parseInterval, splitStringInterval } from '../../../utils'; import { TimeRangeBounds } from '../../../../../query'; import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval'; import { @@ -38,6 +39,10 @@ function isValidMoment(m: any): boolean { return m && 'isValid' in m && m.isValid(); } +function isDurationInterval(i: any): i is moment.Duration { + return moment.isDuration(i) && Boolean(+i); +} + export interface TimeBucketsConfig extends Record { 'histogram:maxBars': number; 'histogram:barTarget': number; @@ -172,38 +177,26 @@ export class TimeBuckets { * @param {object|string|moment.duration} input - see desc */ setInterval(input: null | string | Record) { - let interval = input; + this._originalInterval = null; + this._i = isObject(input) ? input.val : input; - // selection object -> val - if (isObject(input) && !moment.isDuration(input)) { - interval = input.val; - } - - if (!interval || interval === autoInterval) { + if (!this._i || this._i === autoInterval) { this._i = autoInterval; return; } - if (isString(interval)) { - input = interval; - - // Preserve the original units because they're lost when the interval is converted to a - // moment duration object. - this._originalInterval = input; + if (isString(this._i)) { + const parsedInterval = parseInterval(this._i); - interval = parseInterval(interval); - if (interval === null || +interval === 0) { - interval = null; + if (isDurationInterval(parsedInterval)) { + this._originalInterval = this._i; + this._i = parsedInterval; } } - // if the value wasn't converted to a duration, and isn't - // already a duration, we have a problem - if (!moment.isDuration(interval)) { - throw new TypeError('"' + input + '" is not a valid interval.'); + if (!isDurationInterval(this._i)) { + throw new TypeError('"' + this._i + '" is not a valid interval.'); } - - this._i = interval; } /** @@ -235,15 +228,9 @@ export class TimeBuckets { */ getInterval(useNormalizedEsInterval = true): TimeBucketsInterval { const duration = this.getDuration(); - - // either pull the interval from state or calculate the auto-interval - const readInterval = () => { - const interval = this._i; - if (moment.isDuration(interval)) return interval; - return calcAutoIntervalNear(this._timeBucketConfig['histogram:barTarget'], Number(duration)); - }; - - const parsedInterval = readInterval(); + const parsedInterval = isDurationInterval(this._i) + ? this._i + : calcAutoIntervalNear(this._timeBucketConfig['histogram:barTarget'], Number(duration)); // check to see if the interval should be scaled, and scale it if so const maybeScaleInterval = (interval: moment.Duration) => { @@ -271,10 +258,19 @@ export class TimeBuckets { }; // append some TimeBuckets specific props to the interval - const decorateInterval = (interval: moment.Duration): TimeBucketsInterval => { + const decorateInterval = ( + interval: Assign + ): TimeBucketsInterval => { + let originalUnit: Unit | undefined; + + if (!interval.scaled && this._originalInterval) { + originalUnit = splitStringInterval(this._originalInterval!)?.unit; + } + const esInterval = useNormalizedEsInterval - ? convertDurationToNormalizedEsInterval(interval) + ? convertDurationToNormalizedEsInterval(interval, originalUnit) : convertIntervalToEsInterval(String(this._originalInterval)); + const prettyUnits = moment.normalizeUnits(esInterval.unit); return Object.assign(interval, { diff --git a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.test.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.test.ts index dd45eb18f3de6..16439e0820776 100644 --- a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.test.ts +++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.test.ts @@ -7,17 +7,41 @@ */ import { Duration, unitOfTime } from 'moment'; -import { parseInterval } from './parse_interval'; +import { parseInterval, splitStringInterval } from './parse_interval'; + +describe('splitStringInterval', () => { + test('should correctly split 1d interval', () => { + expect(splitStringInterval('1d')).toMatchInlineSnapshot(` + Object { + "unit": "d", + "value": 1, + } + `); + }); -const validateDuration = (duration: Duration | null, unit: unitOfTime.Base, value: number) => { - expect(duration).toBeDefined(); + test('should correctly split 34m interval', () => { + expect(splitStringInterval('1d')).toMatchInlineSnapshot(` + Object { + "unit": "d", + "value": 1, + } + `); + }); - if (duration) { - expect(duration.as(unit)).toBe(value); - } -}; + test('should return null if cannot parse interval', () => { + expect(splitStringInterval('wrong_value_1')).toMatchInlineSnapshot(`null`); + }); +}); describe('parseInterval', () => { + const validateDuration = (duration: Duration | null, unit: unitOfTime.Base, value: number) => { + expect(duration).toBeDefined(); + + if (duration) { + expect(duration.as(unit)).toBe(value); + } + }; + describe('integer', () => { test('should correctly parse 1d interval', () => { validateDuration(parseInterval('1d'), 'd', 1); diff --git a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts index cf51219ab916c..e6ce43526306d 100644 --- a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts +++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts @@ -7,21 +7,32 @@ */ import { find } from 'lodash'; -import moment, { unitOfTime } from 'moment'; -import dateMath from '@elastic/datemath'; +import moment from 'moment'; +import dateMath, { Unit } from '@elastic/datemath'; // Assume interval is in the form (value)(unit), such as "1h" const INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + dateMath.units.join('|') + ')$'); +export const splitStringInterval = (interval: string) => { + if (interval) { + const matches = interval.toString().trim().match(INTERVAL_STRING_RE); + if (matches) { + return { + value: parseFloat(matches[1]) || 1, + unit: matches[2] as Unit, + }; + } + } + return null; +}; + export function parseInterval(interval: string): moment.Duration | null { - const matches = String(interval).trim().match(INTERVAL_STRING_RE); + const parsedInterval = splitStringInterval(interval); - if (!matches) return null; + if (!parsedInterval) return null; try { - const value = parseFloat(matches[1]) || 1; - const unit = matches[2] as unitOfTime.Base; - + const { value, unit } = parsedInterval; const duration = moment.duration(value, unit); // There is an error with moment, where if you have a fractional interval between 0 and 1, then when you add that @@ -31,10 +42,7 @@ export function parseInterval(interval: string): moment.Duration | null { // adding 0.5 days until we hit the end date. However, since there is a bug in moment, when you add 0.5 days to // the start date, you get the same exact date (instead of being ahead by 12 hours). So instead of returning // a duration corresponding to 0.5 hours, we return a duration corresponding to 12 hours. - const selectedUnit = find( - dateMath.units, - (u) => Math.abs(duration.as(u)) >= 1 - ) as unitOfTime.Base; + const selectedUnit = find(dateMath.units, (u) => Math.abs(duration.as(u)) >= 1) as Unit; // however if we do this fhe other way around it will also fail // go from 500m to hours as this will result in infinite number (dividing 500/60 = 8.3*) From 8e2bd8f69f57559bffd5c1758b1235cb2e8f2962 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 23 Feb 2021 08:56:56 +0100 Subject: [PATCH 13/70] Simplify anonymous access & embedding docs. (#90409) --- docs/settings/security-settings.asciidoc | 2 +- docs/setup/embedding.asciidoc | 55 ++++++++++++++++++ docs/setup/images/embed-kibana.png | Bin 0 -> 37243 bytes .../security/authentication/index.asciidoc | 16 ++--- docs/user/setup.asciidoc | 2 + 5 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 docs/setup/embedding.asciidoc create mode 100644 docs/setup/images/embed-kibana.png diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index 7ffb6b66f5a2b..bd714c62ff543 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -264,7 +264,7 @@ You can configure the following settings in the `kibana.yml` file. this to `true` if SSL is configured outside of {kib} (for example, you are routing requests through a load balancer or proxy). -| `xpack.security.sameSiteCookies` {ess-icon} +| [[xpack-security-sameSiteCookies]] `xpack.security.sameSiteCookies` {ess-icon} | Sets the `SameSite` attribute of the session cookie. This allows you to declare whether your cookie should be restricted to a first-party or same-site context. Valid values are `Strict`, `Lax`, `None`. This is *not set* by default, which modern browsers will treat as `Lax`. If you use Kibana embedded in an iframe in modern browsers, you might need to set it to `None`. Setting this value to `None` requires cookies to be sent over a secure connection by setting <>: `true`. diff --git a/docs/setup/embedding.asciidoc b/docs/setup/embedding.asciidoc new file mode 100644 index 0000000000000..1b20baf66913a --- /dev/null +++ b/docs/setup/embedding.asciidoc @@ -0,0 +1,55 @@ +[[embedding]] +== Embed {kib} content in a web page + +Once you create a dashboard or a visualization, you might want to share it with your colleagues or friends. The easiest way to do this is to share a direct link to your dashboard or visualization. However, some users might not have access to your {kib}. + +With the {kib} embedding functionality, you can display the content you created in {kib} to an internal company website or a personal web page. From *Dashboard* or *Visualize*, open the *Share > Embed code* menu, and then click *Copy iFrame code* to generate an HTML code snippet. You can embed this snippet in your web page, and then add analysis, images, and links to give more context to the object you're sharing. + +image::images/embed-kibana.png[Generate an HTML snippet to embed {kib}, align=center] + +NOTE: Embedding of any other part of {kib} is also generally possible, but you might need to craft the proper HTML code manually. + +[float] +[[embedding-security]] +=== Configure security + +Embedding content through iframes requires careful consideration to minimize security risks. By default, modern web browsers enforce the +https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy[same-origin policy] to restrict the behavior of framed pages. When +{stack-security-features} are enabled on your cluster, you must relax this constraint for cookies as described in <> for {kib} to function +in an iframe. Refer to https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe[iframe] and +https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite[SameSite cookies] for more information. + +[float] +==== Authentication +If you're embedding {kib} in a website that supports Single Sign-On with SAML, OpenID Connect, Kerberos, or PKI, it's highly advisable to configure {kib} as a part of the Single Sign-On setup. Operating in a single and properly configured security domain provides you with the most secure and seamless user experience. You can read more at <>. + +If you want users to access embedded {kib} by skipping the login step, and Single Sign-On isn't an option for you, consider configuring <>. It is already natively integrated into the workflow for embedding dashboards and visualizations. + +If you have multiple authentication providers enabled, and you want to automatically log in anonymous users when embedding anything other than dashboards and visualizations, then you will need to add the `auth_provider_hint=` query string parameter to the {kib} URL that you're embedding. + +For example, if you craft the iframe code to embed {kib}, it might look like this: + +```html + +``` + +To make this iframe leverage anonymous access automatically, you will need to modify a link to {kib} in the `src` iframe attribute to look like this: + +```html + +``` + +Note that the `auth_provider_hint` query string parameter goes *before* the hash URL fragment. + +[float] +[[embedding-cookies]] +==== Cookies + +Regardless of the authentication type that you're using for the embedded {kib}, you must make sure that the browsers can transmit session cookies to a {kib} server. The setting you need to be aware of is <>. To support modern browsers, you must set it to `None`: + +[source,yaml] +-- +xpack.security.sameSiteCookies: "None" +-- + +For more information about possible values and implications, go to <>. \ No newline at end of file diff --git a/docs/setup/images/embed-kibana.png b/docs/setup/images/embed-kibana.png new file mode 100644 index 0000000000000000000000000000000000000000..f3b0f542361fd31c59eb4d5fcaab03584a2937b8 GIT binary patch literal 37243 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 query string parameter to the {kib} URL that you're embedding. +If you have multiple authentication providers enabled, and you want to automatically log in anonymous users when embedding dashboards and visualizations, then toggle *Public URL* in the *Share > Embed code* menu in *Dashboard* or *Visualize*. -For example, if you generate the iframe code to embed {kib}, it will look like this: +You can also use the *Public URL* toggle when you're generating permanent links to dashboards, visualizations, and saved searches. -```html - -``` +NOTE: The *Public URL* toggle is only available if anonymous access is properly configured and your anonymous service account has privileges to access what you want to embed or share. -To make this iframe leverage anonymous access automatically, you will need to modify a link to {kib} in the `src` iframe attribute to look like this: - -```html - -``` - -Note that `auth_provider_hint` query string parameter goes *before* the hash URL fragment. +For more information, refer to <>. [[http-authentication]] ==== HTTP authentication diff --git a/docs/user/setup.asciidoc b/docs/user/setup.asciidoc index 54bdfff8e0bbb..ba848681689b6 100644 --- a/docs/user/setup.asciidoc +++ b/docs/user/setup.asciidoc @@ -59,3 +59,5 @@ include::{kib-repo-dir}/setup/connect-to-elasticsearch.asciidoc[] include::{kib-repo-dir}/setup/production.asciidoc[] include::{kib-repo-dir}/setup/upgrade.asciidoc[] + +include::{kib-repo-dir}/setup/embedding.asciidoc[] From 4ab027749401ccd0931d70f7879cb045caf3a8b1 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Tue, 23 Feb 2021 09:01:51 +0100 Subject: [PATCH 14/70] Add CODE_OF_CONDUCT.md (#87439) --- CODE_OF_CONDUCT.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000..c286a3152c459 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +303 See Other + +Location: https://www.elastic.co/community/codeofconduct From 89c103a8962d95b532ce3a7c71f0274b5b893e85 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Tue, 23 Feb 2021 10:31:48 +0100 Subject: [PATCH 15/70] [APM] Guard searches with range/additional queries (#92112) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__snapshots__/queries.test.ts.snap | 9 +++++ .../__fixtures__/versions_first_seen.json | 16 ++++----- .../get_derived_service_annotations.ts | 18 +++++----- .../lib/services/annotations/index.test.ts | 4 +-- .../get_services/get_legacy_data_status.ts | 12 ++++--- .../__snapshots__/queries.test.ts.snap | 34 +++++++++++++++++++ .../lib/transactions/breakdown/index.ts | 9 +++++ 7 files changed, 78 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index 3e68831ee7cba..0521ff7d9554d 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -34,6 +34,15 @@ Object { }, }, }, + Object { + "range": Object { + "@timestamp": Object { + "format": "epoch_millis", + "gte": 1528113600000, + "lte": 1528977600000, + }, + }, + }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json index c53b28c8bf594..8a277797825a4 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json +++ b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json @@ -9,16 +9,16 @@ }, "hits": { "total": { - "value": 10000, + "value": 1, "relation": "gte" }, "max_score": null, - "hits": [] - }, - "aggregations": { - "first_seen": { - "value": 1.5281138E12, - "value_as_string": "2018-06-04T12:00:00.000Z" - } + "hits": [ + { + "_source": { + "@timestamp": "2018-06-04T12:00:00.000Z" + } + } + ] } } diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts index 67aa9d7fcd870..25c42f403da2e 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isNumber } from 'lodash'; +import { isFiniteNumber } from '../../../../common/utils/is_finite_number'; import { ESFilter } from '../../../../../../typings/elasticsearch'; import { Annotation, AnnotationType } from '../../../../common/annotations'; import { @@ -85,25 +85,23 @@ export async function getDerivedServiceAnnotations({ ], }, body: { - size: 0, + size: 1, query: { bool: { filter: [...filter, { term: { [SERVICE_VERSION]: version } }], }, }, - aggs: { - first_seen: { - min: { - field: '@timestamp', - }, - }, + sort: { + '@timestamp': 'desc', }, }, }); - const firstSeen = response.aggregations?.first_seen.value; + const firstSeen = new Date( + response.hits.hits[0]._source['@timestamp'] + ).getTime(); - if (!isNumber(firstSeen)) { + if (!isFiniteNumber(firstSeen)) { throw new Error( 'First seen for version was unexpectedly undefined or null.' ); diff --git a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts index 206e04d49cc03..d86016ed9d505 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts @@ -111,13 +111,13 @@ describe('getServiceAnnotations', () => { { id: '8.0.0', text: '8.0.0', - '@timestamp': 1.5281138e12, + '@timestamp': new Date('2018-06-04T12:00:00.000Z').getTime(), type: 'version', }, { id: '7.5.0', text: '7.5.0', - '@timestamp': 1.5281138e12, + '@timestamp': new Date('2018-06-04T12:00:00.000Z').getTime(), type: 'version', }, ]); diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts index 87f3c0a5d1b38..a3adca0d306aa 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts @@ -5,15 +5,16 @@ * 2.0. */ +import { rangeQuery } from '../../../../common/utils/queries'; import { ProcessorEvent } from '../../../../common/processor_event'; import { OBSERVER_VERSION_MAJOR } from '../../../../common/elasticsearch_fieldnames'; -import { Setup } from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { withApmSpan } from '../../../utils/with_apm_span'; // returns true if 6.x data is found -export async function getLegacyDataStatus(setup: Setup) { +export async function getLegacyDataStatus(setup: Setup & SetupTimeRange) { return withApmSpan('get_legacy_data_status', async () => { - const { apmEventClient } = setup; + const { apmEventClient, start, end } = setup; const params = { terminateAfter: 1, @@ -24,7 +25,10 @@ export async function getLegacyDataStatus(setup: Setup) { size: 0, query: { bool: { - filter: [{ range: { [OBSERVER_VERSION_MAJOR]: { lt: 7 } } }], + filter: [ + { range: { [OBSERVER_VERSION_MAJOR]: { lt: 7 } } }, + ...rangeQuery(start, end), + ], }, }, }, diff --git a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap index 5d6a92a874111..62050563497e9 100644 --- a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap @@ -164,6 +164,23 @@ Object { "service.environment": "test", }, }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "span.self_time.sum.us", + }, + }, + Object { + "exists": Object { + "field": "transaction.breakdown.count", + }, + }, + ], + }, + }, ], }, }, @@ -298,6 +315,23 @@ Object { "service.environment": "test", }, }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "span.self_time.sum.us", + }, + }, + Object { + "exists": Object { + "field": "transaction.breakdown.count", + }, + }, + ], + }, + }, Object { "term": Object { "transaction.name": "baz", diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts index c3741184c807d..f1e202df312c2 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts @@ -87,6 +87,15 @@ export function getTransactionBreakdown({ ...rangeQuery(start, end), ...environmentQuery(environment), ...esFilter, + { + bool: { + should: [ + { exists: { field: SPAN_SELF_TIME_SUM } }, + { exists: { field: TRANSACTION_BREAKDOWN_COUNT } }, + ], + minimum_should_match: 1, + }, + }, ]; if (transactionName) { From 2ce344019a8679c36fa2e28ffeec135a3304e279 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 23 Feb 2021 10:37:56 +0100 Subject: [PATCH 16/70] Add text search mode in global kuery bar (#91814) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...a-public.querystringinputprops.icontype.md | 2 +- ...ublic.querystringinputprops.isclearable.md | 11 +++ ...ugins-data-public.querystringinputprops.md | 5 +- ...public.querystringinputprops.nonkqlmode.md | 11 +++ ...uerystringinputprops.nonkqlmodehelptext.md | 11 +++ ...na-plugin-plugins-data-public.searchbar.md | 4 +- src/plugins/data/public/public.api.md | 13 ++- .../language_switcher.test.tsx | 93 ++++++++++++++++++- .../query_string_input/language_switcher.tsx | 47 ++++++++-- .../query_string_input/query_bar_top_row.tsx | 11 +++ .../query_string_input/query_string_input.tsx | 31 ++++++- .../ui/search_bar/create_search_bar.tsx | 6 ++ .../data/public/ui/search_bar/search_bar.tsx | 12 +++ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 15 files changed, 236 insertions(+), 23 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md index 4b1c5b84557e7..3de186cf77514 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md @@ -7,5 +7,5 @@ Signature: ```typescript -iconType?: string; +iconType?: EuiIconProps['type']; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md new file mode 100644 index 0000000000000..738041c2d5750 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [isClearable](./kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md) + +## QueryStringInputProps.isClearable property + +Signature: + +```typescript +isClearable?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md index 90c604d131800..a3089cc5d4da9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md @@ -19,10 +19,13 @@ export interface QueryStringInputProps | [dataTestSubj](./kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md) | string | | | [disableAutoFocus](./kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md) | boolean | | | [disableLanguageSwitcher](./kibana-plugin-plugins-data-public.querystringinputprops.disablelanguageswitcher.md) | boolean | | -| [iconType](./kibana-plugin-plugins-data-public.querystringinputprops.icontype.md) | string | | +| [iconType](./kibana-plugin-plugins-data-public.querystringinputprops.icontype.md) | EuiIconProps['type'] | | | [indexPatterns](./kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md) | Array<IIndexPattern | string> | | +| [isClearable](./kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md) | boolean | | | [isInvalid](./kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md) | boolean | | | [languageSwitcherPopoverAnchorPosition](./kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md) | PopoverAnchorPosition | | +| [nonKqlMode](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md) | 'lucene' | 'text' | | +| [nonKqlModeHelpText](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md) | string | | | [onBlur](./kibana-plugin-plugins-data-public.querystringinputprops.onblur.md) | () => void | | | [onChange](./kibana-plugin-plugins-data-public.querystringinputprops.onchange.md) | (query: Query) => void | | | [onChangeQueryInputFocus](./kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md) | (isFocused: boolean) => void | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md new file mode 100644 index 0000000000000..809bf0bb56b28 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [nonKqlMode](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md) + +## QueryStringInputProps.nonKqlMode property + +Signature: + +```typescript +nonKqlMode?: 'lucene' | 'text'; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md new file mode 100644 index 0000000000000..8caf492bebeb1 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [nonKqlModeHelpText](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md) + +## QueryStringInputProps.nonKqlModeHelpText property + +Signature: + +```typescript +nonKqlModeHelpText?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md index 786ac4f9d61a9..193a2e5a24f3f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md @@ -7,7 +7,7 @@ Signature: ```typescript -SearchBar: React.ComponentClass, "query" | "isLoading" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { - WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; +SearchBar: React.ComponentClass, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "isClearable" | "refreshInterval" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { + WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; } ``` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 664f4e16fa6d7..63b766a82e8e6 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -35,6 +35,7 @@ import { EuiComboBoxProps } from '@elastic/eui'; import { EuiConfirmModalProps } from '@elastic/eui'; import { EuiFlyoutSize } from '@elastic/eui'; import { EuiGlobalToastListToast } from '@elastic/eui'; +import { EuiIconProps } from '@elastic/eui'; import { EventEmitter } from 'events'; import { ExecutionContext } from 'src/plugins/expressions/common'; import { ExpressionAstExpression } from 'src/plugins/expressions/common'; @@ -1998,14 +1999,20 @@ export interface QueryStringInputProps { // (undocumented) disableLanguageSwitcher?: boolean; // (undocumented) - iconType?: string; + iconType?: EuiIconProps['type']; // (undocumented) indexPatterns: Array; // (undocumented) + isClearable?: boolean; + // (undocumented) isInvalid?: boolean; // (undocumented) languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; // (undocumented) + nonKqlMode?: 'lucene' | 'text'; + // (undocumented) + nonKqlModeHelpText?: string; + // (undocumented) onBlur?: () => void; // (undocumented) onChange?: (query: Query) => void; @@ -2254,8 +2261,8 @@ export const SEARCH_SESSIONS_MANAGEMENT_ID = "search_sessions"; // Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SearchBar: React.ComponentClass, "query" | "isLoading" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { - WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; +export const SearchBar: React.ComponentClass, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "isClearable" | "refreshInterval" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { + WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; }; // Warning: (ae-forgotten-export) The symbol "SearchBarOwnProps" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx b/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx index cc048b5ed9cac..acbd48718d92e 100644 --- a/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx +++ b/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx @@ -7,15 +7,15 @@ */ import React from 'react'; -import { QueryLanguageSwitcher } from './language_switcher'; +import { QueryLanguageSwitcher, QueryLanguageSwitcherProps } from './language_switcher'; import { KibanaContextProvider } from 'src/plugins/kibana_react/public'; import { coreMock } from '../../../../../core/public/mocks'; import { mountWithIntl } from '@kbn/test/jest'; -import { EuiButtonEmpty, EuiPopover } from '@elastic/eui'; +import { EuiButtonEmpty, EuiIcon, EuiPopover } from '@elastic/eui'; const startMock = coreMock.createStart(); describe('LanguageSwitcher', () => { - function wrapInContext(testProps: any) { + function wrapInContext(testProps: QueryLanguageSwitcherProps) { const services = { uiSettings: startMock.uiSettings, docLinks: startMock.docLinks, @@ -55,4 +55,91 @@ describe('LanguageSwitcher', () => { expect(component.find(EuiPopover).prop('isOpen')).toBe(true); expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeTruthy(); }); + + it('should toggle off if language is text', () => { + const component = mountWithIntl( + wrapInContext({ + language: 'text', + onSelectLanguage: () => { + return; + }, + }) + ); + component.find(EuiButtonEmpty).simulate('click'); + expect(component.find(EuiPopover).prop('isOpen')).toBe(true); + expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeFalsy(); + }); + it('it set language on nonKql mode text', () => { + const onSelectLanguage = jest.fn(); + + const component = mountWithIntl( + wrapInContext({ + language: 'kuery', + nonKqlMode: 'text', + onSelectLanguage, + }) + ); + component.find(EuiButtonEmpty).simulate('click'); + expect(component.find(EuiPopover).prop('isOpen')).toBe(true); + expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeTruthy(); + + component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + + expect(onSelectLanguage).toHaveBeenCalledWith('text'); + }); + it('it set language on nonKql mode lucene', () => { + const onSelectLanguage = jest.fn(); + + const component = mountWithIntl( + wrapInContext({ + language: 'kuery', + nonKqlMode: 'lucene', + onSelectLanguage, + }) + ); + component.find(EuiButtonEmpty).simulate('click'); + component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + + expect(onSelectLanguage).toHaveBeenCalledWith('lucene'); + }); + + it('it set language on kuery mode with nonKqlMode text', () => { + const onSelectLanguage = jest.fn(); + + const component = mountWithIntl( + wrapInContext({ + language: 'text', + nonKqlMode: 'text', + onSelectLanguage, + }) + ); + + expect(component.find(EuiIcon).prop('type')).toBe('boxesVertical'); + + component.find(EuiButtonEmpty).simulate('click'); + component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + + expect(onSelectLanguage).toHaveBeenCalledWith('kuery'); + }); + + it('it set language on kuery mode with nonKqlMode lucene', () => { + const onSelectLanguage = jest.fn(); + + const component = mountWithIntl( + wrapInContext({ + language: 'lucene', + nonKqlMode: 'lucene', + onSelectLanguage, + }) + ); + + expect(component.find('[data-test-subj="switchQueryLanguageButton"]').at(0).text()).toBe( + 'Lucene' + ); + + component.find(EuiButtonEmpty).simulate('click'); + component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + + expect(onSelectLanguage).toHaveBeenCalledWith('kuery'); + }); }); diff --git a/src/plugins/data/public/ui/query_string_input/language_switcher.tsx b/src/plugins/data/public/ui/query_string_input/language_switcher.tsx index c4afc2e05808a..0c3659c079913 100644 --- a/src/plugins/data/public/ui/query_string_input/language_switcher.tsx +++ b/src/plugins/data/public/ui/query_string_input/language_switcher.tsx @@ -10,6 +10,7 @@ import { EuiButtonEmpty, EuiForm, EuiFormRow, + EuiIcon, EuiLink, EuiPopover, EuiPopoverTitle, @@ -19,16 +20,25 @@ import { PopoverAnchorPosition, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { useKibana } from '../../../../kibana_react/public'; -interface Props { +export interface QueryLanguageSwitcherProps { language: string; onSelectLanguage: (newLanguage: string) => void; anchorPosition?: PopoverAnchorPosition; + nonKqlMode?: 'lucene' | 'text'; + nonKqlModeHelpText?: string; } -export function QueryLanguageSwitcher(props: Props) { +export function QueryLanguageSwitcher({ + language, + anchorPosition, + onSelectLanguage, + nonKqlMode = 'lucene', + nonKqlModeHelpText, +}: QueryLanguageSwitcherProps) { const kibana = useKibana(); const kueryQuerySyntaxDocs = kibana.services.docLinks!.links.query.kueryQuerySyntax; const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -38,6 +48,7 @@ export function QueryLanguageSwitcher(props: Props) { const kqlLabel = ( ); + const kqlFullName = ( ); + const kqlModeTitle = i18n.translate('data.query.queryBar.languageSwitcher.toText', { + defaultMessage: 'Switch to Kibana Query Language for search', + }); + const button = ( - {props.language === 'lucene' ? luceneLabel : kqlLabel} + {language === 'kuery' ? ( + kqlLabel + ) : nonKqlMode === 'lucene' ? ( + luceneLabel + ) : ( + + )} ); @@ -60,7 +81,7 @@ export function QueryLanguageSwitcher(props: Props) { setIsPopoverOpen(false)} @@ -79,13 +100,21 @@ export function QueryLanguageSwitcher(props: Props) { id="data.query.queryBar.syntaxOptionsDescription" defaultMessage="The {docsLink} (KQL) offers a simplified query syntax and support for scripted fields. KQL also provides autocomplete if you have - a Basic license or above. If you turn off KQL, Kibana uses Lucene." + a Basic license or above. If you turn off KQL, {nonKqlModeHelpText}" values={{ docsLink: ( {kqlFullName} ), + nonKqlModeHelpText: + nonKqlModeHelpText || + i18n.translate( + 'data.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText', + { + defaultMessage: 'Kibana uses Lucene.', + } + ), }} />

@@ -99,16 +128,16 @@ export function QueryLanguageSwitcher(props: Props) { id="queryEnhancementOptIn" name="popswitch" label={ - props.language === 'kuery' ? ( + language === 'kuery' ? ( ) : ( ) } - checked={props.language === 'kuery'} + checked={language === 'kuery'} onChange={() => { - const newLanguage = props.language === 'lucene' ? 'kuery' : 'lucene'; - props.onSelectLanguage(newLanguage); + const newLanguage = language === 'kuery' ? nonKqlMode : 'kuery'; + onSelectLanguage(newLanguage); }} data-test-subj="languageToggle" /> diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx index 61e894410437e..4142bccc09f40 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx @@ -19,6 +19,7 @@ import { EuiSuperDatePicker, EuiFieldText, prettyDuration, + EuiIconProps, } from '@elastic/eui'; // @ts-ignore import { EuiSuperUpdateButton, OnRefreshProps } from '@elastic/eui'; @@ -57,6 +58,11 @@ export interface QueryBarTopRowProps { isDirty: boolean; timeHistory?: TimeHistoryContract; indicateNoData?: boolean; + iconType?: EuiIconProps['type']; + placeholder?: string; + isClearable?: boolean; + nonKqlMode?: 'lucene' | 'text'; + nonKqlModeHelpText?: string; } // Needed for React.lazy @@ -185,6 +191,11 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) { onSubmit={onInputSubmit} persistedLog={persistedLog} dataTestSubj={props.dataTestSubj} + placeholder={props.placeholder} + isClearable={props.isClearable} + iconType={props.iconType} + nonKqlMode={props.nonKqlMode} + nonKqlModeHelpText={props.nonKqlModeHelpText} /> ); diff --git a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx index aa2fc9e631436..42ebfed42eb25 100644 --- a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx @@ -21,6 +21,7 @@ import { htmlIdGenerator, EuiPortal, EuiIcon, + EuiIconProps, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -56,7 +57,15 @@ export interface QueryStringInputProps { size?: SuggestionsListSize; className?: string; isInvalid?: boolean; - iconType?: string; + isClearable?: boolean; + iconType?: EuiIconProps['type']; + + /** + * @param nonKqlMode by default if language switch is enabled, user can switch between kql and lucene syntax mode + * this params add another option text, which is just a simple keyword search mode, the way a simple search box works + */ + nonKqlMode?: 'lucene' | 'text'; + nonKqlModeHelpText?: string; } interface Props extends QueryStringInputProps { @@ -698,10 +707,26 @@ export default class QueryStringInputUI extends Component {
From f246a053f3d0f323b78432d889b5f0fe73e74fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 23 Feb 2021 07:23:34 -0500 Subject: [PATCH 21/70] Update docs on filtering alerts by string params (#92239) --- docs/api/alerts/find.asciidoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api/alerts/find.asciidoc b/docs/api/alerts/find.asciidoc index ebd1c7841206c..5af01efbc7787 100644 --- a/docs/api/alerts/find.asciidoc +++ b/docs/api/alerts/find.asciidoc @@ -6,6 +6,9 @@ Retrieve a paginated set of alerts based on condition. +NOTE: As alerts change in {kib}, the results on each page of the response also +change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data. + [[alerts-api-find-request]] ==== Request @@ -14,6 +17,8 @@ Retrieve a paginated set of alerts based on condition. [[alerts-api-find-query-params]] ==== Query Parameters +NOTE: Alert `params` are stored as a {ref}/flattened.html[flattened field type] and analyzed as keywords. + `per_page`:: (Optional, number) The number of alerts to return per page. @@ -46,11 +51,6 @@ Retrieve a paginated set of alerts based on condition. It should look like savedObjectType.attributes.title: "myTitle". However, If you used a direct attribute of a saved object, such as `updatedAt`, you will have to define your filter, for example, savedObjectType.updatedAt > 2018-12-22. -NOTE: As alerts change in {kib}, the results on each page of the response also -change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data. - -NOTE: Alert `params` are stored as {ref}/flattened.html[flattened] and analyzed as `keyword`. - [[alerts-api-find-request-codes]] ==== Response code From 4c82ffc25ff468c2dc73e3902a865d7574480cbe Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 23 Feb 2021 12:54:55 +0000 Subject: [PATCH 22/70] [Docs][Alerting] updates images in Alerts Management and adds docs for the Run When field (#92225) Updates images in Alerts Management Docs and usage of the Run When field --- docs/user/alerting/defining-alerts.asciidoc | 7 ++++++- .../images/alert-flyout-action-details.png | Bin 95857 -> 204041 bytes .../images/alert-flyout-action-variables.png | Bin 138446 -> 271027 bytes 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc index d0a6b7f1c2c03..528189b8a1216 100644 --- a/docs/user/alerting/defining-alerts.asciidoc +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -56,7 +56,12 @@ To add an action to an alert, you first select the type of action: [role="screenshot"] image::images/alert-flyout-action-type-selection.png[UI for selecting an action type] -Each action must specify a <> instance. If no connectors exist for that action type, click "Add new" to create one. +When an alert instance matches a condition, the alert is marked as _Active_ and assigned an action group. The actions in that group are triggered. +When the condition is no longer detected, the alert is assigned to the _Recovered_ action group, which triggers any actions assigned to that group. + +**Run When** allows you to assign an action to an _action group_. This will trigger the action in accordance with your **Notify every** setting. + +Each action must specify a <> instance. If no connectors exist for that action type, click *Add action* to create one. Each action type exposes different properties. For example an email action allows you to set the recipients, the subject, and a message body in markdown format. See <> for details on the types of actions provided by {kib} and their properties. diff --git a/docs/user/alerting/images/alert-flyout-action-details.png b/docs/user/alerting/images/alert-flyout-action-details.png index 06287ed1837d2d6fe0da2a635d08aa44fcbc3ab7..7547fc819f539035bf8f2a541f675eb788be793a 100644 GIT binary patch literal 204041 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≪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

QG9K_hbtro z-qrfbXk#~WXG1%qTj#!M+Y%MPph+gY_ix!pr9RnmENo$dCmu3iz~hBK5beww-&{^= zEU!q&gVm5mg_Bi7#*k)B>q)k&RXqOyw^Et!pCC5YQue1XgeE+(=P6@&mHkGg;tl`# z$K`R>)4iL0$cJJu^al%y+bQ#!?ywyobvLhMT5!%l#6PgIgEmJ+Kwi(cKTt*qnce8# z3#WJwjs_FTeH+D5c;9duCOZi1{x1oiaL(%UQCI;A>CgTPOjqEJhVVWFjN9Of(^-gLh|Qys+l)(v%mT6@0Kiq$E@FioQ03@T0S=buUgtJ17x0A ze}2kfJbU$RQH5f73X3b`O`o>LtN8x==;6wp8@8VG4a#Fk%OQPzlt=fCzyFBPqpo;~ ziH#72A*5lfggfwihVM<+0toKCRoX&DFQ13%YG4Gi*60gYFbH&DbN{XKAn7$G$=f)$ zV4x``lb^o&(0n_bB7b`cGuCUh_&e-xI-Z-Ij=_i9!%bV!HY>m%JjELFU>_6mLJ93T zIhJ^NIJ3aWbdQ4f!upr(X#-zw&m*TY+yF9W;nQC4ylOePz*TfMH1pDMnO|Hg0>N@Z ztSDS!8oqG*ku=S{5NTNxpE-HS-6=;Lnzmahi>%%w6R_6oDS4!0Ann?IyY?aU?R+i< z8{Qlh;)c=41qa~UP(!o1WU)tE3OIDeCJ80Wg}k1(SQcxNf**^y;me~zH>dcA^xT&1 zi-qnt#0srPf3cS8RiF#}2Lbhz-gL|TlimOxVUD`Bv*enASu&?5CrLHV6Qi`V>4_el zpmfq!2jyXAFVXpAi&SIR8J`qYtE3+_lT*w$D5LUCBTm9RCnl&-rjqR+vi2X`%&fc} zg53W`3Wex3c2d*<8~;X({OH+r8}X;V1JNidb~63b956nq2Kdc>?IAY1oE>0tAf4(G z!ZTj-=@ImDl}BJuC1Fwl+cm-LT3DOi==d>8G5TC5FoR__Cg&v9BQa=|%pip~h15g~QcpnY}e0JOa2h-+7%0pX7N@r75VJU7>4{Yq-m5!AK1 zspY}5YtmKe>j$5q$wv$P)<~Be2MqR(stV7;ND3J6?T`8s+CxQtqEy0M8|QWd`)*hv zQJ%W|7ZWP?gPV^q6LfT;FT&%XhwJ;3QFgtz)(N?}bFbAyzWA~FBPd;;$}ZweRn@!^ zynAbOTF*pJt%>&T*pzWj-dp7JBB<8;z-aDVvbqCr9GguZbN319Xx=U}IP`u=m;?2j z%tl|fCJW2UwMfUsyk(~~Ho39ADnpnFZl@1`iJ=R&GB(c`OSG%$H&zL;M*V@M-!VL) z(P#b2W&K|l_jJnM@wXD{AfZXUE(7nen| zb>@K2OF2B}cR7<3@)cWArls4BxA_Mq(qvTXh1y9e#GQvyJdf%nXzmT?6u-7lw2Hmq zpTY}>(VfK!tnP@Trel9=Fq)@ zR!H7VM~V?Zx`=C+zi3xU`RXBQ+kyo@&~|+NcsoB`}z-ncfq8&ig_Ax^Raz>5iXs4c-rb8^^TD} z0tX^y!tt5~zKE;w)xCa^t&#*&^n#=^%za(ISS_tAgddFARtOu%LN;{-Ih#S2PXGDc^rq-(xvWhY)Gob=OSer$sukq2gH~Ey=lx$-1z*BByImJMLRA|Ns+xFVRoEr+_3&iAeoD>wa613!=204E z4^CTe!Ky&Zfe@<6i>V3<^|Lw4$NK*ebN&<7{#CWL|Ep>VR=>JT@2t(2m zwmZH1E9lYD)x8~$f|6doBjdw<*O7wUA(^y9zTfo<1i(qt%mRr&7Ysa>P4BPJZ*e{m2BE(M zSU#ECcbA0=KJHP$H5ig^vo55M2;yMM%ky#czHzkqAoKEjAml^|KE&0Z($@|zqE*<< z^}pZGv-JOEsD<4|4`q=lxd^@l&=UKa87s+oXP&hBg zesW9e1k|QtCFg7y`5%-5Z=lZQmq!brlqHbfp-(=YLxcC=;}qw z0{X6_pPIY8g5ah&(ENBUUI27I3cK7CUUDBzHRnGNSzijTureeCs7Q&Cf)3i93j6MJ z;b;gczr@ntq_E75G#p+~Yg!sudqf4(2IP}0ecf&{O3>p*-lsKtiruRo?%M^U zO;i-+z*JKu0n~Ele+v|}l{`-0>XyxaxQHSE!K(EBcbQj-5YYB2Nj0UXW#S^>Cny;4 z_T)!f6kkr2gvVKkK!MIMv#ulIsAd z<1SDX$4dM@sxSwf2tN_x3OAt!UST#=is$XTGX?}_ni$tWuUU<)VVH6~?Y`hcCkMbK z=pktGl)N|fIGoK-v8|aMyO~II^qXuvYPOH$J;bq2_sYt?DTso>+!joZ(cgb3ldaL~ zXEs%ey_L;R{59k=B4D+Z?g=qHdvNbbgP%^{Z{i2Lw`DdX6wfWKdr%leyrVY$g>A-K z@`mF43AGECZR)XR&?L7yEbCaDm}07(8BAT~_rS`$l&{TC^~eLkE@CdrXn-v?cp@*h zI3{-0A;%Q z+T-k!I72GCJ4Wl?xND*PNNkrqUOfr!-s5+Vkz^mpdr#Bcw!&YZ+m1AcXmxc*5M$DLG+oZ6jFmf~m(Fy~ z^>L`8)C5a|f2Gzm-T$^j@2Mp1UPo4^V~~Q1K~sO^((jUkj;E5(?mh^5jW5+|eM)0# zI>4%DvR_g?fv|6`jYo3eM41RQ#$A?z&hQ6jzQ#INTM29F{rnIaxo^6M@~uv`cIl?r z*BqE;4^~homE8QACUOji@w@Js5EPm3$@;2!fQppFr9>G^XDiduaMW$utoGu*hQ5Y) zyqXez3us{5cz_Its<~-w5M2aUi*C5E~r|G~? z+?39A0(N!OqD2F*_tpF2ox&TT)S51i@adqz z6=SXqqvpmeejZz_XJ`)jPjet(N8|q4QKNtFD5+PS8VI?1np1{xDYUQ#_H{KQZ4eK{ zS2TOw-<6cc!q(_J`Q5nMiu<$3Px#K_<KrpiI2wnnk%T^IXN1 zCW4S#>$ogDAow1lHz$cqWw-$G#vjSfQY^Jo25TC{>Rle*>(gtLl(b|YJluPD%n!Cd z+`q~Z&Fz&lbpM#fWeYWJ0y>kmd@1Y{E4OSnV$&L(1?Ow6H;1%$e3R_PHVWxRQ4v?3 z)o6v+q;tQX>cZA8TT~D(Hgd>~;|`L>m^%JzKT0Jixp`&Q92(^yFpw}D3vfT07V>gs z>`QN1(>a~Oe%><$&*s8I>x@Ir{1MP`LZ~j~0yNQM@~q)YR=bS2^E<{`O;({6{wB#S zhUEZ?O`ayKQ`0LZbXrd{Ur9~U(|Mpg)^hMmv!GV+e2nOo`0yU!{d>~c{hfru4WDQA zKIxsGn)dluqLCl?iSJ9B%5Q$#V0Is(U&?`ko(XA`Xw%-mL>(;#<|Dnw?N6-c_8h>_ zXZ_l$=e9&{)*p9#gNk}^pRciG8TF1p_+2K1C z2?-zzbskxC>+i)BI~T|B=sNL@r4?qVOpkIex(SA@&cQ#oVYkCEs1Ls-OM6~=F)-=4 zMf<<<{ZPF2T3y2+O@v1NZEi4=C&7fD;ONWY%6JC6enfm-!xVSF_?AeePkZ5*V64is z?RB?*OLm@N@x%f+4WFP+qF=mNV{W{m*{jo2V zHPBkA+^|jR0kr8p$cSq(!AT8WX@2i1B9n@8g=xLMT{Ca32ijb zCnQ*_(mZCw&G(nd!^f2}6@gz}H5>yfdBeYr}LX!b&!)Qt5}EYy1}ZI3zd2G)UQv?1V#wlwW@8WyR2h%?CPbhREsI5 zE$Y>Mc&=h??srU*Io!VeY+_Z8=#JQvO`GF{@`;@Pef4)7gN1AI^@6H>%D$+vXxw{R zwnmeFSsLnl6cRVsoZDohtlL+;Y1s4sU#o*s9`31kS@)lD*sEcmJPRdQ5TUa5f z)9skTPyAoKk>TY`oy_S%$w;(@^nqJQgzi#Qi}icXj=g-QRQqBm8`&l^9`4Zgrl;Cv zeu<)kmcmw4n~hycmr)7F3`o3=n+qIi6n@f=w6GeKc7R@!Z#P(T3@jbN_sJ9Mw%W%B zP*vn0hKMZ1moiAcNB1Qj20YV6s)Ysc;E($Nlzw6EEl!-C#=ewtJ*xV)@{~y%dhV`A zIW3f`a;v7tZ4c5zv93!Xy1y@|3EehAd~sfyCr?PvGC^pTLBEdcGBleBeJlU^G^vwI zgS>xo_#i6d4`q66K=!e1@7%`hwfY-a0-oRGQOhHF6dnU?(tJE99(k$EldvREEclJ= z$&by8ckYCYIt{I7=2G%)ulcX;&K8yPmqP34F(50T7ecO9qj#2t`XyOSGAyKg&QkBV z>{bN>K2=C=_pqT7jIs2ux8qb9u3`}g(stbo!-e zV&8n1l*rTJ6L(#A!(jMi{dW3(JgmYHMTwF%l; z<=$;?qWp)T4p)=#?J|o@PN`OLGwsIge3@>&G4HiXXv;uC3z)kL3+(U)w%9mL=uvQ+ zIM?ER0aiUh2F9G$H!oem$D1S#T!v<{%(VpBDhroiF6+VkP92H==qgeR0S-1Tok_N> z|JUO2=d&}Gn8HpDGgO~US2`L=*9*y7Yvqg-PneZ-H)x`5^vev(kZokgNJ1!m5b z#n)(;prT2heYoU5lCHJ$uC|blrZYNN^PWqW56Q;0*8EkhCv!X9;F6;HmSXI&qD;=J`LcOSx6=iPh>oUh8+N- zA+q^gBRfsneY%S^9d^eJLd9gG9R%CT#*ZeVmr+>iK0}%mE<7ifj~d~2vspK`+mI&b z^f(|E{pC%>LBXt7!!AO+a(|)fKj)}&xPV-fjQe=IJmTTLI_E+-M1QYUYR3fY;Gg&PW7XK6djR6vo&J9a z3%Er_OP7QrZ-R>~v18E3W5ZB&U^BuwH1I_ohh#gjAxw5p$?D{286Nprq z@1xHkoKiA!=kA!%Ujyis(=9d`pi)-Nwi1`Ph{U9XhJq6vpmX$%dW5Wf0T-2)L^3kn6v$EgEZaIRcn_JmWItRjK20quD z(RlgnW`hM+=2B#;v)35dsL#73@b$iwy6V?h6n8M%h2-MK>B8ie;?7w*+43H~qVE^BGDg;g5A$ye^f_FpDfwj-#_Hj{6;wTD(+fy0d%j7eHVr_ht^>>T>Uvv) z1TKPWAX-sYh7y{MkFS4=+K1XbugXf_A>6|JS<@ z<_*?)K>J|QiS6ae&? zZ&9pL=JdFx~R^rHp6<+lNYxjAsp}Z!d38`z4er!-9?q{H)`Y+g^7x)k1F?jB(#T<}L7= z%~i%?4La{w;p@sm{tMc}k?~K09`Tf*6CyIak^+7_4u)ovM-!tiv@?9W+VMII!4Egg zty)$@E$M;^yKMc!NFyoj6vBV_yVB>Oh{oJqc4+3R?UhcG;fC$phv{!aijLI^GAHkG z!42@8d|HcE)FA3Ge3eR-soxzz_wg>(|2@sSDg`JN7mu0`hz!B5*@l~ch0;((i$3QW z0ea~biO2VgipP2whbEcLpDj4_BSSAP8^>z&62~ zkzpCGb_+=s5LctkPLDq_<<>r6c{-9+h5=zBV~+ za5T!xMrcC9DQh@P==3Ej%bf#6W2BKkh=Yygyf-Fx1dJI!0)BORMZd$Jei^D`(x;c7 z+W;!+H?U8CUQK}D;2l_Y1|$#em50|R+SJ)IzfIdt$kU{IaNgB{k^WpeJXo2An@IGg z=D<087vLBFg6iiVW%VD4H4=~zzc$L|Q4M{1kX)ZdQXyHvz-3L_)jXHHUyly;vB(P>iC`wd&JZ)pcH#_#ylVJN+#N&$#?J^H8`P zu?4 zSiem((^#zM_g(kD0Py4#z}XRxhzThAT;}+9ygJZ`uaY<5whVJAP!f%`nXQoVzp1_m zLL=mo9a(yD?s)H^=&_Hzb#nx{28tKcy8sK*wsw3(nDv-H`VwtN&^foky3ZRzA?-KB ze2f6Gm-fLLwy{RxDhP2qcHK;>}O(S=5BQr^l zo1-?6hrG9Il}JXZk(z*@?*oc}UQh8i9O^YXM~4Gy$tvE+#m1Is;GW;VNq3pj`a!E` z=K>=n16-seCfMrcB+SA&8vvn zT!{GXehj9xXgXr6vXL)kV0uX5snSk!`@s zuv`A~laKz`3y48qYx(o22Lm5+K@}GGB^n8NqpNp%=BH(!+a~zOuNRX9R82={JnS3& z@hT0jmZfLSC*AX7%_iJU3t&siLappD{jC;k1nt^gx+CcAH~mq0&?lVtry;4{!?TWu zyxYCJdn43=YGpDJm*|a#)lSfao4as~}*3_H*@vfE?rn9J|Z0ZTnI=O9PUgPKm zPsL~7B*TR3=dRDv6qdj00Y*M<8*Aar2`<)&K^1$JT0OBqLyH7yk3At&T&K#0wy`Ay zsWpYWb+-7s0!e z-X1QoSrRDM@>l{NpNg^vrH&;C^t2zLMwk4wRSc7b9_epcgVaPuDkEI51M7R+$8nJ| ztKVhGcD0c(oeb5s{*aXGv(5NW>`y%h3=mf~6dp9LkEniRuiLEd8$v?xYpn9kh!txL zHTE5prRD1PR z*#_vgEN>Sv*G)ut{;7=q3H3Uf=0N6EObXH}<5L>gU{WgMYb#NXr$ziWF~GnAc_Tv< z91CZ@Ix4Akm#nv%{K~&1eI!3{Z^n$ z3Xv>CYLz$;okk^$R$!>`J)iw>D(?n_|7@S2@a|IjKJs+Bbe_sRL=BDRv@&WZx-Vsjw+uj zF!H_ze)C3lRPDeNx^M!}C?E$>J}Gcqa?g%qR?}r}WaOjy-FVbg8k|EcaFrc4eigAf3)Y{`bpj)#K|tMegsGyH>%^ zTYc8MH8$q$;r-<%zA{sU6-AlT+`DHKBVG`INHnJTuUY zWylSp0-P=MNOW8;Iehe(7nMkF2>9rqi3Nzzt5=0MtJ!Wu5t!@+@kPDmQe9mg^LvBUPuJYnNR{M*^)^T6hY;PFb48R-kq!%sHvFY&)5UB3W z`C(CDvnI%-#0@t~_ux184LT9M^^}cNufEz}eh$mciyMIQ1{tj2wbgb{f|#45nAL(q zugqR1WWLGh($rfRV8h>4YVGj&cip{e$5zAJkiKxd=4it$!4ToJ25~3_TjgS0-}?FS z_Qu30q}2C%<(vQ5FBLc3`P~5oZ|rS-Ph5T&;HVWr=zQ;wUR!kLmilq$1a5zTzKMFU zEQ@T24r)}iB9;6U;wGO`i@bn7Y^jUBN{poRfv*0w62w*HcIAH`4lS|_13Q9t5JZr_ zraEo{c9wNEXMT+qgGnMF^TnbjO^nj5%Xxbxl9#ttk|82N1W4Ng!t%qyk6x8BzK1HV z)%#qbSjC4>%8bk3SDGM#BjSWzLvYMo4yC@QwiW~t5r6Wr@0-1#+q~_>yO(}&f%R8 zz;{uQaw7FjDv3?y)3qofcQ*z{tTAG@@BKyX`HRYO4ww6jM1!s^&7v!lu@X;7lRRj= zF-pSD0CC*M^KP9Zk4w$EqowCoQ%$8!4d#80Y1308erhsvGkD+%i;*IMp5{@unA6Qh zwFkh2_8QR1K^t;ayzTpb@TMQvDS#$Qmg}TJ0mNe@)~ub35n2_xrDtSh9)YK37wiEE z01E~3Ziqe&BjCwQTuO-ZWfsNhcPjUH=eiBVW*h$u2g|&DPzH^(&A&!|E;c<(oTFNZ zEy^)PTf0nI%A`qMt=L}qx}9_E_Q>|aUFh!kg~#G-Q9ChzRp@D*ob+Q^Q^LE6Ql=}8 zT>zViqo3S!zjw~V>UVtqi;>smgV{^(hpgdpZAGr=$NRhRT7vCmdG1Z+^t=pr)Pu85 zVOg@_6TaqsGDz|<65zsH3MJeAVn2B7p|jNMLigUAC|*q1elahWkF*BlG6XVlh1FMk z-{$M0 zj~yTR)#KIW9Jh>TN?GX7<>CAD78&y*e5nxC&+m09$5& z(8!-|3onw}#Y#`?j1qpJy1zl6(${DGvIH)lDA3oMZ@BC5H|^+VGA(_l?LTq8`^oBy zGNR(SUw#|l1%qIWt}GqGyU6D*vOAg}g)XEk=Zp9LE`+c7&_hcaQJazHfqE1jzwLG4 zN3vF-!56!LBTxpTdgX8giQv+FPxl2wc0k3*giXu^p#ThIhPpGYg1*m2z?uV=2!FHG zu>A?oGQdN|$MIn$^7_=NZ=D*9b6o9f46>(`f^p{T#NHPI;(y;dt$ z{hV%m(X<{sGg@!Wa`dhFYxa$DzAIYNUI5qK`M93(s~n%TP-PE7l74wVDV1hpG{uF2 zCm3~}1}nd3rq1i9yG1A(vfQBE?pQp#=?q|J;Mb>?DUSX{k0b62X1l!YFnb%<3gMCG_=?P4*t`ud%c11GZe8Xq&c&`3ethV^r`-Y4b20=VJqygl{Go&h)YHF@KLg#{ zF~4j9Oz$w7(nbEqJ7>XkL!%&stRQ#y9x-TI^Q~R!r6KD+ULm>8bkP;7tH<^t{DROM zY`s`is9LDoHZS1aZA09G%&6V)VPmEK!V6di>|+}~bebn=|# z*Dm>J%+>6#m0bSom^jlpX!r;^kK+lHnIO4d>gwoms~mVc9T%bQTmtWHK@f znyvD{F6>xso7>>ncAW$pB7#J#7!yPJ1>ia})W|?`Wp_ACZ)prLv9L3}PSuU&1`#DUq*WMX7E(&9& zfX%{t>w|4w9jhzP9FqSC1>2uG;ooYL@45h3P3wUiE;xbFLxkO8IPUMiF`bS-^Y|VP zBFwK+tJUbLH7z|ls8o9p3sxbMPz)q8g@u)v;#Yk+Q6}KEO4T>>d^O7@GGI=_<^GCE z{i5aWbSHCx27S%W<$wsYvPh#O2O9G;N&aS>$RzqLmf73w@Nh%iaPqL~K&g2Z*h(8| zj&9(d$H-e5UY~`KR_#oKcFNaFMAN#l(r8SLBJ%(R(yY=pS*%)IY)kU_+b($s0(Bh9 z5kPUmrc!KI5g3%`x@!z^M^ds?cf+)uJCF0UWKzpw2RfkTJttR)Y?Vw?+YjEi7Z`oW zvH88z1`<$YwmVLp8nBXcVHdxMs8KFyXL+0|bs+ zx0fvXI$SL^ODUT|Z0*iZ$}o3A29?tTD$d))tG+0w-^l!Sisx(lmr7~hJbVI}dCS=YG8fmBp~UeXRPwy#NSb>>H7MC-;Hqmh<#?ZyE{Fi($2>LbP>8s`3=P z(*Tyg#Mp%T90D9&6OHSpU94hlt8-5spi;^B;PTKwh21xZvbzSZL#p1t&0 zz^1a4w|nYA4jfry`}_3jWTZD0gmRHbb|lBIIBs6jq-j--z+e(Jiw8dvCcBN#pLuo> znt9IBT-9!)KE1wam2@bvp#;fEZbry%nMXzC@pSx_F9aLWbXR#p&b@?DdlRGA<5p2wzMt>rjFg?EoaDbekX zh5F5Hpe!l3eh3}q(goi9kkC3v17gr3{i$%q!@TaY^Mn!`sL%!`-_-mWi%W&^5~!xy zf4`P4eE}d00Lq+2K1-rrV`zrXA3BWrm?G@%$Y|Yo7o<2D??F$a1T}@C`yoWJqNt^k&_~WY(RY7ta30NyFV@Rv7VH|7yWV54e!h;{wf2QW(XK_2p{*Zc4PHmq zwuhs8-U8x9Ce946ejQwfFLf4~c*^vevnT0X_S*4=QmIX)BL0%Sr?|!E^eu+fz?@&p z`I=(xltLh$TX8R^0_E!CLduF^M+2_XSBNeGgA;M(fPxKy*n75|u?&n@>x8hrKn>)A zWp6m{=UDQIGa4t>%l`|<_kXieKs-Ij>^)v9P@%;Ne$VIDZl7r2TCeM><-kZkKNkr* z*DR+PYt+#2bedpa;}cY6Wyd!Bj7oKu=~iK)N!^ozxP&6;DY+(l0b7(0!($?Hu?h7C zBvrGUuBy*EO%UlNuanel4(g;mGyuTpovjxik~)t=D*Q;?95wn>N#Iu0Hc=A59LU2GB>rB|5i;b|?N z({Ge3HrD4(J4#5UE4N_h6)*yPRk_CpvCYj%>kAqn@2?PXFNXS~gL5hp~ zdspN=8~<&RZMulr_4J=rIs|v)JFeKnGu~q zv)X!DVhQNFOe0?ctIfN4W#ttc~PAGpw8qQwS2Y`qmKT=;Bj zI2z!dWm-ql5wQsGpUcY3&*0fBQwFJ>nMdS)y68uaq^wp?;t?tLQ%kw;th_xRAAx30 zmj{{tB0^a3H5wMs+lW=1I=*p}x%IX}Q7c#pa{IB#?^(|AlTJoR| zG{%(zN;PrLdo!N^+=rm;^}fmEXhzS&!$}RysvA1xflFiamfKZ)1w+A;M)~c$-y|Sy z>pR9YaUlSb)o2O{lF@<77NA7Zd5sr_bgW6r5~Kze68g7#)8=35%@B>kCQ?Q*RnM~$ zw}}fb1H(Zc1l>}l;%~!vM-6*c+snc&|1t3oK#W@pp9nnF+K;D~ia=ffSPguUIzcEN zN->CsEZ***y0emURrx+w3^}?yjQ6-bcIoNAd(LzV4|&op_qVa9q~68=q`iC-PhPpO@O~j zBdsCA6pnAOyT+#U47zY|z7OD$pN2g2Tlc8}9oOKr=@iX-7`zaOP9ohFL#Y(J_Hyn^ zc~!nEe>cK+if>l%ybnsWx#Ek|=#k84#dP^P92RC)9TTyj_kFpM!jE_1fJ9TXmB`Fu zAYqc>CAdGXVcW}xVu|$O=C?%aJK~4;O_1og1bZG4nZ4lIGfkL?>`sk{;ztgazUkb zHO<~@t-aQm&lr=EittheW-W6`s7wEasOP+=OF_p4FLjvTGXYNiXp80J*%KJ;$9+^IWvFGVup$ z;lGyBgMH5Hv?H3*3!aTXTNhVW5vCeoW9Dpz-@X0}l(j^ta@@f7?epXtzC;;dPy(Tv1R4aOey*!Y>j*FKnObhS6)ctUnPsB6_7uZS7 zZnRNJPl_O^pBfBaytpsRVxR};zwN}nTTt%D{g}$ z>F+DLe)rt++h9qck-L4y%HjCN;xEWW$Z(Snw%| zl$wZJ-&S1!vVX>zc7IAJ^XH9f+YxA!oga9Z`{3O%D$BWs0Qv?CF$UOsD{lXU`DiLQ z%~K^Lh)|)zH-;5CQ5M_laR&u*VnxKw%XQE6xkCC2Nq)Rx?LT=wWhrOUx-k<>pPox3 ziWY@Hj6HkVG_eRQ#PLj^HfOT!T)EhIu0pHLb2XqiT&VmlWxJIp3Y~OJsMBFM_Tp{| zr(`uoRmVLvsX=c0eV$wzrTf{EY1_VNa;kNQeLGo9(Xm`5zj~?n>SOA3aXd{hC*s;&V-mXs$vNguh`=L&w#uX8d1{)s zV-a;(qN!IH6d5~U2$1`st@tf0L=JC783hQ3z#Qi&OEoJ$y?RZKf0Olqq=#AQ*wujT z)JJwBEL8nWp=SX^yjOlIT=M^Z4qYz?3elVF!00__FKk}ZrW=k|se#=U+?{*Sn(T~E zmkP;keitK=dIQ_;WS*kW)p;>UWQ%_jLr1wZdNq>D18d>;gXNcWB4!@7R(QG6TW3tX zlV(bby6qh=_W}gYw{CWJz>apZ`fg@ZrXG-QNap@Ic2yYy;+S%3x{Ekj8{OU1hFv%7)0C9N@i)*e|<|On($1yOE%G;*qItve{!Gawo2-fjqWc7nrZ7wJM~u+tNln_$ys@BRG= ziDGBUS6Ec?hfhA5n4AYE=T^waJ^ww%>GffPM3DC+ryZ9bq?Xv_C+Bu$ zQd%1S@9Nl6t(2NkTr(O|%R1WMebaw~aiTAeR@i|(K+tPtz$`YqKc`YT;L1qA3nY$< z-FU5t@}!=4HUdR^%ywD}Qw1H8O*L4~;S@P1MxLaJa-AjdsRHj*5!HzEL3 zFFQh?KD=bqO)GK>vAys;ZhA&SK?;tCKk_5VhSE({qj}FUX`=M_R+;EED8VkYijvxCa2@u2lVkRW@ zG#=EBY78zZ_<#A2h5?1hL&5aad3$46uS*SVcEvM$-h41w&4KLOaFoX{^wi?Y+lE5s z!&SJ;Nf5;aaK5$i`<3>0JuKqO>W+Vi$`smnM32RyI2q&L9?rFBODG!ulZDj52v9)# zZ@LsZ%K)~kV-l0Jo$`GHL*&NTt4VM=8hy zokZ|G&@%*Dz|{Ev#5E-UiB_$m(|tEmAC*m_O5tlS+8|deRi^R*YDBKJLVil8WAeB! znx}1oYU2z8V#N&fEhjb^8{u^=C|1Y+fJpuq3laRpLMWNgWHJF?*iZ@JT|VOCTw`Kg z10)Lq_k3FUx>0W!WYg-3;{9(4bjIgBltLaC1+5G1RAFQW`)b+`+;2)R#_sapo{(tY>zo+oO zhsl4(-2XMZ{ySU$Jv9EaE>Zq>Dfyoh=6@IAf4(;VXLS5~y!>}5`Tz4$k|y10)kiO6 zHe1GS3V1QjfDXa~5duyKGjr2y9NZc|IxIh~_EC8M(5l|~gQuZORlnj=ygBOKoC^Ni zh!WW`%_jb->_20I-xdWh;S___(>Men;_ zhPIv!aZjp=3CR?xGP2iiwes{s{`S;vu@q`m4}58?qp4M1fz)dhsh1a5Z;d8s(1laP zO;LYaA>sNKODf=`3aGakvFsXj{bv3G-G>C*fc;c7p}2g6l2u2TY`dt+jwZSY*wp1R zjl5$H2k4c36)H_u{VLT@;iNm$f7`%lpR)sDn?QKs*^JQ>;S1oxW{a0(O}`WLTnYhG zbKuzY!|r(YS#N^Do`>E~fojt~4r;smZggt9fCS9tIi-1X;up2Gdn^UZ1k@PLPPv5Q zG_;f`Q7D}PpKZ2Wv+EHht&Hg39}&DXlss0`3FYn3w*X|v2o%yqX+Hw?Rz1MpntLVU zx%7zSSDr-IT2R#S-Xv}*S-MzP~6A5)*7nSd9XVgzVWa%Va z{_`d#5`*Tdws=6cd!>t3bI}i9+Ul;Gq>4QC&eolP=Evb|Xpr4rN%qfACZ#Ix>ouu) zK!bcpXZ|Yx1YEV)ny&%lBnD{Ut4o&fxgV!DS7-LpeA4nU1vvXeN8Ls+MnN7erM7{0`I(^2VyY+EZ7OP~z^^ndU$S~3M9Ph2NJDTX&*cQME4q#?| z(LnCHJ0&vSX6paOR7Ax9%YThfCHr-G%USZby;q&mHTllUy$;3ToQ3sHKe5jd$ru-C ztW!wTt!djM1~eOeV%ZuZV%9~bqv>+&i;v+zwhuLt$n>{=7T^dY}^LdC!*Mjmg2Wb%-U@aaw?*7z3Y2`zd(wk@@L^ zE%v6c!sKJ)TXLrgBs39=-99pCj(0QZKd#nsrvUeDvKsQw2m>_ryD7zvP^-_B$}Mhh z%Ul?cI9S$Qb+wY~3`ZoJ*cyqg9Ri;+t0kD8=)BT*?XRPVo5& zjvl9u`dnwDm3}Z{!JFV8!66`5pXQuI z;|NW(QTY_c_F^K3vz=x=RD91dX5z{=UfYGNc0Maj126cNhC5J^*^H=%&I}?KEWCF= zoVSfg`Zfc=-dNu*k5}0Lg?l?JmoM!eH#WZ?ng^)^F$NjOP*$Mo`12La8Vsr+c=CoN z@j~)Ca8Ld=AOE;JZth#OIWquFt0`B1!h_LWcvo#r^O@%FQveZk3B}yz(K=_FG3!OG z?2R;O38U>NfY?$%D2{QlUuhEf#i#l2Ny_eVnTt=-v~;_nT8kU!a)KCO1$FT{E4;s% zHTWR`TSKB~5*zQRHEeSke#H}eDg!E6DRw@8r0G^8+61JH2gjcs*DQ!N(hKmw&BWZtcZaB`u`e8I2PMTWP0dqkADVJJxH2Qzk4 zM?RvSm1j#Gpe;6#UN`!W5Zb$iYxvcQ_4(oclBD6;hXC@|E#z}%$(Ku&5@`}lY(R)Y zYW`)40lCU7NspW(UWv`C5WV_Cc#7F(Ut`No^YLZ{FF)$BZ@*s)RYHNoISsq5kXoWS0VWbNG;q%9y_nNRKW zXeQDlur+YP-4Axw)I;Bs1Uw^zmIn(I-&rwjT4;Svua6 z#mdFN`Diej%I}Gkbb&lHw!mht+iK##Ebs% zdIPiH_0D#`$s@z_WKG>&#}Rsa+V?{g;!&tv*z$(K;j5~eO4khm41D)+M^U7e=6b*@ zqJg2%E-zPub;H`qEdDttmfS?Ocbec)WC7R=G4cV@dadJH;+4gNU{C0?J0Kckl8RPiaTzceuvErTcXzQp*ZD7{QMS$6_Q1v)+p)Xu5R^ zwy)bDmnH?2sBzBqzZ~~mVfrjN+WUV4v}0QS6t!?i*px;@^IXgqbtf$aF+$H_}Ug0pm5J47yZN7-EGxcjpaD zUkGp>WL8SHZ4Z;kz>4CAnDEfgBrNBdT!xE_>R``+ z?CLI==OEYqk;W-)%?`{%slcLx&?R(HR_nAI6F3=WqoZ`6{e3Hdm>R{Ke zVi6b0Y)h0YuuZ}D;j7?Jpp*09r8{Si!$Mc6`~{?S#4_-jAAWSV+#5i+ljNzi-;^7X zRj`EoB^=?e2P^@^ZX@efFHQ^XSL~FG^ZUa_uOZhgaE^&0GnHfUxskA~p0WsjB*6ls zHmp%!9CBYF&_3Q=I^o$nr_J|7@7z8v{W|D3v)}jg;&d2u(5m-R@%8dKZ!kUW+XCA1 z#yll9u$=1qCUTvIIHBQvxppvJ97-1G1ry4bIwNe+U#9pOohi%|(E;jen;u!d*)aI~ zZJH*@g$@mq-6q~dlbZ9^ye@;edv{P&3_(H}@+u@=_(4;NQ9rAosm^6$CkFzrqIh#U z78rB_ROLU$ok!!e1{EE&d?o^9#PjnA_tH)i^{kq zf}$deQVMa)c=*eURN3s+d*MAczXK>(@g`qw4SW72Drdh5h;^IQ2?pD0^F=j5i7TIP ztq1%u@P5eE|K7UG*>Q_MoboZGoN*1kO|R+VN5-^$;xWF=)pO*qm$z3FgReeFM~%n& zjrQ#8>-SFE%+{GrJ;aO}AhjFfJuYPRXyPrRuxoo1SciSsC+$Yvc~0l!rq-JCLC28a zd)t~TTUx)Xw((;j>aCE?Eo_fXa%xacC^OXwugKbwWOh-Fojv}kQAj&Y7=mon6!!B; zqq}dDGwF(E34UgWFR#2mvG78W7adYD3#=`o^x?Rvn15V>BTr6wQHFP(@g|0}8_J14 zvE*qrjzEm}a8ZJBTcXa{Iv3u1uadg%9vP6;>`CZSx&B`a3Lg1({@&9Y+<~%8|5fv0 zVo6(HTb53%1F6L+edW92x2^E#Xck zcw7SWbB2k!0^D_hH|kL8;p9(tx*TbsRu8#WB-@Ino9!hhSy%!0@?%leS0QTwJMN%Xy;^p1yKw$sNFkU6=pt+Q%VOW)0h-45^{aSjfCOoJZzYhR*>A4fKAV4DdSX#~!a2CvX%W z5*T!GGp^&sNxEwjrkDBR@w*(1Yb4w}fOSO3F~#n`>$}#iPgF{f98(qB2ZUphqW0t! z{u-V#quu82z|_Pz>?1SsH;%gPkB(E|LkxH5i0RH2KH_kVy2k0OWMmRi(@Q zE>a=K@uxZz^z80?u;0oMKyIjH@D;q(-xufSV$AKc7;tQq>V9za-i`{gn(cf{2}nS> z9(JERUC465o}QJ18$-%8E46{reHowY-jw`6EF-pAllytG_7D$q27`{YAjrl92nZ@~ zS0bNbY7oR}sRUo{mLwcq@%Gk3tH^fA3W9km0%Q2ww(U~b2jX^oc4eVs63Ka z_nmhFLC(gtj22Dy_MB!k__b!^6Z~HYRi#?X!?E{@ z<}j@QO8U2XxO^|qcZOq&%bJw!yTzukjRVNO!BMsx12@aF^@%np?7a48zzg7p??EFb z1Gqyqc+i|$6~3s;mrr-7nNcS%=d8nHfcYh;x8}m_e7eFB4blyGcoq1RF@^D3kUU!E zNjWSMcR=b@*Crl;q<3!0G8J79F=Kq-tNra(vmU=oQkY?b;dq#b(1J~-2U^(LOP8vE zg6D7#D*_jAvzALO_vM3vw?UxdbT*j2v-=H>on1!CjjwD5r;fLjL8pyXAW>w#N5m~u zLJS%k56-G_T5gw}&&WmS6*K!YrUY#qGZ1;dVE^;T4K(NK4Jy%K9LL|v)*B{qIb8bN z-ozDhFQm5fT9`i&og~*9De~m3hl8Gm6gN;llLAtz5@WcY&s;PGi4 zw(+i#mpIPMuOW`qDwmfVm3h`}gv2mf8PONCORW%X7^i$m_MUyzCu5t0Y3v~4uw-ES|sOL>(q zpQcQ;N{J~1R~tA{dK)u3LakeZc7Ls)=#V}Od;Zd6fb8bn-S+`}458F(x;%B;_{tP- zXtuwQC+EyqUWP~*8o6=^(0(!*v)7RF70Rv`TjAt9ACCvowd%Egk)K+q&3ryCg|^3(Ib1wlP<+RWFzdJxXlbuAXGGGHMl3un zy9jmYhDlRqY}ZBsqbsAGJ@c^En!5WT25~FJRP3FJ-WUvD;%3;vOC_GM8=oYa!ggLF zga~Unn({YcB;%H_)s!2%r#O2pri1VfY0GNJkm+K<7qnpO`s0n& zLeAJ*%tCM-RB8KKM7PN%xBhk4z?m6Z_HF+~`=iU2vXtmId5;|FgKi78iX7WgE`V-w9_-&@Tu0C*v;GQ2=#A}Y7G8gSed^P0 z5z8(Hk*UtZZcjGd8r4q`k-^G@(ny_sG)t+gzny?|3y*1MZn{0bK0;qJFHJ{oD`Sf! z&+DEDA6Ct{YG~Gi%V|7Sy!bnbu5QVLnHZ!OLYV$}KXMg%qC)63*IpraOCOSOsNfre z35|L$3AqtWx3I5p2Zx&=$gLInz=)iv;JJDxfzOe*+et zxI7hR(>+o&S3JAgx;amu^o_9%JMb6TGz@5J$ye!e|qVAmalfbHIwd6n;B#GJG*2hkD%kiq#k(W`KK5(!*W`&Em z-c&`LMqNvp#{7CP87``QBygys>WnS{SW^wy3a|bn3{AbT6WY`F0TE7}FA(`JlbmrI z-Iofkq}5YXgyLV;?+%qcB7cSF4Qqq#x9Gzu3dvvF_w5(>&)LHE`KAxQyLq8Cc>#BqC&#_^YRWUBM5i_rk^c^xp{Pqcm+!lZHr@@tqXpq5_&)o zx*&67SS5aq59AnZ^Ql1bDzz>sIAnXD>13am>$Z*E>G1`SDyMgLONIYjv};!bsC_Sy z6yiF_)w2E*cf%V7)2Z>iVVZBfLu7&EWc9LJ zRvu;sk)N~}sCb0W)x*Jl2{v^|35DN*`HxRBTYmC){7`VNy_}(W`zN4SbHzzw^o4o( z7!TByvsP5!HC2YdLLkD^1xal?A6j|sb+=I3Ef%@Vn61mOLLD%IhHZEd=9e(vdV{z0 zP%$}dbl_)U`&&!2995#vuzRphPZE;AO0@T#2=BS|7_@l-Mq5 z8nDYdk@UOwiO-J=JLtzRIe8iJ zJ#w)QlC?f1dnW8LQ>H}>;@-6i>i&=#LCelME5+}%Kl38 z6q?)s$@+X|*uifkZNX}kdW8*eS~>#fV_)-wD>}c<9VUgA$8P z|HAj|EW~Z;<|?2*h)hT-FtkIhO)?BuLLQjup|l8DqTtIzCCm^QXxg|npe8^~*brL0 zfZDAdkq{!lrXgfv@4Wya;$#C1X_oev@tibx_M$O1KHY??+Bj1|70Ye?mtc|w$UP^X zXS-{8S>B&FkcF+ht;TAjZgVnBgt)6dzG}?fvCMA4M8TP@k+?}PWvO;+Bt&TVz|u(e1wD9J~LLvNoXR>$F3+jvWd<= zGtgAV@;2E@>|$Y7`O*FB?7Q4lN8z|QBy=x@@pSefFZJ;H$kvQsW;!g=Bb*DF=;?iQG1OC~18v?}N|(!NyilDXCZ#1Az1PpdZk^Q*R3U zMK-zgNVmu8?lU(WX_9FtDtSV=`6>CmaGvB`&f^ z=mtf_fz3VAi9p|!yFV{$;3t-1n@EV}2Ih%z^FZpQS@Wmv(%WQfuA~wa zQ6ZPVh4ySF%RTG1M1d|;Ye)pPQU1Sv%`zO9{(5pwU8YJ8nHV0-M5#T4*EJCXZV<$Q zmkbOJe1Xa$`dO(X$F7`L6ePx9Mbz(6T8a63aOuM@Wr5k@p@nFB)n|R?l7kK|DO&U(h`T4I3IdUaV z;O4sc&UeA9(-19T1Mdx1+jJn)Dv1}Al`#FibkaehbdA&zSU4I_QBWDr)9e{1q`_uUu>Q4(j@`+8xryGk zqL$}^g&hl@Lqrmd3y6(5iuNT%diubMQ-~CgSAk9V9bg2;Miv_-!cZkx3(T50<^&B+EVTXWa0cc^6f=6YiqP4FnL^RA;?eiRMC`Fcuo-+7 zUw!$c+x%BwWk36&@JDyN(w9%1#;Oj#Z4DguI<-W5*)Q-L(7AOg!+Shr4#|%q_|$Y8 zUSlr;uE!V;(A#RoMNkDI^PQQR@hrac|ilQ&=Tf?7tgLFImfYqH0eYy=T*8eS-fK|SN-nD2ANhsNzK^MquZoAi#ZVl@*VMAhC*Vf^~3Tjh+Xrk^eTZfrl?``#+5SYQixKQ);Qu7v5 zVWpE_Rm$!kl49e1n%&UjT2huRENBWe|axZvFxB z?{hH&i+xKuNmZvPZHdn(FHD3L<=yFU?1$^X8i=5DK6@kIq!dHH6bTQs6_kHx0SqCi z)ytP6d??WiY_+Mbou#i!opA1h9v7FFpoCN46~A_&h&02kA-6dRGA)Y`cd48)N<7*8 z60=4^*S>Pj!BCD3f&0b{Q!*HofC5`ZmN=o&_OaEUzJo~VTBkavsR?sAd-d|v@n(7UnDrd{uSI|^PiP01bb8RMT5r)6cT6uNg!k8| zHCYwg+q0v^_f?S22{5%J> zN&KGa)a#`EM}kx!_}7Vp?(M`qLVL`deK9XtHr;w<&ONjWHL42^L??b6P9HU&T;1AS;Hg;e0N`ya57s12HI$hQIOK z?z=T^x~H{9ee+4% z;&auXZ#C!1Jx-0Y4TBzw=Cm>KSn{rp+8MuLm$(%FYN^lQeh+rft zEJ(DIQGS7S>r3zKFBGNFbz~FR(%QtU2cObX=AMQ$V1I*U+X}v~V8rEgK*eKWXms8g z;~pf!e9QfY9xy!J^Tz{W(~>TS*Q#~JUodBi3GFTy;W;f^BEYe}bp|1QeqejJhOJ5u}@Z824Te?mIGh8&C#1Z^eun?(EVbj2456 zDk7SlCNqOQqPu{q)yWN*u2D zE_Xl3vIsp&2;cgJ8JYz)WwM~VHr@S=t?clkdVe%msq)BHuGidu{=37T(wIDX?b}@b z7p9c^n;igt^B9LhV z3Bnljqyd%fg3HX6Io&H9|Jf>qkG>TfZTYfEILfu!dU`o<>aN3$Z#X3n%2)TaGHO#h z*#a4&a275JhDam{Uy@=X(IZm>sS5ITiCQbQI34@`w?N$dIBsIc9X8Zm!~>=P>O{Re zUMQ_kk4Aud;Oal5yQa7CaegqK@X}#dY%kSV4Eke+!~g$9upAxb(K-iI5mij;g&$su znt$s``l5SeqeRJ&*vLq~JL&blTszm-aN%0B-wSMy)LSzcyfc#lY^_Hq-s}phx55wL z-OtxVe6*6IllfGt1=Pw9N3+`VRV%9O%dqk-$9;EN=bi3_-eG>w%pmRDf8s%6x_UvC ziD~vD=acZK0o;uV7Y|&kikf$iHg^A$NQG7b2v)u4h7u`n7`0I)gALww3kv(My+UDB zH#D+wuJyB3bUGRY?mX_~E3W`pd3hO|L%}2ZPWXDn~GQC)X3TehW z0Kv#H{}C3v=*ApXe_pBl?<|7toU>1PIln7HU0b6-k#nav_tg!*12lT6y-s zTZ`L^d5>3|huJnsKVFQ#t@OY3lgO7BsiDUvSBH03uE+V@8{w45Gkpb>B(|8JjA5UR zG*eix)Qu6zu$-MgMS0v5s}~mh9XIYf3j;-&Cm3Z^KegyduD2*J{-L@1%-Qzz3iRmM zb8Cmj>lWgoc6-reK)os!_$TR|B8HG|W*~+6Xu2BQE1m#(Y}Z>{!uJSVM0s40GlD~? z8d#_yEmEQwnG`|X=SLKrz^;=kY43@Nx*0WJOTntHt+6Hun?Ph&1t=IU724xTx6&+I zU6GQ*gse(qm=QyMQZF{B26!pAg<-wc#EiPuWcO&5cJe`(cW4bub7>@{S&gkUqx6*_5fck!1Ec|AO+>j&*yd&tS9IdqB0qqW~e zlF&JtrpntBSF^-(79(rr^<-57BrEyK{QhxX z2$P@Diak;+=MTll_tA7@IUz4zgl4^qf`XCuTCbk^Z(TtJA|pe;XQ}QnP`4I*C-n_| zY+)g)s@{6xO6t7Bo-w*SVye&8&sSY7)DCfXC+`eB*hG5udTyv+CzaOy{XL103l6J6 z8vpLqlx1~JAvudC``{vw{9OC-d9xv& z_GMGz!7+I$*ug2~9+G$BX#WL8-E%0RgJ9tb8L5F?sS41xKfm> z8Iz{t`*k28Q3k&SXaH8@k)ZIUe^u757^tdJukJMN8PrX?E)>0khBdxgtp2X7|Vb54Je|oNxcyk z&dtzp-9Dxt8c%P#d%&u-TK;10tUK{qK|K+}mVzSmN~WLwIAN~R789&D#_ud|n2AJhBDae%nkd)H?et124Gvshw_WybeT4L0%-d4Grd{bXZpW7i?5zkp zx&6wlP`O!X$8uX{P~T;O>iK!298LZHEj^fSUga~00i4!Z?Z#Jpc3%YU;IY(=if1=q zJ?6eVeqJ=BwbrVC6>`-C2mh*a^e#_AQ}^&b3o$&GXhld71Iw2bu&Q%x=Afy!jH;Np zt4Yfra%#wy)aqNm;i|(T>G!wJ!D|+TUT;=-5Z!+HeEE?kvu3?;myuOWdxgrEsIcx+|9EyC z$^cgwp!M|2|EhKv(AZDY*coyH|V)Qa%1bs zQY%#M-As56ZFhksTS$pI_Dwm>JG<{u?<+SCI*_O5Y+5i|6 zYVl4PhOUIL_ZY^jYpLH>0Px0&KU39+rDXL-W1qGU$_rNA+ z>`|+$`=s>2@% z&)Xvze0pv|P5k@j9^N2(jOD<3NUPK}VV|67`7`;$`9ZN~bKaRN)jQ0bmyiI-H#gs# zX&q{VFcRwN)A+|doJ2&v?%-?Eyj_)Lhb2Lr=nIyG%vz$tRrXThHrEp=8W6>4lr9i z4!=BPeu#MfIHqQB?~qNuJ*Okfy>1;Uz=kVkPOFfv%XivbT1LwzWdHYF`RosNs$P9Z z3qVQt>z-9n8K+Z-wsUSt5^%7PLO>e414cuae2;K z=YqZ)I7I4v`c<3!4HTyagrvdR+koAT)I1GWL^gWqAJJ4lR}Rt|J6Um|QZJkaH3ru= zU7!DQ>qoWY*?+zXdr1`{m%?X{7pB;--sJM;g)t%(&B8w>xZbL}erKqAv*-qUvDH>F z1SSEB7E3|(+CCG$!as;Vx(MJ+%r6YWQ)s_`=Z)>o}E*qVZ7tU=hDHhuoc0=?bkbLGl z9Y%d;d=Ox0){!74OPyudt2Y^d^kBHgo+04WA{EseG~vaWYrT9s{7o#8VmJ6gt;Q{b z!bLE&5TN9QhMLdNlHz*jyR`FBWQnhB<7&#d9yGg7x%@xW0Rc@gQT{i z$Ct|Q_K!1z&T>k(MhPY*Md%yGA83xWe!4}8=Z6NLfgWnItUA1F66zCv_D*!( zbNLDxfq$XR^u7FL67Z@&eHj`R>s2far|)>=?~5HIP7Qz1*)qB7kzQ~A(ydiWm8Y|R zo~HG__+(Qiq{xrLga4W(>=(U!YCPu8P6hI%4>%=mL;JJEZ$-4O3D#P{S0M-1%^=&` zvs7-pEZHMnk;{8~57s*m!db+AzV*Ib={P3Ek<|IEmn(%TUnSA&U_FDH#ij%$@gWAf zH*5YXo=R<2anfgyq1hhSb$I{mWp7x*rlVJb?ef6Y1(szF9ISPmX01^kW>%V_AE(xhKgOvv>wewZQY&pxdw?P`z8l&`65yXRmY%3t4gOF82iyf1bg z*C`)`MVaOinMW`&z-*%`kwBTNGlk0$J@)%3D#|HKv8Ti8!G7~`R6+L{%pHLl?sHaZ z+TR1yk)Ki1XQ3?@cKC2dzJ@wKSOf{On<@IWltDaQq;nWFs;lNpE7VgUz3matC)Xge*xV&2oyX2K`SS#(E}U8EWd>$A6t)Z z81;hAB`kv?movf;>oc8a_2cPTomsT9_*?Jn@b?_uPMVJ`y8~Iz9tLblxe6#M`4H)s z7ya(~GPni&34d~Ql2AWPJD&RW>05{D?7uVCzIm}SIbar0=k4K1^Kb}+{-kdH;hdYQ z-IV!UsYcr}*n&jEhey)THs-KhHGms=ux`!RzM9BuYb8kE=IRNwIhaw^H7APaEV=eP zG@C9|am_xq2*IJvA)!xun~E8k!dqrEF( z?pvMrH)55-mJ&r0Y42DNn9HnV8p`F8H_v3(A$GHLoMxI==-Zz1mshTxFK#wL3Hl{9 zZhI4Wjv}7y1R)Mu&A)%ER;+Vne~Kaq>nTa-Z6xar^`cV`m8TOeFY9kmyC3Sv2)Tzx zMjiO#5mb5C@fH}QRGEQq=bKFW*ak^b#e_7|nm$ZMWMU=Bd@G_3*oXM*J0T3zJZOi9K%4`Il`3eM+3E67ph%~@&@5}A=;|kCoz;9e zU<$C9XEmry{vGKY@*GMY4#H&m%iIh-=pxm~nayOHIhe>lK5;Z?54Bw){OA`Hsi@Y_ zS`|EA>x?uml`w`!+!>mLZd@M1s#EIh*31SVyW#<81T{7n7!d8)^wGs3%heA3v$daB z5ES)83|0PL(LAuR=~tCB=aVv%`A8VE#pM;d!{@=a{7DOqB_fY0!0sD?Ir0WTYZ(S* z0!9sYhK)CmblUsbeJ%;rJ>lPuW(GYJFVxe|uQeWkUZxWy_B}g#8&7+0dWCeHEH#

2xr|dh>oyu43J3bWlHSlI@#w8ywQZi5$0j1GIAo^G#u^yuV(PRjB@W z7z-HX21>@-q*Pnm#GIFo(E|KkRga;1tT7UCn0q`mE%PspfUrWY^vHVW(=H;4%@IpP zp&5$xn`9nX#d?!O_xeQcpfQuCwG7tw?cprqj$=6W!zqn+jYSJ{JHa13wT$4-)UWMj z0#V1yq$SILVFP{lxj=uLw_@EF)**|zebi_4IlG{s0K;BbKw$NQ!w5(+<}s@Q(!O4z zFVduVx?UzJ>n2tM*%aizvAdf*c4I+3aec>wH>Sq5$(y*WlN}KsD-c9I7$$h zA>yQE&F>!+dUy0GK*UiR>2-^Fas)RJak85Bb~RFUuSd}^Ll_W=uTr{zvGkX0pY(oU zlIQV_@ljxe&sn-LLCN*qA|1B9&B<847*6BD0SSay{`<4Yumbw5fVrscoG~!a18`^_h3!q+PDPQh6I8KNpQCS zK^q9}5F~hT4estPZ;^A(_uX^et@_@d_v-z)Rb3QR?P~U3d&!(*jxnaF7DI;?S7~}N zpC(Lpvm$6CpT+6w4(GPcHVc23O5Pjoi^$<`zsgSU#u`BK!0e$lQ?IA1w$-{G>r8{>o6U8;VW5q#-@u_& zm>SoqwBV!I$Mn;`^2#7H>XtEG?wp{cGC`J2;`;9T)u7Ml{PhSzS@X?9o&7PWlyXI9UA{b`LA>rz*6f`HRT)n&9AqMGlFZ1Q7=bmz zu57a*lsUXBvxEBU7UeN3#92;AT_%mqZ6)sVj#9z_^RZ~%7L{GN;d@5r*;2V8=1Pd{=beRkbZUX;<%8FusI z-7^Yu=AdCJGGh#C;%SMhHPLT9;Jfk+sIZrg%1eQHbz08}Lgn2rm9d)aBRdvb*4`!E zHjSj$`HqT5Z&O|V)!;%KK@ZUZCwfMQtsBg`fr6}O{Em(1o2d^S?|40*vt4M)9U3yK zSR3S6BuCcLWNBCOxXG@~JtLk!wn?7)wSqo-I-2LgZMtQ7mY>r$P0Qa58t8~!H5F_e zj5_M=5-A++8KcSD7%`%`&6(FacCXoX*bjb1jH2Uj`Za@8eC@`ZIoV;?Ew=WhQ_yO_ zg|}FjszG3H@Rx%S=jmeTVGEvHwnk_j5&Ev?mfAg9t6Se@dVjE)vpbjgsSReUXgQx^ zr+?K~VRD;WO&$xApd$pJw|=9Jro}-#gOorJ6HZ zqb`9DuFsX($uL{bcTM*_XUkh>>-i#yFH}`Nc|MO`bjQlgC4W85e~K@BGctF$--MYW zCcMr$R2!o6DUNr{*MHS)>T~;vU0&7dz_Pzv*ZuXW?&MVW`UOKpFzEm%Q5% z0t}toobV!FJ>mQQ$7AB|KaBMi;sB3qh2>?dxAYvl!|Pu>9XLU}um4&%H##W+3MmfxFCo zHHsfE;=N{Z-*1(zaygH3|0+wJncotKBkeKs_3WZ{3Qw>epCiXPVSquX&1k{i!h+M1X4;eZB_Eh zb@NICz7<4p4#Qyw`mYg$4we%7b(l^+X8mbq3|aV(3bd_P`*oM=2>o#hA=k~)QHYvO z*C0yNMT^-$t4cAoX8`rrWf7)6ZXuBcQ&b+U=IA%6<2{YMoIfbD8f0^S&3RGdd$5%b zbdHw84;R%oM<><-W13tgc~)8j_Tx^5xHB9QzuLV`tZNP9)T;Lo_E18yL+x#O|D<8n$GWgyLab<9j5nE++z-VAzQD}S-m2($iGM_*Y4jpIMzv-?bPdspISYVnRxxO2|b53v5qsr z$wcd1N7l;vX{SV*s=T&zTWzo6aqk}IY_63;uoRz zcarZab^gqO8K&TT-V%%-t(YZ8!8;(t#Xp@pcs|$5PaNQ;f)bsTzEYUqIhklI)io=s zEgfhl0WV=cJLSBV-Ux8MY;~|>8BIE?9Xo6P-Ftr_sZA z^=WY3BH!xEe?Hwe`eAw4J193ck8@Ap);%Gzw>vY(ip{`e-Cte_=TS2808+R0nA^=|@&EF@Cok?11J>6mOd{UlYD@80=Lt|^*Zdqn`ZE3rr$HnxG$vxV-B^OEK!*wE6 ze!w*EIN$ZNGG-~Yqy0em{PXj9Ht;RJUUHELIP$7%J1-MPC;ay_6p9H&$&=%3BEMF0R zx`%X*_qBZ5rODo|7C#7^STKDzDWZ>Jz*iLUHjdTwL+2TP^X=E1Vo{i;98dU>7ec>M zD|CnOlaZ}o2m3zKwr9kMjtEj=bNA9TkX2yI}{DoVk1Z z=8b}nGv_@LXf453DlhFl+Phxh4oDhb$UNL(YYV9TkQNX*GaB?Gno=PqiGzT4o=>cd z&xf2Z0p*^+3pRsu>SmpKkn_1pKnQSKY*YSV0*v(eMw`-Elinz{hB|2 zZ+H(Lg!Ap2M{)OVE&&fC!F2ntr~H@iL&kgux?Tt}oBY1yKR0(z;Q#8#)dESO9~R|m zSM0w1;hGAK`oSxy=-2rpxbfpI^q|whTax#W@$alICo?JnRoBXQbX$eF7XdY8{ewN` zdWK$qY$JdD6mbQxJlWC{;X;2UiXtQUo?DSjq*(rKK?9#j457SBv+J<4))|f zaTE|Bf4jN|i+gQZ)|IW8ai=!^kZ^OQb7Et-7Jc(nA7LR7L8b5&=%kcE%ev6}vEwe- zBKQB=x&GY0JiWli;Lq6w=1J=&>;WVff=E|Lr-bHr?&~p_O@1YUbwu=a%s&Q!x)6rN z^5`zHVGvML=f_tNg5n`W=|6WRgglTeVpigl1HPG zEvs6fnQ^i^*WdLloJ#U@bF=o#BSSM|M!Zi)`yT4h*X6qJ-C5rXFt)K>F0>B}7TQRU z703~ezp1p%AQ%zzd~BPe+hVzNJhjxVVfr%+qZn28`AAlkgPF5^+h9*R3Clm$8X52J zJMh1`0}XHK&)stO903}?*zYgV(`e;zIINF1u0NC6tics~`S;-$_(E~3kwzbl{vNHj4XSnH z+Hn4aa;B!rSn(Cy?TvtdAR?c&bYGq04os-fwa9zX5S?~tAC0iOukLcXAGvZm3wUDW zT7;@9aff>S_?JE4W>Ec0zb;uFJ?BDNq_zO;hxJv`TH@TfTaE{B7X+3U6SYQ+X*EpWQUbpYkBxe+WF6 zY#=@(QHIPFE(n-KRtH>kDtAEeYT7pkD-HyuN5wVe?3LzrCHs{fW-ix-2cn@@G#T>A zQGB!!$smBrkfYG4w5A13J}0E3^FJeac#BOGqRUkdO!|N?ldlzQuqS)K*krkf_n?z8 zdKEMZdSZi6e52mi+9Y%mgtg2Dd#SVit6JP{i6LS_(|Er1kjiZEtJUioyR$Ae6wJ%z z2o4+l92&21Pq!WJ_Jh`7jS{;NA{V-K?pSK`VLDrG%Q#l$9A)Op&F$%}UpoDL25lLV zJ}5A%Qw{4=R`f@cV#d9d`dlj6nu2mTL&4sl&6Q2E^rT8!pX6o`6^LC3MeyW+;UB%WGt- zjAV~05=3gVejM#9nlOGwP^y?Te-=4b-AgiFXMk6Gyv5Dx(7_S8xoc*&tnlR7e}zPU z{PHR4_lgC~#Ye65X{Zp;c`IXyexlC@l|Lv(V2g065K4#p7{9+a%cRh?`##1kkD5~T zRPDQMLo}*oFC4Io;y0Mf>`4uq3*VO*j98JL@n{{s8T!7cUS=UQlnYT*v)`!}9;}k3 zG@q`+SobV4IWIf38l&rZM8MOv&N;dhe6sshb{$u_qOiJ69rOt?-JZHEELL3e1&3xRVyrH+(AWO4O}+U@ju^9kbD=8Zv8)f35;uZU@PAu$FMcmN z5D$3U1pV+D0{X14vhh9k3jxUJPSLgR=1{CNu=o?cOncOmKWq-ciGL~kT=LnJ$3*ZC zU<@RyRmF>=)t@CTOzqQ#DKF0B+EwaGjHkPeUyU5X@Zp7Yl=A&g#uFQ3#fo&ZMRq4Y znWf!lFIERygUTvv!bgQoTDvL}WGC;-ZRka+IFG)SikW%5_F_(SHJ~L9DJV)^&AKi& zY|4Y1Fp<|yyOn?Lu26X@Akit*xx3I;o2&ZFD2`E76h0}lZE+8zY$l8xwjPxc_6v@x zb|uIMoj&`H6029HepTC__;(7^IIKlPkok0K{62R;i$P^JKAo}7T$A0sJ&tmh%{2<5 zIbveN(1O>N)e_upbXmM^b!k?-XB`G>#G!U+A_4xYYKGe{$yCjIS z@JONX%fs7iqm_ORd`1cM6Wsj++f6<}5D(DKINKrDXt=IUk47YReAU4K|L=hji7nPU zT^!~%Jg>;c3ZHV_mVw^2At&C7^LWgsp9$Rv4vCpQlvzzsyPbZD!5z+WVrQmj2$}Q* zEnj70I3kDheo-9<1*|&Ox;d2k6I{>UV>2Be-|KkJZA1msCrSjyW6L^KJNvzZ*>;T5 z{%cdE=@Zx-S`TKW>=wJ^c&nVE(jIQ_ACxMjw{~XxuAQ9NFvycn8E+*F(!2+YW@TDw zX{@%=ULimeZ2L1I!iD~YRzHL5hThmBCg&jSbyZd5}1FLJ-y{RvI`jPw; zzC|(0bwaY#c||{qEmnD?!bs{$<~=7{m9l!mi0o_gb~xq@t<$>>b`jR~jl&$>3Cy30 z4SN>!I1N{*#tWI>EXgNv#1$KR%3FPVs!}Tkm%cEc{F=1mN-JtJU&jz4o(ekf^%u?> z(DmFsKTZ?Uf1W9I#^F<{>#&r!t`13pyFac?Ca-4O{ER399lW4kaSZA?N4vS0X^LN* zgQDnTvxsdcUjJ8?^WVqBdkI;PV>J_KKY-ky$DQ&T0$u)0zdW(!pikiU?$4B+FO4J} zTOC;AIO=VFm`(+7Un>Wji&S-o=jv$#OTo1C+i98I<4|p7&|VT}N+@HpV-L-3o7Hr@ zwPU)}0IT*ahgR-0!-vArZ~CY1fsC1ptE<2kE&KD1mA*V0WvbOk^n|D zC6=45Y%Htqs8V|pAfSpHv{s2^FLggx$d4;%II?KljegEsBelu8<1?(lEY)!=c~nrc^6Fahu)9|2Z==pb%Do}bOc{rN z_z^W9@FTOQ8BUafm1ybcMHgGcUIwpJ@$~f(t2d`oY+oqkXX9CIKFWutN`_|z z&8&51Y=UOyE8+5dpC_uGC}!)cnSmx8n_UIJ-g}Gr>rM*V8h0+S9h|K*pfkY;KI^vJ z9(Fq-BI&MnG?j-*MZXdKrG5DHjiWWoYaZH6QV^0rqx9Z`e#ftsm)rSJsnf;Kb~}KX zJS}b)=o&)i!+msXqR_+VX{c)6k>}wM-x_GAK6#8o8HiBQ(bm$u&N>b1*xOjmbSqw+ z#|C8Ux14EagMk!5LaA`BQopw_Gh=@Kqnm8+ zUp{1vzk@T2KZ3IdkQx-7(?GU7)3+O<1FAC9ouA0#HBbTI{-*X!7MAi>CrR20XTtHI zLYSvzW5254sr>6#mDSeQ?1QQ4(jR}eU~4_t#qH7@%0On+sdT?x6djQSioXY=Dg;k> zP5YZ&ZOS)$wA`evi$Ci7G;@+gh4ZcwanW7vjM}MFj7JU*GpM}2sme)jw|xmn|G?fq zP^7B#Yt-trzmn+v0H==p#wQ0+P?FQqRK4h+71_ER$AR+MEj?m2Z#C@HB+e?t&O2C7 zvr2q!8!5K_r7Rfem7z*x-gibSWJe5@a3`VIiCmUZbnOP;r_?vtzEUc2*FGxy=ZLWX z9ufCHM&u!6kw!T>U+;akhxfR$!o3a1z`F7)wL+F6gL6Qfk_siP7D!#&JI+C9vKzk9 z=}5lDN?ua+AyF)nvUI5ts_gH772tI7kkl2%GZf@QCf7?21v~O!U~hBoY^A=%ZmzN7 z9c<#$wIEBYDf0y5WU_Xbjc2Vy`gMxXhY$i2Z5Ml+!lMvE^-iTVC8DuvleGvN?ReX( zHeyrrpK{~-i>fyPUGnG!tq8l5-TC2+b%M}0shLb!K~@ugTC1n!4bjJV!~<=x;g6CL zoOWV8j?y=X?#EYUb|ZK!*KpFMaU@zkU9)8fr{lj)3-CJ`{qJ!w`DyC_yxls0b$ zfiM*(&lyL!^mB@~wZufVlU3$ZW_{-vhwZ*l$Do)uG~G#Tu`IBz6h~g|?^1HN&0O`(!HPUKQ-%U zQWfkEuD)s_@4{iNHVUR0Osk|ti{K{}v6ePL%EcgWQ>P{D!KB@&#a(8rbxodVpLXs1 zzAhg6{tL?9L1S#wIwc6ZX*xIA=#XgPrA-Rt$OGaod?Z?7`;NQ>e6}9}In}sybwHj! z^Ump>g6|_!YRUc=LKkPrJdpg><^Fvdu)*3b_b4UyYQ3L2q)t6!8%%}cpUvEL!ssw7 zw4Cq$V6E5{gQ!}x8hs)m>|2?6eYn-$xXr)+l$+t-PXe~zlfe0}#gIddjP4QC!4p=EU!d3{t;kJ^I7uT(3Z4ocgx^hB*^jn?h6%k)5W4#z?iRiq^e1= zunK;zN<;63u%l6^p(?c+iU^FPVm!XfM=No8ei$}!R?lgN*Z(or4hE?%D7MJQn!xCw7cCKqO}nqx^~&@)A0o9J5K%(N;E_4d=VKw-pM+mKHDZdilu^k zn^BuJNHI&QCEg<|PSTt5+P<+}=TUCx-a-`Z;VR7K9DpL(3iA+_c+sRJUam3O1wI63 zh2A`FN5ZP*Ec~Esg2vq<1(_G2U5YuBs~VpJ$t9nEx4XO8xrE^c8w+QEo??}4EV^$x zt@Vc=Z8;!GEbAWfs6>SCRTM|BkLB`bSKM%4MH_78(O=aD_hNoGTll(L;@a)%(gBFI zZ+y$A%K-C_I-*MVaHWr&=7xmoJKAZz+oYzQrEBX`({GS4qLc3Q&FFBaU~ZmfrKWb% zT}D3B(e;^y_g{F-A1JSg)o)bh1R%Um%Xe_|4oH@+?&XGUSZUS^E;e^~)5!iwmK={n zh=&qpfuN|v5*H_mG0_@{%(y5>lu_<4(qt_4m24tc_!6Tgjnn4s#_t{9rI136x8qXi z&3S6viTJNno7v3-@E9~BHlL2xzzcjw z%wIZjE)4+V4JyYCXbJFI9;`L?jO`VyYYW9+ZJgO#q*boWk$$w`ij20BpX1=Rn6gF- z$s^UiU2L1Nht0mVZq+u_lfa^eFHGm6w@yGJt26fA($SYw(2h(wAL zj*0XNeGF$O_*pkN&WDEuL)c^U77IT}hp~k{bPA2}P(x@|y>OOo2v|d4EdjiT(4XJr z^B1lhx5reWBA<$?%kC8%6)FftTt1(7Jv21Sif(-U4=7~eJ9Y<5uw#iwY zY!-t}nD)Mp*2sHPKOUOe)XlkMJL_p?g3jX+WTLhJ>$|(?&R?D9iy*}j4t(_oWvyzC zPx9{O^mjk>7L?G)4CBuo1uEZYc1yUdR|m5PllmYyg;t67kB|k-969%K_4S ztPZ?(55B1!sNKs2G%*FAj?%nh>)ObV9EDex=f?ppJy?`-okb5kp3qr>j#zGLrQ1J) z>-gtAuizWX-&ZzNp?rPAX;ZFsM*2IJ z?0@kaK4QLsxNqR2RkFrH(#rsE6F2Eh$6Sc@lYx#_HI>4*Edl2lil??2A{W_Fwklka zZ3w6Bsr1b53Fwq`LE6^h*tv1*$K8mR4ra9`F|weG(*?3we@U>$uNs5UZ2P|mKUx2d z@DopW>~cooR=O&AoHAF(pvE&H8)KkRi>3lW1kD@ZpipI_eJaNx$@kVbIEZ=Je0x+S zj^COqLp~a^SyjvDaS?ML{h4U|UGF^i^QT!7ySTsV4kuo<70#h7GeU(x6Q<&|VfL7| zhWE8!s9H`P8w2;WWtXkwCGkGG_l)5Mpx%D-gt`Y!0f{%z3lxAO)Sa=eYK zG@Ax9?5wgk6Nh#3;cy)+0gx@Wp<$C1@INZ0612tF2C1|4efH)MB}toQZ~zg;@fyG1 zX&qmP>gA@ZVYPVU$71wj#)dABC$C~|z_{n<&(w5!Myj5HdDt#rRyd`6p9RJT&6anz zi&a<4nA+k%D1Kg!m-#hiDWTGK%w6hbjsJ!zAb|*##8Q8Qx3r_6j0=Wm^^h#J z1hgb6 zqvL*;etMed9g&@*mxF04$kaT1(mx#>J`-{u{X7X!%B!>C3lh)QU`uD8@|VBCV=|}i z(egFOECrNhJ45?V+WC_L)vJvhXjF1yVo**3J${T^jpqgLtem|^d9Yp8*mSo?m8EoF zh3NF3aZTv&xF+*=TvLNYYo#bK%cAJ~pdG7iFl7BgT;^_#%lykpPRs>fRT>MtD6Yn{H-w63Bd?r0G@Cv= zT&O&3MMCCJ`P!G(pUbSjGNxTVbb69fZwGw(ZVKi^E|Sedb2IDNN*N{NGYCG1u4$K1 z-^!`0Lsh9564Kfub&FiBmy4B&?wgv`9>E^hmj&2an3Pg4I4BONm2F~mJTHT#B5m@2 z{!oIrtmTv&nl0zk?fv%||2r-E*Mtb4|Ks)1IovdsLqXFUGnHLIDZV1J1E=%h?Oc)! zETty>%WY4q$oTO}oF=pfBDe0;m^1)Lv#(3dZe?)QuDE3?^GDpc=sSk=fPcIM#&_T) z;N4;)$oT-Oho_)SP?M$!8dT;F7cFW$F46*zqK$7-kWz786=47e({R&3yJf@*+EnyiwhhwINx|ng%2#@r{ zf84m9Gyph8_Z}g}L47;SL(L0^J-8S6DSHNu3VQx;sAfeBvhj-9kJR#^R z>QC#4q`hnz24!JgyuKIzC{Mg40f!U;N&Pz|P?dgn#)3raz0~;cYJUO^$RHHuYvAC) z2O-$4{}lhgK>nvP`H#i>f44IEpE>vc$DCuj|KP`8UI70o+XMLdKZJ*WK{Wr^68=LK z`R}sCpLxiCPK*Dirv<3RvB6sjZosS1D4?)R>d%l}|3p0g5h&OCyphqVU>(@0`5nxQ zAH#ZXU|x^WCh}MxGs?2{Tp~MFjN0W z!O8Q_f|JV7n!{?e4ue~X1u$wm5c4_#9*AZx8}p%iKpf!1yWtMY)Bcu(M#x2mu8Po( z1ywT; zXY2K=G&%U1N_6^9ZpDf9AMhIS4ZK!M*BFTOQGRc!NFACfBN=$?s!5}h?b{_CMMs?v zeVq)-sQP0k2hSG zd)OsMcTu808N{k;5|jHrF1dLYO1Y#o+4wT3mex1N^huD*lQ#Z?0rXA`kksyz-?6+w zxALMXpf}0yeYBOuzNTSIuKvbtw9bpzS1+aHsnN+bTHjlvDFP!QSb_F7pk1bfB)a{$ z!0pCY>VB?bK|wyX)wvO#t;^=p@kqVGBo@hOEu!dPs#+}{Ubhs-W)0}5mjb)ciH#meGR-TtvnNt1jf^2!f zRR98rIN#Br_ue1a<#oeK@HyR|z%O?FAhFd>+n8Ee$u^rfxLbu4!roDMrwq#`&lM1yc;VV$ZEAo4aKAL+QG@sC6z34kFkECCNVK^`jB zZtA7-_AL_aA_3x{sA^g@oTKSh4q{v`)QE>vP&r5Uy zj>$kzR;vxBjzrq5-lPpo<{Vdls7gYPO1=rw(Gpxc_-&v5$Y6<(&z{n-Q}Np}@i`S> zvj3ZFBEW7v^`%)7vvX)2WuaHPJ)H8maJu$fnmKW^q6?vwTeUOg`kYDgnO!x91Se%k zZ?oXc<|;4(xZ&ThaC=_KQ%$&SkIV8LT^>?1y~R)Dbqc|JAwiPGZ`-gBIzaTNeivVv zYmttS%8Q7hkbX&{&f0)@uwv6s`RoeFZ`5DLJFQM8bwp9m=F%uy1`~3Zi8HxZ>pJYt zF_t(@-{vCCY%Ow9`}{(3<&t3RYs%Asr+w+?JjlckaA`%X9*+xxf{FxK{G=g>NG3s{ zj&Mr$#qRS@BRD7ytrXI@8H3r17;RBp_9ssXd2K}=q#+c~4op{H*<1}~EBTzB9-@#! zS6Kno`EM^&m-1F)hujPOjT@pJB_#@En&={AykwJ1L#ImFW^K6CyEn;xeai31y-1ZP znb({JhDqC*ZNIR>j6s{7IJr4iixo+wpiEe-t@hy=n=219vfJFd?V2jxP4yuvv5!xO zXBTN91EV(Kiyq0^S{b(t3Ehho5FSck(@dDmRW70{wI1oP5)$?eNqdZc7j~5}rbGn@ zSG*P*&#unC%z56V%~cNT0>D6#vX8m=m`PDC>5-|1U6Zw6$#>rna7~GRn)JjJE zmow)_!elZ00Vis^{`2NzqLPWs$nfbRb9SVMeVrb`JBNM6Fs!-k+^PQkBpn2*SEStC ziNB5DtvikY6yFdbYvsEEY;A#vm^llSgN5oZJ;aZ6mycw%dG&6De=SlG(XXi$YeYMv zy6o1A7})+8^Zm&^>*i2UDs=R7E^V=2 z?f;ptQNFU!{tAN6he;#`3f4tOvaf4^!tZi0MNe89JBPfvHd7Yg7n*36%ikCndg-wo zg5Bh7QEb>Q0JH;Hv}8w-Qbh(WnQE^n6{Ti*GyfvStf@79XLv;+6(QkTR|Fi$ach@) zlB9N_bn#Mg3b=`7lxx2rI)K*_7b?AlGfa(M8;3{ z8f5E{%JwR;Wi3%K`q*-{cv6EB)jVt2i zVrOJp2;n$%S!uDRHmB?iD^D5JBdSzh1?~3YG4?l04g?^H%kl8OZ94fh?BdduGxF8? z@S9D#t8#$_bs+w+gC3y|5wEMnci+%dda?%oEU-NWKs8PmUtbSA+xI9)7|Rwv?opha zgC`kr07vs&9%5qt9tXm`-JeOx>u(-#C zfU?p~g#F{1`Z3X9TRJJf^57j?g&`P;ZSS(pB=3h1UlE` zJXLel^SPVDZaOYD{tdZz*}=SH6Hrf;KBX;wagyJR8(N9CM(=<^K%+9|$M|z7fLmWR zT4vt%()Dmi6?PhSb-IQXtwi?3`cIBcL%h{$|6-?dhM88ZYPve?nRk14LvIKJ)a8{1 zlP6b(bo}rHX(-PSgj@BsGIO4@+g1W~#H{6ZQ7trLZ8)7cz8z}t7{B6-;#D($T^VOj zM0xXo9DlctGEuRc#OlXOm-@#9tU~wJ(LXy_jMeyRdtR;{w#W9md8o~Xd;ZU36^@jv zk~8w6CY{|W-r^RZ}m~K+Erx>HZod!4pn>_GVwxKZ89oABE@elyo?i-%|s=l+B<9NJv zDJU8g9q!_TvXl;dQ#vifJ?qYzVUZ7-Cv-d)TCwx(sSrMgC>IH-nHpcSn;*H)@ypRhGluOpZM-(SR_j4@ z>;R@^;{AUTS2n;x9Saco2){`N77QELfa7brB23dJdqZ2mJIL3UMFI)!zp3eJW`Nvi5|?Ui8HMC%34*%3cwizH^Qhl zLB^m>z6@eUr_96;_<#dh#^Gju^<^~ZxyMs*ak*E@unh#yYoDNq$h1@~d;JN(ewWbj zcI;1LtpB{*s=3&b6Cm%WY&6z+X0Va-{m^r(MwAt#Rh9Fyg|1Wk>&x$SGX@&|QTlwB z2m(ItPyR*djUBoP33O1KeQt+UNw?!F9=$=_t zN3eZv(AB20M+x;=y2$bH@cEygoB_HybxGl{y&9-ASfC}Hqgh1Io%F3C&d3<=(*eUd zCQ+I7v}o;l6=Zk)XWe7?PNNcr6r=UD$E(?R>{m+zP^HGkM(j6VKC&8EQ)ssrNH9mg z87P%@qfr`XJ1hX)OIvll)<>(RM|srBIrbFP#UB;~4f*-E&)2Ys_bKn}+Q@-H*hTq3 zAzh^}U{ebWK|a&o-+N7Yo)cIwnRR_xs`3!R)ADu zI^iS+D2WbVM;IwRg=AdwUTZF7-fqY}f`lkbEUdG>`g$ z>_$!wFOTC$zFjeLv4oN0M7cm+yp>d6KKAjt>WOA-PjrRd$&P~ARB`Q>@44T1X>5Aq zxg56ybv)q${v<_Toe&IzrEbJ(Lu)vw85~XAf`Sv-)3-)Yyl;pvH}qa$Ga07{+VL%v zt@7)@=k~5dNK`xlYfISY7|J&*nwbUip8*bL-mn4u-TD3|q;HF?lE1_(&&_i&^F|#t ze!3Dc_IWlodpN zmF`fPtX+;`rI%UVgt;f_9V-ld5=!13k3VyCLytEz>P1xx=C&HwPJ0WcWR(Dj^SeSoXd3XUYsyK~v~42$~4u_~+n z#<9wBWbAopA#R?Kn@4TY|9G@1i9E@x2sV43vW6*-p76>oXZ-MNO@303F&D6H`;PrW zeiW+}8-Dp0Cm8#a9}4O}44 z{r{8KM!V%(M~1)cwDB{JCc+7cXBlqYo?P+$XyfrV7c!|oS|gE)~{HvY=qr|K6syPFJiiCchdERSz-?Qwy)7AfpxJ)9pyVhEqe z3n$sUy3)iiFkfmDG;2U}*+1p(k>^Due{T1=L=5Q#vJ?Xxk2r`N1R3*FXo_nu5;O5hwCF5dy7&*Pt_Dn2hpYV z%o>|utU4bpcf57W4S+|4KMrC1n;;0`7wEHLFo++|uWOb^@KGD3vff+%BQM|vFp?t~ zhQSSF$03T{7+Bq8sUqpp^%bsz$dQpaLMOVA94XLAzfp;|WH$$3Du5FIC5p-!=a36W z??|PL#Q_)GGS_uuS>g0*yrC^3v9<=Wzk1rM2pP-SJ3BovaOIoG0Y5@}S6>;gAjCZt zKEDW{GVT9@O|Qo0L%m49{lh7v>p8PR@)nI&B77um!Z}=QRJVum;%y; zi~)a+iJR?x4Tb`7-3K`=cpJ!Go7XRHjq6R!7<`HAS=$Y=5SRA={@#O^;3QM?1rcl+a>0qXZT@qGp&S@#}Jy{Y~uap~4= z$s1Ke%@a`a|1IFVFYER2?_9G#9{SyFFeUDX5t!To&-?~lE%0xz;|K!-kBk;582S+b?4NF~+}j+jqe@+s>psW&M50%?;?@`TJP> zW=5a^jZ(*ZZ~dPyZZ7%H%>l*x{|6(t3MTbL`F5H$U={Dqx20#wK0mS8M5mPNkA1bg zx;B)nT(V*u!CC9(+k|IgDe0 zp`3v(^AQ}9WQfPFfYK9cxHYzq1Fzk_)#84rVm4Bc!T3$$)Af8Pe;@Eef$2OSLh z)-&oeAGwS2hh6dY67HGWS)haH8^FPP@e%aH@{>#Elr|msk_vQ*4v$DY&|8D>LZ_U` z>kP&}_`Yk{fAV(C4K%16T-Lc!2Yb|xGal@tZ`QGJkhe ze|;ql5Ir6BmvY!6DI%jWlXaK0k<=PKS2#mMIckw{?B)v!yMWJVx>Cqo;xK_W=V<$! z;E?$$Ra({m!mTWi)pYJ@=f&~# z_D_J8`q#T-GeSP_U7ZN8k9xv{jc4+Ei}YL4x?))rGi9um#_dh1#ST`cy2$LODt5zH zfixxJg`~b&UrmvM4)p7<{T9{!GM|dA@E5ZB%5v;;>#hwcdIuxv^;Aw<3CfxF4_zuW zj``BIYHj1|bsaY+lcb^<3h>}BfqGV+MZIiIV49dqXuI|t4G?;JqSe0qNanW2kWFS5 zKZ2c?J;q-5ybi*?6r#MUzIS!8PYMW#l53<9KF}+=Twq1;q{b6t#FKw@ zpTWzYIYaBPS7M>h$$U-<%?1EoVlubzZ0}t3bT8uu?f#y#hNq2wEl_U1DrJ*I74cG* zhWcgAPB`E9hc9K5KPkS*v4LZoDL10aDxbK#q+Q>Cp@7Eo1)#d)NXfAVg)@dx2=MrT3P?4jIXcnVo) zD7{Mncad&1>u>Ih;GL+p2bG~5Xhs=bZ~2A?-1SC`oqnbrf#Jzdt@$OdrfrWCOlMi3 ze0%t9fRN8N0%#o=IS*EbKS@PUooPDo_dewr2`CY|iyWNN{a%p8kKcejPh;yTIxA6m zTq*v~n=<4Ls1I^c_0C#rt0@AJyU3x1XRjH)+aKrd?z??9=5XFPLGM31 z9GQQU6G6=@#kI@3)F(mj-BzTwU#87}ed5+|O6>W0STg*@?ny|h3fA0GkVmrL=sO9k z9iHv{LIKQvIL15?Zp5#3;0)12p0)v)K~Jgc^gfjP;kYrVfIPFT07T`3Uu@tA`urDl zJUSRUY4dGC(yxqVK5NrSUHn8n`L=(ki-_rht{ zSy1{SVYE97Wa3tV=IbGmZ3gVN%_L`3y~q%4k?FT_x-C4XytOX-kmsz;hl|P6Iv0H3 zfS}2(@-Ly@29U_!)UHNl$-4QjS=36LImd{P?!c_yym;~!^F1gv{scPpA8tEk`RgC! zUOSA_#ntNuV4Hz;Ob3iQzZJU#IquCKJZ6^l+)d+bMxSEjmW}6&2cibL2`-x^iH=Xa z8Zm5=4~aDOdbbjjqzmWHO@Mqk+2xVYz{8`bjG85R8aUv6VtOUHCB+MJY%zr!W8{t2 zsjP?eo2#;YyKxyX(X}pJKV+k8?KN-d{nQSB!ykfsQ!YK&UR4NEnl>PM4F7C%yv0ic zNvoOs=6RLKVH5bA&6Fb40h7$Ol5!vrK7^xq737}mXTSBs{laBhBNf)P%vQ;$KPfCd zUJGTk6{Y&5+kYg;=2dGsU3^b+w#l-1MQj@%Q@<@TzJ* zLp)${h<_*{Q&|O{U|W>N>m-4GI&s*{j#j%7VKBK+D3VT618KpTzazZm2hvQGSB#vg55^jP>?5 zSpXtinEmxEn=7bCMB)zJZy|6XA#GVqF&6Nq1x=!o*)#2yJ=1Dj_J8{8-OcydXFj^4 zC$CRgi*Olt=!#)pEgjL*yps#YHTYJ(^>t(oNa1{j@2T$b?M z%gv%A>&FGLXWv}B6mYeqW3t(xIjYf14Md`~4zSjRbyU_oJ3Bt)Wg~r2{SyT;HQwq{ z1-*+-ONN`LUT(I88tpI39Cag34rNqlQfi)*pf4vsdI|Sl%`4HB+f?evxoulWvHZs5mYzR2j0Xbt-iR``_PrJw1f4gMMn;gcixDYo`pF%G6+% z%ig&5bC%qG<);Fd%NpzD`3W(G#Q}W><(=kbcm4%|0)h*N&ay=N)o5w`#Y}VTeNSfU z><0^4DGgau!%lJ{xwG!o#*M2X?nTUFU-vl5HV$DHu<35I9F^e zS)JoCUI~eZ5Jcx{?n=2_bXpXDk<519ZHnM3y)LeKzEGCWy$bv-$lh$Q1ya%4P3&IDo+B>F~i_ouV<<<7XlhP8%r9 zOjX&0#~OJ4+yOd(45QD5<?op?84qr6GYfR4vy82 zEVdQ+NPQEzugM8H-iFBq91j3H57Nwgl&O=IQ_(>T&R8K$X=t^?HTjwN``DrH?bvQ+pID49oYJ9u1sI_*5UspJD3|AoHxB1xTNL>7wtC*Bu#hKMnb1+;frRdOQ8 z-q~BNF6#3m>Uu|(hEoVXw*jSA^ZA9&4S#*H{X2|*uPQG7k_%v*Xl-(LG8 zr4B!VJiSvv!W^%bM48qvgQ1Fb*wAY?6k9?9tofzxc>P0Q!A?VE$z^r@7TaT;&nq0dyTpz8@(Ln(_G)s zV7Ye&-z*)Otce>s>^Y30QV^L6I@NZ$F*l)^6KL_r5N@5LRc6xhKUTLsHbv{w%7eZe zDK;ZGoh&t1*?;~Z>2!a&_3G}l#1;6bP#KjTndHE83EW-n!F7|t!lc8bB2*$^Ef|%2 zo8a44Gdkt!jq76lLtLEC2S>9es*vxRXSlyL-ru^sBE~|AloxZ+582=zH|$ zJHYVFbG7P$p?2M8S}PJy)oi*t@lvb#!0uC*UvVrwS%^JHw4?Jo(^;qLjMVxrKCFbw zzoJ7><$!Zwbh2bkq3mogN`JgQg^E&RU|R@Y`Rool-A6i|*P&8T1uv?@D5S;WIN_fW zTmHv^wD+IkpTzf@)_d-dQD02W5o-xmdG0vxu%+__9dAv@J8e(mBBGN4aN_WEuC?F{yp}^3RBV7XwQqrX&ASEp@bjL7s zcXxMp%rJ2G=X=k&>)gB6{RhrEYt4_dX6@O}e)ju$zuvF7B_tFv3_)XL!W+}3`}qA} z6=(#Aq5$ECxe)b5A9$?FIH}9%Nj)RfJh}RNwKyP`j`N(rNi$%$vF0P~_#R+76AF@q zkq*7`3q!x~V;uVRI`B3+t4qmu5}nyo>q6^!F`Eh~UOJ3n834sdYqdS>b*g@UzIEJC zBW%zqDdDEBW6O^kKVYKoF9XU2U{slOGrs!I687VZG_Cfqycfr6a|Q(OL$xgB%GYSC z!#S4^TGI5W_7}5ap5hSqa@0u&KE5n7gI;qKMhNeJay?pzkx~P|NnVUelbt*M_`F>|@F2Fu({VGrSg2 zY)}*SosgHu*wQP~2dC324i1?sb_HEG8KgG0-QF-qBDT*-EG?-f1L9?J1%nA;$s$Jc zvmI$N$s)y13tzpv@-}iotNv%fQtYxfNk3k5|8y}sCt~XZAdOqEMs*o0cx40;Qo<qy7T%$&Kc1o2&1tCP zd1^8ZXsT-_D1g^KTfj*5&rLcy&xLxTN{#l*QFOoW1}^gOMWP@f6L))W_#&!}1RI08 zJm*k8YbDXn?RRZ>{E;^4-L2=3W>VJjB8t8z3Q-J4Rm5NVRf^8Ai6hvcqtskrA(eSaiD3sMhDR?~$K9~W3g9Y?Zw}@5 zXd7~N=C9rMirls-Z-f`?e?jptHnA{(0SWKc#h)$yRYpnu`GFMiECWq9gfm9i?jx!B z^>4$G$vI&yeW?^zO8(#D%7s2lJRklA8Tg`zr^I6@U7sGYc+*JO3l%MueuDAX>m%hZ zV<6X!K-yGh;1d1sE|KsZeGYnx7YtoBtI&`L{<$(2Q4A7s_8>M>Y0lA<*aIn^nn|?` zVE-w&O0j=KnO+bmssE^0fAT{x{2P<3GqH+-CVv;%+b|A1wnse={Sd-F2w}v6AVRXD zqT={>`NEjdN&fEcVNGF^@`$hOY~$t!BARhc^4^djL44jhYm5+T=5#2?ep87mUol&H zqe%1@x;{jj9sW5yK?04E_YATby=JXY<~xkPMtH8MZh`kHq>i<|lj9?LgUP>>PTAz` zC{GX%5#um&+FT;;n7BRYmp9;Q$% zX;#^ZzODRH$w=j~J1@^YQMeP(wjO3aI&T$eh3PW#11p?mT`=l1lljIN-}_5NLLo|j zy!m^@{T4r(pB2S{o12GP(p4wa+J!0lE=&@z7}fh^&H}_OmhKf|s7sEA`uxe$5-cQM zwi6~^Hand=g^N)YE?mQp1YNaN2$VyKYMZe+ zUWLLdr5%z5tJm{#jH1-BAiDC?lRBnG+H>~D%TPya0(xAIbo}1n@kj_;R>Q2 zep2})WB?MoYZFWu33`S9d?3B?c{9?Wv~_B%8X}qRQH3E9i(K7ZEcz*dh6@fHDVajb z9*YwDv;?*#2>ZAJ!7BISoU|)q;6Vk1h|qw1+>^TRj7}tbab(xKsGhELNvHKOuRU{W z-w@-S?XnolB7lZyo6ED{HU&YMq8t;kw5Oy0h`VUfES6A7-d3HB$Y|1yP zI8SS?7!;M;>{OZF%R7nhecYty$J*JA<6}wbafWmly%6OZh zH9&VgF+};||>WN+AY+kB>ykB7S(m$hky!A*SpcC{Np2lR8xF(qW^2JXN!JgchSv1=M_b)kAOzm zW}(^V{5LidjK4CN1>;gR@_(^NEJ}bM(#BdP$NgG4Z1_4gbuN>~y(C07cNbRJ`Sj73c)YX%wnAr^O#Cm*C;X5UH+a8>ql-Oe0Le3kcH@h8fU zn(h3R+Lf@o*K}0(VJYM;AA;ryW!sm*I(c{~lnU;nP?OSI;{H?sCl#H?Jf6)%n#4=| zz?rICC%o`lyxH2*L>LFhUrfXWj&gM_PiY)Y1}yy%&VbRRbVgG%Olqh|ucjE3qR>ZK zD4HE>1-+zzJNf>|Y zE9}URffYv+Ft=mjpd^VXRnRxS3~xg)=49oGZ#s0Thf`PfoTe_E?_U-Hx$!t-E=+*e zwl85&l(D?MnCVo^`i;X-j5Z!GR~at`Zm4aOM(xTd>C)LQH1wplxUBre=?h{A!(|7b z#8YC7;Gj9qf^~IODI9Z7?V{?%)MWlL8RO6SgJ{e;KqvMNyMNfP7UDPyAI`PFqP{{< zWeF%E?!NPsIM4FmF2OPx>&|~fM18~JN%D>AfJ!sPtgheT%4jdqY|Y>Q<}u3uU8J9X zxx2-g@4(j*@={F7TXsH}EbcK{M82|a&v{42Yft0L+l6h44iG#ftl<=)tX<>RbJj9a(y85+VHTue@X=9kl$ zv5{}0@Y7iWYwd3SnBQ>=I#tR<%uXKDVIZ+lhw96h?p2h4hCN^j3%Exgl4UR%*Awnr+$Zny|J~sljxY(G+t%EE4caqc|fUf`@K)Kh2R8q^A=yH00$zsOf4i3c-o?7Vme#>0Im}7t%Ku#TB#KY&S7NSip}X(G zro0@H0>L-R-M&{JCH4XS*M(A&EE}%pU&tcYxsLR68Ml|&Cx7ovpeh%(Iw{owmm-eK zj*6jfbHn9H7DB}MA&@N|#JOIFC4pIM%BOr1d$BDFbH4CrmyLsxM^Y&wyoTHcdFssV zWzh$7|1#eZn(86D>G@tv@=4b}BPKztbwvrFud(@+bzgNHeHJb($Wu0Si?P6FbhT*4#XQRHU%iU@OuK<|oi|NN0RWiAysS*bb zdz%zBMng7uh>RNa4OC-$W)L<-?bm2H~4HiH?DF5$2 zTdsGWOS@0Q^6@{l+ z{l?^(#EBIK<)BkVb|t8K(Y386HosM%Q(|?g^4rsJN^QGV+yq1V+xgY3pnU4=1bH#7 zlc#A3oanMIg5&gq&efP6*e&Pe;TZu{RGIU{+_RUNCc)RrzoDIY2G%NoQHB6UT4wOq zt4|^WPVA#(l9z<}2IpNBrd7Kcci0oc9|*nlO|=x#XE3c}q&DsdE zzF5Y;vEf|httjSiXWYB^Cf44AA-NFn-n0<6uYOg6F??)bo}&_?Ks&op^^WGzTgEwX zjc@2@$66~DUp$APm@BvFN;Atq0mZ!|(-0yN-W3L~Yb@z!jUh8#>F1^TRlD>T&JBZX zTr#=iqny%fbS+K_?!n%=1jz5uTX7wLg-?+N7z`@%!&yYm+P%UnkUQXn#sF+ljS^UW`o`J9I9*nHDTFT0Qptg)<;MW(jH}_>| zTsoC?UzO9?ZEl>xw`4II(C6@nj&HkuylNpJS-xT9Hgm>_XIS-5PtIo?M_ znA5@%oz(iM0cJdoh;!FRKAOR8gd?Mth37(rIjD@%Ob-2HO_%w*iZ4VG>+YMe2DLw< z8Q4ci*Kk^3`pr%T!xaZlmkr@Nb5D+$0w^EP9b9`QcrnK(Q+^Mx86lb8O83mCM21?u z?ZvFdgHGuzqkrM!dgU%bXWH}$(S01B{9tbk@XtC96{$Nr55jaO3~Ut9n@&yiI%O2zA+uBAnWIk}`C_`@yW z4CF5dc}Sw`aJWx!y_Lw*OTL0F9G_gOTZDg*T8(vmsy6~q;ap9A(F8;s*tZZs{0bQS z-I^F|%p|F!zMFf__#cSW6y_x0gb-2HdMtg~9Tx`Q>XyK|@VocLSkMJYSy?W5dgz8`LyEiZ@!BE!uOSW%Pw|(Mh1`Fs$oNB@YKD0DW)v+ zk6ksSJybF%UDV1pkzOX-6qz`)@mp+Ju^ooz?4SmfR~I*+Dg$f{tG$wj((g$Z(u3L^ zx&jxer7VI--?)iHKEO6`bPP3qB)jgE4-V67cUfC$XEY5 zo^(w5g!lz|FY9{_wQw}$8ERHNKk`GQP%8n;U;bSNVr_5cR}pPOCaObky&NHL?42di z!Q&M0=B%c)+=w_nyqLSS!-hjg+^HeOd`maNUXenjE3>fYHsK~4qNg}8fjydtt_vnh zS$YcZ#>gWK$z1@IQO!G=@j!#;zs^g^7mq2A_M{l>1J1AhEs5}Y2t+)t?Mr5$INkpy zM$w=AyYnlfz1YQp=-UOSEyynDhGEjZsJK+~4@SR)J2Hp| zaL4EH@&M?*jaDf^^HWNnOdw4f+Zk!dROrdqPPtstgi;`)k!qdiVm{>>Au%=+eIb3< zUIGazP}|EbFwbL%E>n6^ap7H8ZT#x(zFdl9wSJ&fYjLc!1m`;ExyN#|>7eURS!{3G zz_v@TaV!Z7-jEXg3A;(rJlm;)f`X64)CL_`K>xLbu*218A>6X~nC$%=cd*4$$TwN` z727@X=_|YK5dpqjZiRmfO)MMOSo=<3t*9%)`ntLB zh|f0FN$kv5aShO%F>_zFfqn`=Rvh;qPT#b^va4UiOzEls+pkS_1>%n-;7>;BoO;u* zq*I>k4sCwh=;V)F>U&q9=vAjsJHbgQxmskzkka-_LQsMbWcYRQwqsUCdApFo{->!c zqam>Ev-#?AS%Q|KJ8|09HPsb3oFM+*%?*m{mplg3pZ?|P*C#l}w2#6bboOm6%QCZt zN^+tf`y{^l46hvXQ{1IhjP@~#N`Fd&_(G_A>1Oni)+&TZ2E-olwAkGHPr*R5N~E06 zGYa%qEZnZ;>~u#zi?Y2zyLyvXl>*rLNyG|~>UrXW?WOLOAG(R`1+g)iE zzphbwxzk4h+1aR0jr82)A-rAoiA}KK#S(qap#9xg6F}PeuPWXVev!D;*{4*9oNmU; z{EMj^XE`)a#Nzuuy#V4aBlhR(2z5&IP|2&pHm5&IqRK#(n)^RAf@Bn!hyg0T#!JPA zYwX)P`{zogK^{ogK!Zw3!D~|N$xv+Id~bl){=6S$9wSk%C>Tn)60`(%f8k_!yUj&~ z)&n#4#OjewnE-t9>Ypscym?fbp#qJ?YIL;+f?Z7HCV1dheR4`yMul#qJQgw$($ksbY8sb z0ud4DVkKs3zyD=oNz3m}_@;@=DP(q=0P2A1w4lLvBG&$`Y^7voj{H%KD!QH5_(mE$+^xEd< zN+nb2jm#X15cLW~?o1cq9x>uGqnJ-#)Q*{Q#&j&zFfu_fn+|Iu_!-)Bn=3EmgSil; zJ;(A&Yu}7<4AW=1>tmO6>tw9NL98u?jo(QD6CbG<5NsBdt*N37M(8Tge7EUez2Ahm zgppd*64}Q3uCkb(2&c*r?l8KO)j#*2O1_1~7n#g6ut>FY8%Su(If+l*R`#u32ymO0 zE>ymD%cjhAthQO;`s$=4QH@CLAhZ(aU36ZAC7pWztqd6>p`9Ia3Hru8-9PoXbi-6* zw5*0|m1K@w(hU^Gt@|2!g{bNzR1l435S~9ti}wwbcJ9S=jP)#s(Nkq5=$He~Sz2)< ztVC%g;PCID1J@k)*ya96f(Ulaf)qa2nCZ!!G*s*MW67tX>6b1M$~uJ;{3JccSDV*U z=WF#I#)0|%0#SWDSqqyBV+O1cd(b3jZx4rKqI>kjxjp-orBDlj_WL`7(pr4mE&0_} zU(ryL3Ahcm&gIT$ASjdHo--bYC76)sbEAyRPih&mxt#B@$qYPypH1iLmA3xc zA%;r)O($H7vPvh5C}__zEbH{I?>N^Yeb*V|JyC<~@~wvsB&hNmdo~5|?|ftQWC*~5 z)>?Dfm*Kg6(^Rok24(22H?+B|XNQfW$YShNRNa_-OX3i^&S?!M6xqh-g&vX^Q&f`h zsOC@VD)JZ+t z{7rvvlPN&Sfxbu6JbuTDX$I@fnVh*?W>Qy&mYc|iS4WL>;ZvNW=!)`Q_56!rdSXTj zw^-*8pnmNq#(TiX%kUJjvVXbTgrp*2znjhka|KK!K!fWgX17J_C8kHq-%-8{mU>JI zznzs_ZC0*JG<(8OZ4&HqdG2}pJrD$p_HSc9eP~_KXp0$VxeYf7=BdlNLp9lZS+k0V z_QD8jnW7mGsQepL7VhdX8T>^&VP9QaY$1KRg-yBQrvER5RpVRykzOxIyYa z#H~;MnubPvhm~*Ugo&6sXMMiDg2EIyM?SMK!A^RgBvn<1A#i$hHccCPz8-+Ba+imhYkliKS2kzXna{cbWm(3~x|Cr<*-BfuBY*Zb!SW2Fl_+~CG>xWW?!qx0}S zpzCk;JlSKMUqKYVKKVI<{vD)o{yRV?GpvhU8mMVv|Ep;@EUOy7cpRKJEVUnHd7~~E z%^ndy=?JQj^l^D}kH1TDcaZQ9-@!pp;-Htp&UVsMQ^)rNM{>5*SL*NYG56SI`?Ynx zTR@=Q?U^~hy#MW5!gpXo&t~pT`k%J1{%R7?l>Sf?ayyg=c7vfiwZ#%F>|LFG+s;#Q zfX|d)ZNFGxSzk%a0yP=edxEIP;RLDy=W!y^#OV*7BU#^7ciZM3gRVe8MV?O%{?nCj z2KIfSKo!1sjfo`uoaf$f={>WM4+(AA9B;y)Rm1qW`Hb&E$lpGi!#S}+3Xw2ehi`Fw z)GtTaj3uO=?+Y=#0eoq{B6cS|q8-C{HwEfPcw+RHOuB;Y-hrRkWAQLJG0}(S1rsC) zy|3RhIE}+=vF%i)qNCrHx>cT?US(x0{LsUZs4sn7r57Rw4ya}U@k{=0YN6ORWDJlq~f-fXwJU=i(lD8OKIPe1X9~CN|hQzp@_Qs&8ie=%k z_*OM~NIR;#eLG%?mR{JaD=1crTYORgd2axW=0?Ai4+qpn2Y%>;RHV*H|Bjolm{NmN zSbZQ-s53iA8?8ul5GZC+L&)oNoXp>{3#m|wAWk!FA-HMYHKw|dup0ESYUhh?V|JkG zm}<~V5E_fRtK6Rm_VGpv%?*))BZ553eqCEch}}No-0?UXVUhk$M9(BUYEOzQBeN<# zs8ZT`LsPXIQj{Bh+K_6*P(x~Wec{cVT7Zu6XIFUFuy&4)I>%jE+}s5@8qTH~>(eQM zgEinHaDf;49c$1x;>2`f{}?#62pZBDDF^T|?3w{O@Qu-l%#QSF6hXt+oLkvyj|Hy= z8h$AYK|bjK^3_hfwi`lFP2qk~&1>~1#|R16t?uFJp#qC(GS!ERRb*I`id{Ib=7>E+t1#fAwY z(M^HAsWi9EB;fT+^*gHOe>))1I)I$O21z%Y^`1t=_7L8VYq}OpnWj^-0IjG$thXBo zC5w#woUe%2qg4kEh<_vHdHnZNDyd2@@@f%ns{PCXaj}FV+~r%FfN3f$=;lA8nPES< zhVWh^30reRGQPewH2PlhfhX`3d@|jcy&#v>_<<<}okFh%v{QE`?X^}%!{?yJM`k!G zQa-gtPyGaluuS#143^<q^1-PUIq%)XA}^p+=t29I-80@&S0A#xbt-<{E(I$62WEeOwN;&S+He$;3TK80JmX5n8+yw z0d4Lf*nP)ReeOwX^Wo1?N!F)~%*Xr>AKpGd)SEo@m`t+D=T!G0ubC@E-&`$x6WvCC850S&1>lv45AMM!`U5-07q<{CzSFCX~nfsMgoW}^x*=y;` zFcdI*zDtJL)r?sY&31@HtD*;BanxAMZLoetQ)II>V#jzh_zwVG_s>~oo&*}h z`^Bt}9Wg$%U$;g|{N7N_y(5D!>rt{BU2#j^y*<<}RepJJo%61R;8A&6Arg)~qScAh)p~CvJDaTuf0j%PV**&AaPg5e9Ql zzV#NbCi*^SJh*O2x&zoo81kOcH}x_^&$0Nga_j#$DPamV5#A}X`BDY?Ww##c55+&3 zYg4FLmVKZN<*8l1UuNx}Vc~fJOx>6&A=7HM69tGfVeidK{LSF|(&{a(t-)5`R&w0*@wuFBW;ON#Um4i|8q+~EnL&fKm8X1&yuYek6C87wv{@ej*Xs31#lJ|Lgwzu*$QkSWwL~+XR z8pThzaSX^EfwNVF(8cbg&=%ap-#yS!r3_D3B+ElLFB?r*xd2|cqHOpk5)m0)T8^ey z9dV5*e+%8_C1ci~vHapIgpc^A>Jo!tJ-n+JKD^&%2XE>PP}1cm3Vrq4IA zy^enVwW%)DMVRl8Ex*nBelcFLASCo&KE*}#WM*s42P=O41UeroH&I* zEC1^E{QZZ+cu$#=Iudr}(s0E(v#7Qvwq{lqmgs%7S>Mb z-0|?8@v0l7N0HOWP`aqBp#qiuUZ|OoFXCNk`Pk7&(a$uV)Fsi0G-$8r{(R=w&dAP& zk6+iOO8guZpT8}24w1ciu~0LAM|P169A}ieMCofxf@z&{YcaUQj6=oumj@u%+pYs- zvY+Ce3{*9MqNZ|X~r?PF5*G=LQ@esWvoaPK6^ z3ov2CVb@BYu-n#v5fssq_(`Uev0uev*xVQ37232m`!!+hDul5{@(ySYv&Ex*rcL)T zCuDhIrXj?PO8U+?Oyp+$ zTmrct@!w7PCiglG$Hp}|3E9rh;x}v}Na*~$?`Qpd4norD7_T-Bx~}NOw&@at%6#3% zY1OAYUiZ<1pwFXRZqaJVscY`~`+k=ldR^MT;bM1vCCI!JL7#PN_GpSC2|AGHwj&M5 z-REa+zvHc5@n^}XrN3@8`4DBO^_X=scqf$6KI;a3aJ8VGHo|aFS`M zUS4MIB00U-T13+MxqCSRpxX1T6nflZ%?9piUwNt3H=jp&ndl`U-uG)7Adg-Ft3x>r zzcH(Rw_Bg|s#7$hpG4Pzl+VqCsJgxfJuXBsX+pH}XnLV% z4wyhnpWSbzA-Zx%KO|M8e#P@FrMXcC<X=uCYKTbnH~K!%;@!9A1sK#aaW{@@}nqs0J%mP0EGuR#rFyPX+x zevPj!g|^9-KCLQaZ>5m>dIdn_p<`eE<@JbJa@<&WEs30VUiU*W&;2lvbej7D!& zI`DfAmkO$SOQ;L5)!Zd0@fcu~XCjzV&eT|p*6t z)7App?u__1UXlLh8*R;X%OnMSIPLgb;F{-(lVH1Ag-)`%x~H_^>`8^|49Aw|zdJ2s zipBv)#l&{zI|X zE$0^w*BgOtX3(?>VPCp8s99WLUn#?!pYhUz*4VOrJ>S6^T)jI^0#DyPgDXp&^gE%0 z^+x|l_(qfA785llc8e$DuMR2EHNb8L!WelIp4E6K1N}v!6IrT{PX;61!p=Qvj&U#o zr`Oh4`s?0~ijC847#f5-HBl3Uut2H&-=_|N5mDDq^51=Gg{r8|Ksa5QNRG!NbkTQq}Xo04=zy7{5SQHN0os|Bt0;@ zd=akq@?IhiGn>~Cj5B+ldNzDIpsunbl0f(5m7dgUGdkcG&c)uu(htuK)h_}ED)y6bGV6|+;fRhZ-&o8H6VOCqzXoE*Y9 zKgG&Bt*}SA(p942oCkvLC!0F4a2?KgHjz_&C6(5?H`yA~C=K6evg}Il_9f7r(cUfZ z;Mm`K!@nvS%K3cGKTuJCWj3fC|7BRpvsyszhmdl$j?ZRO0}Hq(Hxk&EFp%36=m`0Z z=VA>#G z1K;BncIR1DkDugl$Kf=NP}4J36`A70+BMxOt0Cjr>JCxc)%JYT$M7|TvdMpQ;!@?A ztb2UM@`w#zzr{Lj--`*+FT_pXtQQLMsXMul{Pt9wSS{qkoUG)YLdg1^T-xy%(AOz# zS{XNchA=;G7{$SsxJfH>h$ZqZbg5DOe5DQB?WHj z(fXwM3Kx_*hf<<6lD3JL!p0@KkI`6;S4$o>G*bUcW@_#mF_#UUZ*u75rSwyebs>1@ zMwcPYZkGLDK35pFlS~fe2ib`N9itLMDj7A6T>EBoxb{QKdhN>Bg(J6N7+lo~gC7Fy zYH4wL(-vNp&1=)b#M*Cg%(y)F|nYMMKFocB15UvE{D;ePTZC*IIBU!Q7Y2U(FYLNSz7WaZwwv z&j?O|VO`#YZ#~AeTk^@&&>v|%$YSF}k9MOTeq@!yo6bqNmvNQ-!eS50?rLswv7Yzk zp^ibdy`XlCiN$9M?b5tQFP_>nh(IPg;f~=;r4Yb7$;(Y`&@bQE9rGm@TWxf#b-fte zpetMvBz@b8Q};Xjpv?~o(Pw^w;yyQa{FiL2!%3ZNwUzFdRwiC<*1>@Y?bh3rDozL7 z>ck6YW)6gZzxJ<2Fib~q>K(rRnUI&pVVQfj%)nbcXrJuBraj4c-3Nc|zvt>*49k=b zB-=$XAk6a5$Br7$iUsL;rY`E!VS-iZzoBo%yp@^CiLplp{?trDR_wH&UCV;*hx~5k zA@j8zb$-f{Z8x48_-|WH-5jfm1It(3zImJWXbp)~TQP8ce1o|f@u~Og$!VIp3MRR5 zX>ILLNf8Jo;-mt@>J8tSvy*yx%sC2R8Tx&Q(KqF^>~S6=E>))1T?ve6*UFUjOhQ?e zJPSP&6ZP!IGZ%b}bW)>yt@UAJ=&DpgqvnS9Li%cAs*}o3jASeAS)zomV#(q(_vuGQ zGXFdCxli!yqtw!O1e7+tB#EMSch3Eo5WNP$Fz;KhoS_H8-h&Ta*p4t)i8_oec$X|I z0%tVxx4n%nZuHo5FKCxiXd zkOpK4xaddyUa@4A(fkKLq|c3V9#F-0Di@RjrR zJ4P1H0>exkx=vhwb9)WLqw~9B4yg#z9OF zCz$p_^Sb|4YiDJX{~?E0zupk&G8m4CvuTx<6S2*yj5btURvi92fBwXUD)nh3u{Xe6 z321z-I@~&ZOv-FPRok=t?joF%m&6;(9rZpPqYCBTcIDh1)IoypcV$!~N$=bU`Cn7n{QAu=dMFGKnn%jk%FYVb|*|w_CjvD^=VgG!RI9z=@izZ4&GbWqY*uB!(T5# zM#r(#mszb%?iXjNVBOzx*KXPeC5#@!wE}3pg{E?KTp$jtI`d_iz5&`UGjcc}jmr$q ztf?w09gpwXue1RD0r7Jg1H-Mf9hON^`*);7Q#28C=rP z%ZO>7lV^8xC4Px}Y>OgrppXYm+}^w{P%M!-6||429r&5ElFQMLujPF`h9P6sHP6;RhwGHe%^nJTM-`9D(7~2XnyFqyi8UbVk9b#c=d4BG z?t3!0Pb^PxgJc=Nevp4VN5`Wl=Ym6PrQsHP_`arJ@O49JF_qKb<-vdjC`Kp}FR$xx zG)>z3`F_~W2q;t}%5J8yY_=YfO!}#rRq#=vwc9t<8QzBLC`me|>5Z<8ONc@KYDa|0 z6xF9<1K!H(fOO28wULW1u<%})9L~on9%fCQVthx(2)}1Ir)%u`Ekk!0+>Z^HsS~p# zGQXK@cAxQ91F?ODWjU6Fx_Ab)+hlBXr=?~yK5myYR+1;z2e&pwKk!H)gZr;HJA`Hl zZKO~!O7Quc|C0yRM$FPUE%k7&zrOS3h<-6{-M#-wW8Ii4=*0;XlDECJC>y9KmiIvy z5BKT&m4I-eW7;A}rxs5;38fufJ!S6^NSc-qLaU-1$AC}eF8AZ%w7_; zrLoKJWXq7T44iKC{!0KCK=y+>(X(>5j-J&MS&4F>^Q8|?ZD?*Ozw`Z(8Ll7ep5v0T zs1ECkyMymU1-4b=F{7<>u*c2HtMVbl!w*q)?x&FIp*I64oFMi0PgSq5&m+rPK89#@CVmw&Xa&ipxIB5H#L~IK%LWPBfs+y zN=7r{>1BFq#(ye&^dd@sw1%3AhZi>k@5N9c$)-+6xJ)k5{MPAduA(-^qKUw-NKdYe zzO`nUm+sp3Vc>!z@Gy2!@Pcyzd&2d`=~XH^1>yCf`&j--XdDdh%vte#`)|+By!_X* zp*aSQa@Pdpy#=R3zeWnNbA=x}bcK}2^8G@Hu|kYmmw+qNiRX~cL&~5^^k0fPT*v5? z{?KV5xV-`uC|*T}j*h)M4H>b#4ky@R7+EKuLEC|Z3O~*84jo&y<3p&tMlxBySTIKe zaEGUt!)SKX?wHlz&*iQ-jS|G)Zwht2WMdS#HtBxSUtRo(6I1@pVK@8cB^K5W4k?_1 zM^$qH8r1&BRK7~z$)iA@*^5E^)NSGGk^qR3H-)_VAuB3JlwDPGt%|DQz8QvZR{fNO zR^%V#388tV77#1+@)>|`TDflxN$P|%d}^@lqmX`f^zW1KYmLKsRN*lT^ldYiPUc7P zwFLlM$FF2Hq!hFosLB#XTa!>td@dMYtB(=m-uH>Mdrv|4rrDYWUV6H5?UdCz8q3(1 zP$FXNeGo(9Obp5Y0ls-&m^M4O0G_LYn5Mg5X5s4|mBGorq1;!n|#H_f7cs~EK*auR^c({1nGd5beNZC zi+W^FM!I){ON9{TcX)Z43B25Q_VHaPfk(ZuMBD)_}GmuutiM5 zb-R!tBEEU%`}BZ6o?rC#yLaIUuk27nzo>^kS*5z^e$;%RnVALve#b8QCF_wRQDm<7 z>xapF&l(jjIG~p6x(cQBYnKKSg)F!%`ia(*5FE|_Fwph)Z4d|YIE4X|izY$PlpO?AO3oqo*Kiv2O72V<4cXK^jWz&WT@~5+ zl7zaBp}BUPY~$g={Y(0wPnn9RL{nG%bz5s$ycAykDltlAK^_XdtX}aDxYOT$sxT4L zRReW;mHBDZ#nq0fzvr3ZskZ>6}bJ5GyyMp9)jn$FYh^m%8cK9B)7{Y%H^ZMuGeE}e;CvsQTkY9ZLt zT%!Je*H>VkEp&37tq8!giiVM46!87mALDY-fme(piyP%M!s?~W>VGgwnS7n8SM!U%RWmAfcqs0& zIZl#j^=ItR_2rZ5xWO>cN#GLfiJ6-5=;<6P#}2J1bG1h~4OlPa%j1&d7hbfjlk0(Z z8bg)EZ&zChnHc`KV@@MJ`SK;eWQ9~r_MlfKH{*26vYS>IXUv`hl3KkM3+!}5t}dO8 zifjy7*#qs8%KhuhLgCP#EVob*8=AfnD?lbrtHPwccP_292RilYE$QfaV|?(6-@aiB90CLmvvfA9s0HG6ThmG1E4 z$MuQ)wKBtc3c5C>OZOHKgBdHJAGhzgZ(ALF^#n{%lxc5-7w5Ip7EGOtFpXzc3tfl& zzOCd5#>FK^DyerZ2aL!Z9~iLyimTt2tWT8gUEU&x*Puu_W8SA4w<+!Pufah?t0b^! zx*G#Rk-3RFtMs*bZwZf<^HR&rwwC>*pO|v9XcdX;mBF7WE{qqqnIAKuOfC^y{8&I*Yz^ez9U8wo|16DEDiBMUmon1ATN`eUT+Cc zZSS49y3Fzti!;3T4O?1N7Ne_Z>vcjW(yLzz7T(8RA8?;m`rX5Vsc|TGW4=-v{Xz+F z-vs-4q-_9qj$ZUK5an|FxR#-McS{3O(fyKq{mx1%?K<%G_HyosfYW(3N##&O9R+3o zoFuR=`le3ZV#LK<>-eLIvbqr zUli2Vq}RBLJLKl6Ame_USbrMM*&oUQy2oQ7K2hAIWW^{swU8{9xNnoT0b!ZKzSlbS zOHxC^1`wsyf z{0>irxu92Y20H@HRD5?7< z(}X-JMm(83fczO4fuf(tg;@Q>MQZ9U?Zt`27iQClbVXGv)3`eWy~1Y`2qUDPXKx{bE=VS6Q7~Kb}y=1hA$p2Ok8HQZkYnQ zW0k2h_c=n4MDjb&8!PX=c2Y|g`LMN%gJ)j@SpDf#=*n9uj{thQSE}UM5sd!a2Gs@O zydOnLFZUVxo84};hUODZKGI^S>|e`Li=O^Oq0+4K)cQx(w8(n&D6Yma1C!=3FmXpSkYL;?;xIKB% zIp{wXyYu(%a>s@U3a?C~@TyMR+?6KhWq4vvo*WyZzb5uCFM~lERYP&5uEA2=o)wv= zMVI)@puxRhM1Q)>`(p$SKubpy=VcK9X7yUXCEJc)zqC-_YyKC2&Ay+WTA0^z6Xn{M zGA;tY7qd^@_P9l@V4aB%@h!0Y^@~G* z>=c@0?HJRQmLRdWUm-=b`TjCaahh_r;fmLs@~!>LZm*fg z|Ho=aH3O&Zs;IoNsM;V(ArR$wy79FqV~mcy9Bh?Tv9ZsMqpL<<10Sx93`+_p50lT? z;`~P=4DSCZE2M>zO7O*BP6J&=;qP9I#N~0eeK)Borhl#r&td_dqvjr{G(ZglA^|KQ zqFhrw^4|hKkJC+oh<$*cBKL^zS+-8{Wp2CvyRy~_%P}LAXM5b? zTWY2kSX2pVfIa%>I@|c!`VB=v3<;;hW{*o?(p?CS@aZOk*Njf;{lhrCW$MkbAsM~1 zbKB3x_YR0pe;?eLdhlMUiRMdv&gRgr|HV(h@R}hFxGeww%Vk-!aJBWQ{O__1{&!jQ zVt}so?{@GbB_?hBzq|nvLIPZ$9|Jd{Qpi&V^C%l!y8Kw-L1wgh{?_@{H)eJBhZ%=! z0D&*4n*7gQsJnZ4LdphaYN5*3saK24s*fUS0(5^f^x*lAJ{2Jzz_9QG zy@Z?g-lT6KPSl4*%KqeHp8D<9(tt-7loYR)WA?jmerka9Dy)VCSpn1BfRcaD@mrdF z(K;oIn8<|N8wOp#oJ0gI_+`+keTq)p`QH!L@cc<ʞoM@nJz!4Gdd!`!3F{zlnt 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

-$ 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 Date: Tue, 23 Feb 2021 14:14:54 +0100 Subject: [PATCH 23/70] [Search Sessions] Fix Discover doesn't clean session when navigating to Context (#91874) --- .../session/search_session_state.test.ts | 21 ++++---- .../search/session/search_session_state.ts | 11 +++- .../search/session/session_service.test.ts | 21 +++++++- .../public/search/session/session_service.ts | 54 ++++++++++++------- .../public/application/angular/discover.js | 3 +- .../tests/apps/discover/async_search.ts | 12 ++++- 6 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/plugins/data/public/search/session/search_session_state.test.ts b/src/plugins/data/public/search/session/search_session_state.test.ts index 08227a392d79f..7559a6e0fdc67 100644 --- a/src/plugins/data/public/search/session/search_session_state.test.ts +++ b/src/plugins/data/public/search/session/search_session_state.test.ts @@ -9,6 +9,7 @@ import { createSessionStateContainer, SearchSessionState } from './search_session_state'; describe('Session state container', () => { + const appName = 'appName'; const { stateContainer: state } = createSessionStateContainer(); afterEach(() => { @@ -17,23 +18,24 @@ describe('Session state container', () => { describe('transitions', () => { test('start', () => { - state.transitions.start(); + state.transitions.start({ appName }); expect(state.selectors.getState()).toBe(SearchSessionState.None); expect(state.get().sessionId).not.toBeUndefined(); expect(state.get().startTime).not.toBeUndefined(); + expect(state.get().appName).toBe(appName); }); test('track', () => { expect(() => state.transitions.trackSearch({})).toThrowError(); - state.transitions.start(); + state.transitions.start({ appName }); state.transitions.trackSearch({}); expect(state.selectors.getState()).toBe(SearchSessionState.Loading); }); test('untrack', () => { - state.transitions.start(); + state.transitions.start({ appName }); const search = {}; state.transitions.trackSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Loading); @@ -42,17 +44,18 @@ describe('Session state container', () => { }); test('clear', () => { - state.transitions.start(); + state.transitions.start({ appName }); state.transitions.clear(); expect(state.selectors.getState()).toBe(SearchSessionState.None); expect(state.get().sessionId).toBeUndefined(); expect(state.get().startTime).toBeUndefined(); + expect(state.get().appName).toBeUndefined(); }); test('cancel', () => { expect(() => state.transitions.cancel()).toThrowError(); - state.transitions.start(); + state.transitions.start({ appName }); const search = {}; state.transitions.trackSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Loading); @@ -65,7 +68,7 @@ describe('Session state container', () => { test('store -> completed', () => { expect(() => state.transitions.store()).toThrowError(); - state.transitions.start(); + state.transitions.start({ appName }); const search = {}; state.transitions.trackSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Loading); @@ -77,7 +80,7 @@ describe('Session state container', () => { expect(state.selectors.getState()).toBe(SearchSessionState.None); }); test('store -> cancel', () => { - state.transitions.start(); + state.transitions.start({ appName }); const search = {}; state.transitions.trackSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Loading); @@ -89,7 +92,7 @@ describe('Session state container', () => { state.transitions.trackSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Canceled); - state.transitions.start(); + state.transitions.start({ appName }); expect(state.selectors.getState()).toBe(SearchSessionState.None); }); @@ -108,7 +111,7 @@ describe('Session state container', () => { expect(() => state.transitions.cancel()).toThrowError(); expect(state.selectors.getState()).toBe(SearchSessionState.Restored); - state.transitions.start(); + state.transitions.start({ appName }); expect(state.selectors.getState()).toBe(SearchSessionState.None); }); }); diff --git a/src/plugins/data/public/search/session/search_session_state.ts b/src/plugins/data/public/search/session/search_session_state.ts index 28865d125b8cd..cd2561d52f00e 100644 --- a/src/plugins/data/public/search/session/search_session_state.ts +++ b/src/plugins/data/public/search/session/search_session_state.ts @@ -68,6 +68,11 @@ export interface SessionStateInternal { */ sessionId?: string; + /** + * App that created this session + */ + appName?: string; + /** * Has the session already been stored (i.e. "sent to background")? */ @@ -105,6 +110,7 @@ const createSessionDefaultState: < SearchDescriptor = unknown >() => SessionStateInternal = () => ({ sessionId: undefined, + appName: undefined, isStored: false, isRestore: false, isCanceled: false, @@ -116,7 +122,7 @@ export interface SessionPureTransitions< SearchDescriptor = unknown, S = SessionStateInternal > { - start: (state: S) => () => S; + start: (state: S) => ({ appName }: { appName: string }) => S; restore: (state: S) => (sessionId: string) => S; clear: (state: S) => () => S; store: (state: S) => () => S; @@ -126,10 +132,11 @@ export interface SessionPureTransitions< } export const sessionPureTransitions: SessionPureTransitions = { - start: (state) => () => ({ + start: (state) => ({ appName }) => ({ ...createSessionDefaultState(), sessionId: uuid.v4(), startTime: new Date(), + appName, }), restore: (state) => (sessionId: string) => ({ ...createSessionDefaultState(), diff --git a/src/plugins/data/public/search/session/session_service.test.ts b/src/plugins/data/public/search/session/session_service.test.ts index 3d49c91fea44e..32fdd9b6a52b1 100644 --- a/src/plugins/data/public/search/session/session_service.test.ts +++ b/src/plugins/data/public/search/session/session_service.test.ts @@ -21,11 +21,13 @@ describe('Session service', () => { let state$: BehaviorSubject; let nowProvider: jest.Mocked; let userHasAccessToSearchSessions = true; + let currentAppId$: BehaviorSubject; beforeEach(() => { const initializerContext = coreMock.createPluginInitializerContext(); const startService = coreMock.createSetup().getStartServices; nowProvider = createNowProviderMock(); + currentAppId$ = new BehaviorSubject('app'); sessionService = new SessionService( initializerContext, () => @@ -34,7 +36,7 @@ describe('Session service', () => { ...coreStart, application: { ...coreStart.application, - currentAppId$: new BehaviorSubject('app'), + currentAppId$, capabilities: { ...coreStart.application.capabilities, management: { @@ -65,6 +67,23 @@ describe('Session service', () => { expect(nowProvider.reset).toHaveBeenCalled(); }); + it("Can't clear other apps' session", async () => { + sessionService.start(); + expect(sessionService.getSessionId()).not.toBeUndefined(); + currentAppId$.next('change'); + sessionService.clear(); + expect(sessionService.getSessionId()).not.toBeUndefined(); + }); + + it("Can start a new session in case there is other apps' stale session", async () => { + const s1 = sessionService.start(); + expect(sessionService.getSessionId()).not.toBeUndefined(); + currentAppId$.next('change'); + sessionService.start(); + expect(sessionService.getSessionId()).not.toBeUndefined(); + expect(sessionService.getSessionId()).not.toBe(s1); + }); + it('Restores a session', async () => { const sessionId = 'sessionId'; sessionService.restore(sessionId); diff --git a/src/plugins/data/public/search/session/session_service.ts b/src/plugins/data/public/search/session/session_service.ts index 4286edf27cd40..430fc8913c5fd 100644 --- a/src/plugins/data/public/search/session/session_service.ts +++ b/src/plugins/data/public/search/session/session_service.ts @@ -68,7 +68,7 @@ export class SessionService { private searchSessionInfoProvider?: SearchSessionInfoProvider; private searchSessionIndicatorUiConfig?: Partial; private subscription = new Subscription(); - private curApp?: string; + private currentApp?: string; private hasAccessToSearchSessions: boolean = false; constructor( @@ -100,24 +100,24 @@ export class SessionService { this.hasAccessToSearchSessions = coreStart.application.capabilities.management?.kibana?.[SEARCH_SESSIONS_MANAGEMENT_ID]; - // Apps required to clean up their sessions before unmounting - // Make sure that apps don't leave sessions open. this.subscription.add( - coreStart.application.currentAppId$.subscribe((appName) => { - if (this.state.get().sessionId) { - const message = `Application '${this.curApp}' had an open session while navigating`; - if (initializerContext.env.mode.dev) { - // TODO: This setTimeout is necessary due to a race condition while navigating. - setTimeout(() => { - coreStart.fatalErrors.add(message); - }, 100); - } else { - // eslint-disable-next-line no-console - console.warn(message); - this.clear(); - } + coreStart.application.currentAppId$.subscribe((newAppName) => { + this.currentApp = newAppName; + if (!this.getSessionId()) return; + + // Apps required to clean up their sessions before unmounting + // Make sure that apps don't leave sessions open by throwing an error in DEV mode + const message = `Application '${ + this.state.get().appName + }' had an open session while navigating`; + if (initializerContext.env.mode.dev) { + coreStart.fatalErrors.add(message); + } else { + // this should never happen in prod because should be caught in dev mode + // in case this happen we don't want to throw fatal error, as most likely possible bugs are not that critical + // eslint-disable-next-line no-console + console.warn(message); } - this.curApp = appName; }) ); }); @@ -187,7 +187,8 @@ export class SessionService { * @returns sessionId */ public start() { - this.state.transitions.start(); + if (!this.currentApp) throw new Error('this.currentApp is missing'); + this.state.transitions.start({ appName: this.currentApp }); return this.getSessionId()!; } @@ -203,6 +204,18 @@ export class SessionService { * Cleans up current state */ public clear() { + // make sure apps can't clear other apps' sessions + const currentSessionApp = this.state.get().appName; + if (currentSessionApp && currentSessionApp !== this.currentApp) { + // eslint-disable-next-line no-console + console.warn( + `Skip clearing session "${this.getSessionId()}" because it belongs to a different app. current: "${ + this.currentApp + }", owner: "${currentSessionApp}"` + ); + return; + } + this.state.transitions.clear(); this.searchSessionInfoProvider = undefined; this.searchSessionIndicatorUiConfig = undefined; @@ -229,7 +242,8 @@ export class SessionService { public async save(): Promise { const sessionId = this.getSessionId(); if (!sessionId) throw new Error('No current session'); - if (!this.curApp) throw new Error('No current app id'); + const currentSessionApp = this.state.get().appName; + if (!currentSessionApp) throw new Error('No current session app'); if (!this.hasAccess()) throw new Error('No access to search sessions'); const currentSessionInfoProvider = this.searchSessionInfoProvider; if (!currentSessionInfoProvider) throw new Error('No info provider for current session'); @@ -240,7 +254,7 @@ export class SessionService { await this.sessionsClient.create({ name, - appId: this.curApp, + appId: currentSessionApp, restoreState: (restoreState as unknown) as Record, initialState: (initialState as unknown) as Record, urlGeneratorId, diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 88747cf9e84d8..420e626031b7a 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -239,7 +239,8 @@ function discoverController($route, $scope, Promise) { if (!_.isEqual(newStatePartial, oldStatePartial)) { $scope.$evalAsync(async () => { - if (oldStatePartial.index !== newStatePartial.index) { + // NOTE: this is also called when navigating from discover app to context app + if (newStatePartial.index && oldStatePartial.index !== newStatePartial.index) { //in case of index pattern switch the route has currently to be reloaded, legacy isChangingIndexPattern = true; $route.reload(); diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/discover/async_search.ts b/x-pack/test/send_search_to_background_integration/tests/apps/discover/async_search.ts index 9c78e8de17df7..2bd539dab5bf3 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/discover/async_search.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/discover/async_search.ts @@ -14,7 +14,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const browser = getService('browser'); const inspector = getService('inspector'); - const PageObjects = getPageObjects(['discover', 'common', 'timePicker', 'header']); + const docTable = getService('docTable'); + const PageObjects = getPageObjects(['discover', 'common', 'timePicker', 'header', 'context']); const searchSessions = getService('searchSessions'); describe('discover async search', () => { @@ -62,6 +63,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await searchSessions.expectState('restored'); expect(await getSearchSessionId()).to.be(fakeSearchSessionId); }); + + it('navigation to context cleans the session', async () => { + await PageObjects.common.clearAllToasts(); + await docTable.clickRowToggle({ rowIndex: 0 }); + const rowActions = await docTable.getRowActions({ rowIndex: 0 }); + await rowActions[0].click(); + await PageObjects.context.waitUntilContextLoadingHasFinished(); + await searchSessions.missingOrFail(); + }); }); async function getSearchSessionId(): Promise { From 7593a911cad9033ef7398126eb2272b533387251 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 23 Feb 2021 14:38:29 +0100 Subject: [PATCH 24/70] [Uptime] fix double scroll on legend height change (#92155) --- .../waterfall/components/constants.ts | 2 +- .../synthetics/waterfall/components/legend.tsx | 13 +++++-------- .../waterfall/components/waterfall.test.tsx | 5 ++++- .../waterfall/components/waterfall_chart.tsx | 18 ++++++++++++++---- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts index a4b75174543a8..5b49e0fd529b7 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts @@ -18,4 +18,4 @@ export const FIXED_AXIS_HEIGHT = 32; // number of items to display in canvas, since canvas can only have limited size export const CANVAS_MAX_ITEMS = 150; -export const CHART_LEGEND_PADDING = 62; +export const CHART_LEGEND_PADDING = 33; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx index 7fa5a3c190e3b..c746a5cc63a9b 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { IWaterfallContext } from '../context/waterfall_chart'; -import { WaterfallChartLegendContainer } from './styles'; import { WaterfallChartProps } from './waterfall_chart'; interface LegendProps { @@ -18,12 +17,10 @@ interface LegendProps { export const Legend: React.FC = ({ items, render }) => { return ( - - - {items.map((item, index) => { - return {render(item, index)}; - })} - - + + {items.map((item, index) => { + return {render(item, index)}; + })} + ); }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx index 528d749f576fc..28e82930f3341 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx @@ -11,6 +11,7 @@ import { renderLegendItem } from '../../step_detail/waterfall/waterfall_chart_wr import { render } from '../../../../../lib/helper/rtl_helpers'; import 'jest-canvas-mock'; +import { waitFor } from '@testing-library/dom'; describe('waterfall', () => { it('sets the correct height in case of full height', () => { @@ -38,6 +39,8 @@ describe('waterfall', () => { const chartWrapper = getByTestId('waterfallOuterContainer'); - expect(chartWrapper).toHaveStyleRule('height', 'calc(100vh - 62px)'); + waitFor(() => { + expect(chartWrapper).toHaveStyleRule('height', 'calc(100vh - 62px)'); + }); }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx index a791f099ab9fe..59990b29db5db 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx @@ -8,6 +8,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { EuiFlexGroup } from '@elastic/eui'; import { TickFormatter, DomainRange, BarStyleAccessor } from '@elastic/charts'; +import useWindowSize from 'react-use/lib/useWindowSize'; import { useWaterfallContext } from '../context/waterfall_chart'; import { WaterfallChartOuterContainer, @@ -18,6 +19,7 @@ import { RelativeContainer, WaterfallChartFilterContainer, WaterfallChartAxisOnlyContainer, + WaterfallChartLegendContainer, } from './styles'; import { CHART_LEGEND_PADDING, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE } from './constants'; import { Sidebar } from './sidebar'; @@ -67,7 +69,10 @@ export const WaterfallChart = ({ fetchedNetworkRequests, } = useWaterfallContext(); + const { width } = useWindowSize(); + const chartWrapperDivRef = useRef(null); + const legendDivRef = useRef(null); const [height, setHeight] = useState(maxHeight); @@ -75,11 +80,12 @@ export const WaterfallChart = ({ const shouldRenderLegend = !!(legendItems && legendItems.length > 0 && renderLegendItem); useEffect(() => { - if (fullHeight && chartWrapperDivRef.current) { + if (fullHeight && chartWrapperDivRef.current && legendDivRef.current) { const chartOffset = chartWrapperDivRef.current.getBoundingClientRect().top; - setHeight(`calc(100vh - ${chartOffset + CHART_LEGEND_PADDING}px)`); + const legendOffset = legendDivRef.current.getBoundingClientRect().height; + setHeight(`calc(100vh - ${chartOffset + CHART_LEGEND_PADDING + legendOffset}px)`); } - }, [chartWrapperDivRef, fullHeight]); + }, [chartWrapperDivRef, fullHeight, legendDivRef, width]); const chartsToDisplay = useBarCharts({ data }); @@ -135,7 +141,11 @@ export const WaterfallChart = ({ - {shouldRenderLegend && } + {shouldRenderLegend && ( + + + + )} {renderFlyout && renderFlyout()} ); From 77bc230901aced047977744169c5b1cba295df88 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Tue, 23 Feb 2021 08:42:59 -0500 Subject: [PATCH 25/70] [Upgrade Assistant] Remove reindex logic that is no longer applicable (#91193) --- .../plugins/upgrade_assistant/common/types.ts | 4 - .../__snapshots__/warning_step.test.tsx.snap | 76 ++--------------- .../reindex/flyout/checklist_step.test.tsx | 2 +- .../reindex/flyout/warning_step.test.tsx | 8 +- .../reindex/flyout/warnings_step.tsx | 63 -------------- .../lib/reindexing/index_settings.test.ts | 82 +++++++++++++++---- .../server/lib/reindexing/index_settings.ts | 57 ++++++++++--- .../lib/reindexing/reindex_actions.test.ts | 18 ---- .../reindex_indices/reindex_indices.test.ts | 2 +- 9 files changed, 120 insertions(+), 192 deletions(-) diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts index 0284c89db7030..91a19bfec3e81 100644 --- a/x-pack/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/plugins/upgrade_assistant/common/types.ts @@ -93,10 +93,6 @@ export interface ReindexOperation extends SavedObjectAttributes { export type ReindexSavedObject = SavedObject; export enum ReindexWarning { - // 6.0 -> 7.0 warnings, now unused - allField = 0, - booleanFields = 1, - // 7.0 -> 8.0 warnings apmReindex, diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/__snapshots__/warning_step.test.tsx.snap b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/__snapshots__/warning_step.test.tsx.snap index f781ebc2a41d7..d92db98ae40cb 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/__snapshots__/warning_step.test.tsx.snap +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/__snapshots__/warning_step.test.tsx.snap @@ -27,88 +27,26 @@ exports[`WarningsFlyoutStep renders 1`] = ` checkedIds={ Object { "reindexWarning-0": false, - "reindexWarning-1": false, } } description={ - _all - , - } - } + defaultMessage="Starting in version 7.0.0, APM data will be represented in the Elastic Common Schema. Historical APM data will not visible until it's reindexed." + id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.apmReindexWarningDetail" + values={Object {}} /> } - documentationUrl="https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_the_literal__all_literal_meta_field_is_now_disabled_by_default" + documentationUrl="https://www.elastic.co/guide/en/observability/master/whats-new.html" label={ - _all - , - } - } + defaultMessage="This index will be converted to ECS format" + id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.apmReindexWarningTitle" + values={Object {}} /> } onChange={[Function]} warning={0} /> - - false - , - "on": - "on" - , - "one": - 1 - , - "true": - true - , - "yes": - "yes" - , - } - } - /> - } - documentationUrl="https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_field" - label={ - - _source - , - } - } - /> - } - onChange={[Function]} - warning={1} - /> { status: undefined, reindexTaskPercComplete: null, errorMessage: null, - reindexWarnings: [ReindexWarning.allField], + reindexWarnings: [ReindexWarning.apmReindex], hasRequiredPrivileges: true, } as ReindexState, }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx index e9a2494a2d07c..9f76ef0aa78ba 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx @@ -28,7 +28,7 @@ jest.mock('../../../../../../app_context', () => { describe('WarningsFlyoutStep', () => { const defaultProps = { advanceNextStep: jest.fn(), - warnings: [ReindexWarning.allField, ReindexWarning.booleanFields], + warnings: [ReindexWarning.apmReindex], closeFlyout: jest.fn(), renderGlobalCallouts: jest.fn(), }; @@ -48,11 +48,7 @@ describe('WarningsFlyoutStep', () => { button.simulate('click'); expect(defaultProps.advanceNextStep).not.toHaveBeenCalled(); - wrapper.find(`input#${idForWarning(ReindexWarning.allField)}`).simulate('change'); - button.simulate('click'); - expect(defaultProps.advanceNextStep).not.toHaveBeenCalled(); - - wrapper.find(`input#${idForWarning(ReindexWarning.booleanFields)}`).simulate('change'); + wrapper.find(`input#${idForWarning(ReindexWarning.apmReindex)}`).simulate('change'); button.simulate('click'); expect(defaultProps.advanceNextStep).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx index b10f059e22ae4..2e6b039a2fe76 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx @@ -12,7 +12,6 @@ import { EuiButtonEmpty, EuiCallOut, EuiCheckbox, - EuiCode, EuiFlexGroup, EuiFlexItem, EuiFlyoutBody, @@ -103,10 +102,8 @@ export const WarningsFlyoutStep: React.FunctionComponent @@ -131,35 +128,6 @@ export const WarningsFlyoutStep: React.FunctionComponent - {warnings.includes(ReindexWarning.allField) && ( - _all, - }} - /> - } - description={ - _all, - }} - /> - } - documentationUrl={`${esDocBasePath}/6.0/breaking_60_mappings_changes.html#_the_literal__all_literal_meta_field_is_now_disabled_by_default`} - /> - )} - {warnings.includes(ReindexWarning.apmReindex) && ( )} - - {warnings.includes(ReindexWarning.booleanFields) && ( - _source }} - /> - } - description={ - true, - false: false, - yes: "yes", - on: "on", - one: 1, - }} - /> - } - documentationUrl={`${esDocBasePath}/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_field`} - /> - )} diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts index 70b0c804310b7..609f36c25619e 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts @@ -37,14 +37,29 @@ describe('transformFlatSettings', () => { // Settings that should get preserved 'index.number_of_replicas': '1', 'index.number_of_shards': '5', + // Blacklisted settings - 'index.uuid': 'i66b9149a-00ee-42d9-8ca1-85ae927924bf', + 'index.allocation.existing_shards_allocator': 'gateway_allocator', 'index.blocks.write': 'true', 'index.creation_date': '1547052614626', - 'index.legacy': '6', - 'index.mapping.single_type': 'true', + 'index.frozen': 'true', + 'index.history.uuid': 'i66b9149a-00ee-42d9-8ca1-85ae9279234gh', + 'index.merge.enabled': 'true', 'index.provided_name': 'test1', + 'index.resize.source.name': 'resizeName', + 'index.resize.source.uuid': 'k34b9149a-00ee-42d9-8ca1-85ae9279234zs', 'index.routing.allocation.initial_recovery._id': '1', + 'index.search.throttled': 'true', + 'index.source_only': 'true', + 'index.shrink.source.name': 'shrinkSourceName', + 'index.shrink.source.uuid': 'q34b9149a-00ee-42d9-8ca1-85ae234324df', + 'index.store.snapshot.repository_name': 'repoName', + 'index.store.snapshot.snapshot_name': 'snapshotName', + 'index.store.snapshot.snapshot_uuid': 'f345c9149a-00ee-42d9-8ca1-85ae234324df', + 'index.store.snapshot.index_name': 'snapshotIndexName', + 'index.store.snapshot.index_uuid': 'h764f9149a-00ee-42d9-8ca1-85ae234324af', + 'index.uuid': 'i66b9149a-00ee-42d9-8ca1-85ae927924bf', + 'index.verified_before_close': 'true', 'index.version.created': '123123', 'index.version.upgraded': '123123', }, @@ -58,6 +73,52 @@ describe('transformFlatSettings', () => { mappings: {}, }); }); + + it('does not allow index.mapper.dynamic to be set', () => { + expect(() => + transformFlatSettings({ + settings: { + 'index.mapper.dynamic': 'true', + }, + mappings: {}, + }) + ).toThrowError(`'index.mapper.dynamic' is no longer supported.`); + }); + + it('does not allow index.merge.policy.reclaim_deletes_weight to be set', () => { + expect(() => + transformFlatSettings({ + settings: { + 'index.merge.policy.reclaim_deletes_weight': '2.0d', + }, + mappings: {}, + }) + ).toThrowError(`'index.merge.policy.reclaim_deletes_weight' is no longer supported.`); + }); + + it('does not allow index.force_memory_term_dictionary to be set', () => { + expect(() => + transformFlatSettings({ + settings: { + 'index.force_memory_term_dictionary': 'false', + }, + mappings: {}, + }) + ).toThrowError(`'index.force_memory_term_dictionary' is no longer supported.`); + }); + + it('does not index.max_adjacency_matrix_filters to be set', () => { + expect(() => + transformFlatSettings({ + settings: { + 'index.max_adjacency_matrix_filters': '1024', + }, + mappings: {}, + }) + ).toThrowError( + `'index.max_adjacency_matrix_filters' is no longer supported; use 'indices.query.bool.max_clause_count' as an alternative.` + ); + }); }); describe('sourceNameForIndex', () => { @@ -73,11 +134,6 @@ describe('sourceNameForIndex', () => { expect(sourceNameForIndex('myIndex')).toEqual('myIndex'); }); - it('excludes appended v5 reindexing string from newIndexName', () => { - expect(sourceNameForIndex('myIndex-reindexed-v5')).toEqual('myIndex'); - expect(sourceNameForIndex('.myInternalIndex-reindexed-v5')).toEqual('.myInternalIndex'); - }); - it(`replaces reindexed-v${prevMajor} with reindexed-v${currentMajor} in newIndexName`, () => { expect(sourceNameForIndex(`reindexed-v${prevMajor}-myIndex`)).toEqual('myIndex'); expect(sourceNameForIndex(`.reindexed-v${prevMajor}-myInternalIndex`)).toEqual( @@ -101,16 +157,6 @@ describe('generateNewIndexName', () => { expect(generateNewIndexName('myIndex')).toEqual(`reindexed-v${currentMajor}-myIndex`); }); - it('excludes appended v5 reindexing string from generateNewIndexName', () => { - expect(generateNewIndexName('myIndex-reindexed-v5')).toEqual( - `reindexed-v${currentMajor}-myIndex` - ); - - expect(generateNewIndexName('.myInternalIndex-reindexed-v5')).toEqual( - `.reindexed-v${currentMajor}-myInternalIndex` - ); - }); - it(`replaces reindexed-v${prevMajor} with reindexed-v${currentMajor} in generateNewIndexName`, () => { expect(generateNewIndexName(`reindexed-v${prevMajor}-myIndex`)).toEqual( `reindexed-v${currentMajor}-myIndex` diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts index 53ebe0c213d0c..11cc01b69d3a5 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts @@ -43,12 +43,8 @@ export const sourceNameForIndex = (indexName: string): string => { const internal = matches[1] || ''; const baseName = matches[2]; - // in 5.6 the upgrade assistant appended to the index, in 6.7+ we prepend to - // avoid conflicts with index patterns/templates/etc - const reindexedMatcher = new RegExp( - `(-reindexed-v5$|reindexed-v${versionService.getPrevMajorVersion()}-)`, - 'g' - ); + // in 6.7+ we prepend to avoid conflicts with index patterns/templates/etc + const reindexedMatcher = new RegExp(`reindexed-v${versionService.getPrevMajorVersion()}-`, 'g'); const cleanBaseName = baseName.replace(reindexedMatcher, ''); return `${internal}${cleanBaseName}`; @@ -83,23 +79,60 @@ export const getReindexWarnings = (flatSettings: FlatSettings): ReindexWarning[] const removeUnsettableSettings = (settings: FlatSettings['settings']) => omit(settings, [ - 'index.uuid', + // Private ES settings + 'index.allocation.existing_shards_allocator', 'index.blocks.write', 'index.creation_date', - 'index.legacy', - 'index.mapping.single_type', + 'index.frozen', + 'index.history.uuid', + 'index.merge.enabled', 'index.provided_name', + 'index.resize.source.name', + 'index.resize.source.uuid', 'index.routing.allocation.initial_recovery._id', + 'index.search.throttled', + 'index.source_only', + 'index.shrink.source.name', + 'index.shrink.source.uuid', + 'index.store.snapshot.repository_name', + 'index.store.snapshot.snapshot_name', + 'index.store.snapshot.snapshot_uuid', + 'index.store.snapshot.index_name', + 'index.store.snapshot.index_uuid', + 'index.uuid', + 'index.verified_before_close', 'index.version.created', + + // Deprecated in 9.0 'index.version.upgraded', - 'index.verified_before_close', ]); +const validateSettings = (settings: FlatSettings['settings']) => { + if (settings['index.mapper.dynamic']) { + throw new Error(`'index.mapper.dynamic' is no longer supported.`); + } + + if (settings['index.merge.policy.reclaim_deletes_weight']) { + throw new Error(`'index.merge.policy.reclaim_deletes_weight' is no longer supported.`); + } + + if (settings['index.force_memory_term_dictionary']) { + throw new Error(`'index.force_memory_term_dictionary' is no longer supported.`); + } + + if (settings['index.max_adjacency_matrix_filters']) { + throw new Error( + `'index.max_adjacency_matrix_filters' is no longer supported; use 'indices.query.bool.max_clause_count' as an alternative.` + ); + } + + return settings; +}; + // Use `flow` to pipe the settings through each function. -const transformSettings = flow(removeUnsettableSettings); +const transformSettings = flow(removeUnsettableSettings, validateSettings); const updateFixableMappings = (mappings: FlatSettings['mappings']) => { - // TODO: change type to _doc return mappings; }; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts index 9d8f9c4c3253d..59c83a05aa551 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts @@ -89,24 +89,6 @@ describe('ReindexActions', () => { }); }); - // in v5.6, the upgrade assistant appended to the index name instead of prepending - it(`prepends reindexed-v${currentMajor}- and removes reindex appended in v5`, async () => { - const indexName = 'myIndex-reindexed-v5'; - await actions.createReindexOp(indexName); - expect(client.create).toHaveBeenCalledWith(REINDEX_OP_TYPE, { - indexName, - newIndexName: `reindexed-v${currentMajor}-myIndex`, - reindexOptions: undefined, - status: ReindexStatus.inProgress, - lastCompletedStep: ReindexStep.created, - locked: null, - reindexTaskId: null, - reindexTaskPercComplete: null, - errorMessage: null, - runningReindexCount: null, - }); - }); - it(`replaces reindexed-v${prevMajor} with reindexed-v${currentMajor}`, async () => { await actions.createReindexOp(`reindexed-v${prevMajor}-myIndex`); expect(client.create).toHaveBeenCalledWith(REINDEX_OP_TYPE, { diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts index 5e5f83d067cf4..82d039ab9413a 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts @@ -89,7 +89,7 @@ describe('reindex API', () => { mockReindexService.findReindexOperation.mockResolvedValueOnce({ attributes: { indexName: 'wowIndex', status: ReindexStatus.inProgress }, }); - mockReindexService.detectReindexWarnings.mockResolvedValueOnce([ReindexWarning.allField]); + mockReindexService.detectReindexWarnings.mockResolvedValueOnce([ReindexWarning.apmReindex]); const resp = await routeDependencies.router.getHandler({ method: 'get', From 80f697d91fee096f43984ec65ca597034d413d84 Mon Sep 17 00:00:00 2001 From: Constance Date: Tue, 23 Feb 2021 05:43:37 -0800 Subject: [PATCH 26/70] Remove external/popout icon from Create Engine CTA (#92272) - now that create engine is no longer linking out to standalone UI --- .../app_search/components/engines/components/empty_state.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx index d742d68b0c9d6..fc77e2f2511e0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx @@ -52,7 +52,6 @@ export const EmptyState: React.FC = () => { actions={ From d01fb56d75d1b80d4ce555b9c6f4af0c1de3c4fa Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 23 Feb 2021 08:15:44 -0700 Subject: [PATCH 27/70] [ci/ftr] run new fleet_functional suite (#92296) Co-authored-by: spalger --- x-pack/scripts/functional_tests.js | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 2a741ad7d2120..d821f5417a4bd 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -75,6 +75,7 @@ const onlyNotInCoverageTests = [ require.resolve('../test/saved_object_tagging/api_integration/security_and_spaces/config.ts'), require.resolve('../test/saved_object_tagging/api_integration/tagging_api/config.ts'), require.resolve('../test/usage_collection/config.ts'), + require.resolve('../test/fleet_functional/config.ts'), ]; require('../../src/setup_node_env'); From 1997927edcdef4ca43770a68e78661afa6e0cbce Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 23 Feb 2021 16:32:15 +0100 Subject: [PATCH 28/70] =?UTF-8?q?Upgrade=20`ejs`=20dependency=20(`3.1.5`?= =?UTF-8?q?=20=E2=86=92=20`3.1.6`).=20(#92402)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 ++-- yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 833246f92ddc5..72fc72ea5f055 100644 --- a/package.json +++ b/package.json @@ -428,7 +428,7 @@ "@types/dedent": "^0.7.0", "@types/deep-freeze-strict": "^1.1.0", "@types/delete-empty": "^2.0.0", - "@types/ejs": "^3.0.4", + "@types/ejs": "^3.0.6", "@types/elasticsearch": "^5.0.33", "@types/enzyme": "^3.10.5", "@types/eslint": "^6.1.3", @@ -623,7 +623,7 @@ "dependency-check": "^4.1.0", "diff": "^4.0.1", "dpdm": "3.5.0", - "ejs": "^3.1.5", + "ejs": "^3.1.6", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", "enzyme-adapter-utils": "^1.13.0", diff --git a/yarn.lock b/yarn.lock index 09b50a6740a39..08bf4c8ea752c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5550,10 +5550,10 @@ resolved "https://registry.yarnpkg.com/@types/delete-empty/-/delete-empty-2.0.0.tgz#1647ae9e68f708a6ba778531af667ec55bc61964" integrity sha512-sq+kwx8zA9BSugT9N+Jr8/uWjbHMZ+N/meJEzRyT3gmLq/WMtx/iSIpvdpmBUi/cvXl6Kzpvve8G2ESkabFwmg== -"@types/ejs@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.0.4.tgz#8851fcdedb96e410fbb24f83b8be6763ef9afa77" - integrity sha512-ZxnwyBGO4KX/82AsFHTX82eMw0PsoBcIngEat+zx0y+3yxoNDJucAihg9nAcrc+g4Cwiv/4WcWsX4oiy0ySrRQ== +"@types/ejs@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.0.6.tgz#aca442289df623bfa8e47c23961f0357847b83fe" + integrity sha512-fj1hi+ZSW0xPLrJJD+YNwIh9GZbyaIepG26E/gXvp8nCa2pYokxUYO1sK9qjGxp2g8ryZYuon7wmjpwE2cyASQ== "@types/elasticsearch@^5.0.33": version "5.0.33" @@ -13002,10 +13002,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -ejs@^3.1.2, ejs@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.5.tgz#aed723844dc20acb4b170cd9ab1017e476a0d93b" - integrity sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w== +ejs@^3.1.2, ejs@^3.1.5, ejs@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" + integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== dependencies: jake "^10.6.1" From 62a0a4beacb55ec51419b0d4a1e0eb3e71a3bdaa Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 23 Feb 2021 10:32:46 -0500 Subject: [PATCH 29/70] [TSVB] Stop inserting zeroes for null series (#90861) * [TSVB] Stop inserting zeroes for null series * Replace empty default value with hyphen * Stop treating 0 as false * Fix test cases Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../vis_type_timeseries/common/get_last_value.js | 8 ++++---- .../common/get_last_value.test.js | 14 +++++++++++--- .../application/visualizations/views/gauge.js | 2 +- .../application/visualizations/views/metric.js | 2 +- .../apps/dashboard/dashboard_filtering.ts | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/plugins/vis_type_timeseries/common/get_last_value.js b/src/plugins/vis_type_timeseries/common/get_last_value.js index 9d5ad4c264c99..5a36a5e099f9d 100644 --- a/src/plugins/vis_type_timeseries/common/get_last_value.js +++ b/src/plugins/vis_type_timeseries/common/get_last_value.js @@ -8,13 +8,13 @@ import { isArray, last } from 'lodash'; -const DEFAULT_VALUE = 0; -const extractValue = (data) => (data && data[1]) || null; +const DEFAULT_VALUE = '-'; +const extractValue = (data) => (data && data[1]) ?? null; export const getLastValue = (data, defaultValue = DEFAULT_VALUE) => { if (!isArray(data)) { - return data || defaultValue; + return data ?? defaultValue; } - return extractValue(last(data)) || defaultValue; + return extractValue(last(data)) ?? defaultValue; }; diff --git a/src/plugins/vis_type_timeseries/common/get_last_value.test.js b/src/plugins/vis_type_timeseries/common/get_last_value.test.js index e4192de2392eb..122f037ddf3e4 100644 --- a/src/plugins/vis_type_timeseries/common/get_last_value.test.js +++ b/src/plugins/vis_type_timeseries/common/get_last_value.test.js @@ -13,12 +13,20 @@ describe('getLastValue(data)', () => { expect(getLastValue('foo')).toBe('foo'); }); + test('should returns 0 as a value when not an array', () => { + expect(getLastValue(0)).toBe(0); + }); + test('should returns the last value', () => { expect(getLastValue([[1, 2]])).toBe(2); }); + test('should return 0 as a valid value', () => { + expect(getLastValue([[0, 0]])).toBe(0); + }); + test('should returns the default value ', () => { - expect(getLastValue()).toBe(0); + expect(getLastValue()).toBe('-'); }); test('should returns 0 if second to last is not defined (default)', () => { @@ -27,10 +35,10 @@ describe('getLastValue(data)', () => { [1, null], [2, null], ]) - ).toBe(0); + ).toBe('-'); }); test('should allows to override the default value', () => { - expect(getLastValue(null, '-')).toBe('-'); + expect(getLastValue(null, 'default')).toBe('default'); }); }); diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js index c7287f79dfdb8..31ea3412972e8 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js @@ -61,7 +61,7 @@ export class Gauge extends Component { render() { const { metric, type } = this.props; const { scale, translateX, translateY } = this.state; - const value = (metric && getLastValue(metric.data)) || 0; + const value = metric && getLastValue(metric.data); const max = (metric && getValueBy('max', metric.data)) || 1; const formatter = (metric && (metric.tickFormatter || metric.formatter)) || diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js index e5b368aefa2e9..17cadb94457b6 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js @@ -58,7 +58,7 @@ export class Metric extends Component { const { metric, secondary } = this.props; const { scale, translateX, translateY } = this.state; const primaryFormatter = (metric && (metric.tickFormatter || metric.formatter)) || ((n) => n); - const primaryValue = primaryFormatter(getLastValue((metric && metric.data) || 0)); + const primaryValue = primaryFormatter(getLastValue(metric && metric.data)); const styles = reactcss( { default: { diff --git a/test/functional/apps/dashboard/dashboard_filtering.ts b/test/functional/apps/dashboard/dashboard_filtering.ts index 2aa24fc8b7065..e995bc4e52c49 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.ts +++ b/test/functional/apps/dashboard/dashboard_filtering.ts @@ -110,7 +110,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('tsvb top n is filtered', async () => { - await dashboardExpect.tsvbTopNValuesExist(['0', '0']); + await dashboardExpect.tsvbTopNValuesExist(['-', '-']); }); it('saved search is filtered', async () => { @@ -172,7 +172,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('tsvb top n is filtered', async () => { - await dashboardExpect.tsvbTopNValuesExist(['0', '0']); + await dashboardExpect.tsvbTopNValuesExist(['-', '-']); }); it('saved search is filtered', async () => { From 8ab68601e084be99303343786f941e198701722f Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 23 Feb 2021 11:00:03 -0500 Subject: [PATCH 30/70] [CI] Convert ES Snapshots Verify job to use tasks (#92091) --- .ci/es-snapshots/Jenkinsfile_verify_es | 54 ++++++++++---------------- vars/tasks.groovy | 16 ++++++-- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/.ci/es-snapshots/Jenkinsfile_verify_es b/.ci/es-snapshots/Jenkinsfile_verify_es index f3f07d5f355be..b22406e389276 100644 --- a/.ci/es-snapshots/Jenkinsfile_verify_es +++ b/.ci/es-snapshots/Jenkinsfile_verify_es @@ -30,38 +30,22 @@ kibanaPipeline(timeoutMinutes: 210) { "ES_SNAPSHOT_MANIFEST=${SNAPSHOT_MANIFEST}", 'IGNORE_SHIP_CI_STATS_ERROR=true', ]) { - parallel([ - 'kibana-intake-agent': workers.intake('kibana-intake', './test/scripts/jenkins_unit.sh'), - 'kibana-oss-agent': workers.functional('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ - 'oss-ciGroup1': kibanaPipeline.ossCiGroupProcess(1), - 'oss-ciGroup2': kibanaPipeline.ossCiGroupProcess(2), - 'oss-ciGroup3': kibanaPipeline.ossCiGroupProcess(3), - 'oss-ciGroup4': kibanaPipeline.ossCiGroupProcess(4), - 'oss-ciGroup5': kibanaPipeline.ossCiGroupProcess(5), - 'oss-ciGroup6': kibanaPipeline.ossCiGroupProcess(6), - 'oss-ciGroup7': kibanaPipeline.ossCiGroupProcess(7), - 'oss-ciGroup8': kibanaPipeline.ossCiGroupProcess(8), - 'oss-ciGroup9': kibanaPipeline.ossCiGroupProcess(9), - 'oss-ciGroup10': kibanaPipeline.ossCiGroupProcess(10), - 'oss-ciGroup11': kibanaPipeline.ossCiGroupProcess(11), - 'oss-ciGroup12': kibanaPipeline.ossCiGroupProcess(12), - ]), - 'kibana-xpack-agent': workers.functional('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ - 'xpack-ciGroup1': kibanaPipeline.xpackCiGroupProcess(1), - 'xpack-ciGroup2': kibanaPipeline.xpackCiGroupProcess(2), - 'xpack-ciGroup3': kibanaPipeline.xpackCiGroupProcess(3), - 'xpack-ciGroup4': kibanaPipeline.xpackCiGroupProcess(4), - 'xpack-ciGroup5': kibanaPipeline.xpackCiGroupProcess(5), - 'xpack-ciGroup6': kibanaPipeline.xpackCiGroupProcess(6), - 'xpack-ciGroup7': kibanaPipeline.xpackCiGroupProcess(7), - 'xpack-ciGroup8': kibanaPipeline.xpackCiGroupProcess(8), - 'xpack-ciGroup9': kibanaPipeline.xpackCiGroupProcess(9), - 'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10), - 'xpack-ciGroup11': kibanaPipeline.xpackCiGroupProcess(11), - 'xpack-ciGroup12': kibanaPipeline.xpackCiGroupProcess(12), - 'xpack-ciGroup13': kibanaPipeline.xpackCiGroupProcess(13), - ]), - ]) + kibanaPipeline.withTasks { + tasks([ + kibanaPipeline.scriptTaskDocker('Jest Integration Tests', 'test/scripts/test/jest_integration.sh'), + kibanaPipeline.scriptTask('API Integration Tests', 'test/scripts/test/api_integration.sh'), + ]) + + task { + kibanaPipeline.buildOss(6) + tasks.ossCiGroups() + } + + task { + kibanaPipeline.buildXpack(10) + tasks.xpackCiGroups() + } + } } promoteSnapshot(SNAPSHOT_VERSION, SNAPSHOT_ID) @@ -72,7 +56,9 @@ kibanaPipeline(timeoutMinutes: 210) { } def promoteSnapshot(snapshotVersion, snapshotId) { - node(workers.label('s')) { - esSnapshots.promote(snapshotVersion, snapshotId) + if (buildUtils.getBuildStatus() == 'SUCCESS') { + node(workers.label('s')) { + esSnapshots.promote(snapshotVersion, snapshotId) + } } } diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 68e3b05f36a0b..f782ffdd8c5e6 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -36,6 +36,16 @@ def test() { ]) } +def ossCiGroups() { + def ciGroups = 1..12 + tasks(ciGroups.collect { kibanaPipeline.ossCiGroupProcess(it, true) }) +} + +def xpackCiGroups() { + def ciGroups = 1..13 + tasks(ciGroups.collect { kibanaPipeline.xpackCiGroupProcess(it, true) }) +} + def functionalOss(Map params = [:]) { def config = params ?: [ serverIntegration: true, @@ -50,8 +60,7 @@ def functionalOss(Map params = [:]) { kibanaPipeline.buildOss(6) if (config.ciGroups) { - def ciGroups = 1..12 - tasks(ciGroups.collect { kibanaPipeline.ossCiGroupProcess(it, true) }) + ossCiGroups() } if (config.firefox) { @@ -91,8 +100,7 @@ def functionalXpack(Map params = [:]) { kibanaPipeline.buildXpack(10) if (config.ciGroups) { - def ciGroups = 1..13 - tasks(ciGroups.collect { kibanaPipeline.xpackCiGroupProcess(it, true) }) + xpackCiGroups() } if (config.firefox) { From cb932075fa9e4ad048a1c700b2dfdd07b750e946 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Tue, 23 Feb 2021 11:01:21 -0500 Subject: [PATCH 31/70] [Dashboard] Remove Multiple History Entries & Stay in Edit Mode on Save As (#92105) * made save as stay in edit mode when not navigating to another dashboard. Removed extra history entires from save --- .../application/dashboard_state_manager.ts | 11 ++++----- .../public/application/lib/save_dashboard.ts | 1 - .../application/top_nav/dashboard_top_nav.tsx | 24 ++++++++++++++----- .../apps/dashboard/full_screen_mode.ts | 5 +++- test/functional/apps/dashboard/view_edit.ts | 2 -- .../functional/page_objects/dashboard_page.ts | 9 ++++++- .../dashboard_to_dashboard_drilldown.ts | 1 + .../drilldowns/dashboard_to_url_drilldown.ts | 1 + 8 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/plugins/dashboard/public/application/dashboard_state_manager.ts b/src/plugins/dashboard/public/application/dashboard_state_manager.ts index 7f3f347e6e3ae..51b12baad4769 100644 --- a/src/plugins/dashboard/public/application/dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/dashboard_state_manager.ts @@ -139,6 +139,7 @@ export class DashboardStateManager { // setup initial state by merging defaults with state from url & panels storage // also run migration, as state in url could be of older version const initialUrlState = this.kbnUrlStateStorage.get(STATE_STORAGE_KEY); + const initialState = migrateAppState( { ...this.stateDefaults, @@ -345,7 +346,7 @@ export class DashboardStateManager { /** * Resets the state back to the last saved version of the dashboard. */ - public resetState(resetViewMode: boolean) { + public resetState() { // In order to show the correct warning, we have to store the unsaved // title on the dashboard object. We should fix this at some point, but this is how all the other object // save panels work at the moment. @@ -368,12 +369,8 @@ export class DashboardStateManager { this.stateDefaults.filters = [...this.getLastSavedFilterBars()]; this.isDirty = false; - if (resetViewMode) { - this.stateContainer.set(this.stateDefaults); - } else { - const currentViewMode = this.stateContainer.get().viewMode; - this.stateContainer.set({ ...this.stateDefaults, viewMode: currentViewMode }); - } + const currentViewMode = this.stateContainer.get().viewMode; + this.stateContainer.set({ ...this.stateDefaults, viewMode: currentViewMode }); } /** diff --git a/src/plugins/dashboard/public/application/lib/save_dashboard.ts b/src/plugins/dashboard/public/application/lib/save_dashboard.ts index 6913fcda4c8e2..684d8b4c5e8d8 100644 --- a/src/plugins/dashboard/public/application/lib/save_dashboard.ts +++ b/src/plugins/dashboard/public/application/lib/save_dashboard.ts @@ -38,7 +38,6 @@ export function saveDashboard( // reset state only when save() was successful // e.g. save() could be interrupted if title is duplicated and not confirmed dashboardStateManager.lastSavedDashboardFilters = dashboardStateManager.getFilterState(); - dashboardStateManager.resetState(!saveOptions.stayInEditMode); } return id; diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx index d279a6c219c9d..b51382347b952 100644 --- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx @@ -19,7 +19,12 @@ import { openAddPanelFlyout, ViewMode, } from '../../services/embeddable'; -import { getSavedObjectFinder, SaveResult, showSaveModal } from '../../services/saved_objects'; +import { + getSavedObjectFinder, + SavedObjectSaveOpts, + SaveResult, + showSaveModal, +} from '../../services/saved_objects'; import { NavAction } from '../../types'; import { DashboardSavedObject } from '../..'; @@ -43,7 +48,6 @@ import { OverlayRef } from '../../../../../core/public'; import { getNewDashboardTitle, unsavedChangesBadge } from '../../dashboard_strings'; import { DASHBOARD_PANELS_UNSAVED_ID } from '../lib/dashboard_panel_storage'; import { DashboardContainer } from '..'; -import { SavedDashboardSaveOpts } from '../lib/save_dashboard'; export interface DashboardTopNavState { chromeIsVisible: boolean; @@ -176,7 +180,7 @@ export function DashboardTopNav({ } function discardChanges() { - dashboardStateManager.resetState(true); + dashboardStateManager.resetState(); dashboardStateManager.clearUnsavedPanels(); // We need to do a hard reset of the timepicker. appState will not reload like @@ -221,7 +225,8 @@ export function DashboardTopNav({ * @resolved {String} - The id of the doc */ const save = useCallback( - async (saveOptions: SavedDashboardSaveOpts) => { + async (saveOptions: SavedObjectSaveOpts) => { + setIsSaveInProgress(true); return saveDashboard(angular.toJson, timefilter, dashboardStateManager, saveOptions) .then(function (id) { if (id) { @@ -235,8 +240,15 @@ export function DashboardTopNav({ dashboardPanelStorage.clearPanels(lastDashboardId); if (id !== lastDashboardId) { - redirectTo({ destination: 'dashboard', id, useReplace: !lastDashboardId }); + redirectTo({ + id, + // editMode: true, + destination: 'dashboard', + useReplace: true, + }); } else { + setIsSaveInProgress(false); + dashboardStateManager.resetState(); chrome.docTitle.change(dashboardStateManager.savedDashboard.lastSavedTitle); } } @@ -354,7 +366,7 @@ export function DashboardTopNav({ } setIsSaveInProgress(true); - save({ stayInEditMode: true }).then((response: SaveResult) => { + save({}).then((response: SaveResult) => { // If the save wasn't successful, put the original values back. if (!(response as { id: string }).id) { dashboardStateManager.setTitle(currentTitle); diff --git a/test/functional/apps/dashboard/full_screen_mode.ts b/test/functional/apps/dashboard/full_screen_mode.ts index 80aea2657064e..1f63dcdafdcce 100644 --- a/test/functional/apps/dashboard/full_screen_mode.ts +++ b/test/functional/apps/dashboard/full_screen_mode.ts @@ -36,7 +36,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('available in view mode', async () => { - await PageObjects.dashboard.saveDashboard('full screen test', { saveAsNew: true }); + await PageObjects.dashboard.saveDashboard('full screen test', { + saveAsNew: true, + exitFromEditMode: true, + }); const exists = await PageObjects.dashboard.fullScreenModeMenuItemExists(); expect(exists).to.be(true); }); diff --git a/test/functional/apps/dashboard/view_edit.ts b/test/functional/apps/dashboard/view_edit.ts index 6c7d60c9a15aa..c5c7daab27ff1 100644 --- a/test/functional/apps/dashboard/view_edit.ts +++ b/test/functional/apps/dashboard/view_edit.ts @@ -68,7 +68,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { storeTimeWithDashboard: true, }); - await PageObjects.dashboard.switchToEditMode(); await PageObjects.timePicker.setAbsoluteRange( 'Sep 19, 2013 @ 06:31:44.000', 'Sep 19, 2013 @ 06:31:44.000' @@ -210,7 +209,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('when time changed is not stored with dashboard', async function () { await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: false }); - await PageObjects.dashboard.switchToEditMode(); await PageObjects.timePicker.setAbsoluteRange( 'Oct 19, 2014 @ 06:31:44.000', 'Dec 19, 2014 @ 06:31:44.000' diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 465deed4d9039..9c12296db138c 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -31,6 +31,7 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide * @default true */ waitDialogIsClosed?: boolean; + exitFromEditMode?: boolean; needsConfirm?: boolean; storeTimeWithDashboard?: boolean; saveAsNew?: boolean; @@ -376,7 +377,7 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide */ public async saveDashboard( dashboardName: string, - saveOptions: SaveDashboardOptions = { waitDialogIsClosed: true } + saveOptions: SaveDashboardOptions = { waitDialogIsClosed: true, exitFromEditMode: true } ) { await retry.try(async () => { await this.enterDashboardTitleAndClickSave(dashboardName, saveOptions); @@ -393,6 +394,12 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.common.waitForSaveModalToClose(); + const isInViewMode = await testSubjects.exists('dashboardEditMode'); + if (saveOptions.exitFromEditMode && !isInViewMode) { + await this.clickCancelOutOfEditMode(); + } + await PageObjects.header.waitUntilLoadingHasFinished(); + return message; } diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts b/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts index b0d77bc385476..68b057e9487ce 100644 --- a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts +++ b/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts @@ -71,6 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { { saveAsNew: false, waitDialogIsClosed: true, + exitFromEditMode: true, } ); diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_url_drilldown.ts b/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_url_drilldown.ts index db2c3b1aa2da4..e0b6c6a5f4803 100644 --- a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_url_drilldown.ts +++ b/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_url_drilldown.ts @@ -60,6 +60,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { { saveAsNew: false, waitDialogIsClosed: true, + exitFromEditMode: true, } ); From 49dee63705aef4fca1dbbcc40272b4bf6aa5b638 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Tue, 23 Feb 2021 11:07:45 -0500 Subject: [PATCH 32/70] [Security Solution][Endpoint][Admin] Policy response flyout view has scrolling (#92265) --- .../pages/endpoint_hosts/view/details/index.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx index 693b185c0feab..ed68cd17fa446 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx @@ -132,11 +132,9 @@ export const EndpointDetailsFlyout = memo(() => { EndpointDetailsFlyout.displayName = 'EndpointDetailsFlyout'; -const PolicyResponseFlyout = styled.div` - .endpointDetailsPolicyResponseFlyoutBody { - .euiFlyoutBody__overflowContent { - padding-top: 0; - } +const PolicyResponseFlyoutBody = styled(EuiFlyoutBody)` + .euiFlyoutBody__overflowContent { + padding-top: 0; } `; @@ -180,12 +178,12 @@ const PolicyResponseFlyoutPanel = memo<{ }, [backToDetailsClickHandler, detailsUri]); return ( - + <> - @@ -220,8 +218,8 @@ const PolicyResponseFlyoutPanel = memo<{ responseAttentionCount={responseAttentionCount} /> )} - - + + ); }); From 6c6636c12fc48887c1ec775063e56ec8dd636874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 23 Feb 2021 11:12:47 -0500 Subject: [PATCH 33/70] Add docs for ability to specify custom alert ids (#92410) * Add docs for ability to specify custom alert ids * Add v1/v4 --- docs/api/alerts/create.asciidoc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/api/alerts/create.asciidoc b/docs/api/alerts/create.asciidoc index 9e188b971c9b5..996503bc59148 100644 --- a/docs/api/alerts/create.asciidoc +++ b/docs/api/alerts/create.asciidoc @@ -9,7 +9,13 @@ Create {kib} alerts. [[alerts-api-create-request]] ==== Request -`POST :/api/alerts/alert` +`POST :/api/alerts/alert/` + +[[alerts-api-create-path-params]] +==== Path parameters + +``:: + (Optional, string) Specifies a UUID v1 or v4 to use instead of a randomly generated ID. [[alerts-api-create-request-body]] ==== Request body From 70645b367ee6813c8d86ad4339cd81e480e7c8e5 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 23 Feb 2021 11:14:16 -0500 Subject: [PATCH 34/70] show creation triggered indication. disable continue while included fields load (#92254) --- .../configuration_step/configuration_step_form.tsx | 7 ++++++- .../components/create_step/create_step.tsx | 11 +++++++++-- .../hooks/use_create_analytics_form/actions.ts | 2 +- .../use_create_analytics_form.ts | 2 ++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx index 390204888b500..07331c208482a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx @@ -83,6 +83,7 @@ export const ConfigurationStepForm: FC = ({ EuiComboBoxOptionOption[] >([]); const [includesTableItems, setIncludesTableItems] = useState([]); + const [fetchingExplainData, setFetchingExplainData] = useState(false); const [maxDistinctValuesError, setMaxDistinctValuesError] = useState(); const [unsupportedFieldsError, setUnsupportedFieldsError] = useState(); const [minimumFieldsRequiredMessage, setMinimumFieldsRequiredMessage] = useState< @@ -150,7 +151,8 @@ export const ConfigurationStepForm: FC = ({ maxDistinctValuesError !== undefined || minimumFieldsRequiredMessage !== undefined || requiredFieldsError !== undefined || - unsupportedFieldsError !== undefined; + unsupportedFieldsError !== undefined || + fetchingExplainData; const loadDepVarOptions = async (formState: State['form']) => { setLoadingDepVarOptions(true); @@ -191,6 +193,7 @@ export const ConfigurationStepForm: FC = ({ }; const debouncedGetExplainData = debounce(async () => { + setFetchingExplainData(true); const jobTypeChanged = previousJobType !== jobType; const shouldUpdateModelMemoryLimit = (!firstUpdate.current || !modelMemoryLimit) && useEstimatedMml === true; @@ -233,6 +236,7 @@ export const ConfigurationStepForm: FC = ({ requiredFieldsError: !hasRequiredFields ? requiredFieldsErrorText : undefined, }); } + setFetchingExplainData(false); } else { let maxDistinctValuesErrorMessage; let unsupportedFieldsErrorMessage; @@ -280,6 +284,7 @@ export const ConfigurationStepForm: FC = ({ setFieldOptionsFetchFail(true); setMaxDistinctValuesError(maxDistinctValuesErrorMessage); setUnsupportedFieldsError(unsupportedFieldsErrorMessage); + setFetchingExplainData(false); setFormState({ ...(shouldUpdateModelMemoryLimit ? { modelMemoryLimit: fallbackModelMemoryLimit } : {}), }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx index 26c5834057c31..77c00a94227f0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx @@ -31,14 +31,20 @@ export const CreateStep: FC = ({ actions, state, step }) => { const { jobId, jobType } = state.form; const [checked, setChecked] = useState(true); + const [creationTriggered, setCreationTriggered] = useState(false); const [showProgress, setShowProgress] = useState(false); if (step !== ANALYTICS_STEPS.CREATE) return null; const handleCreation = async () => { - await createAnalyticsJob(); + setCreationTriggered(true); + const creationSuccess = await createAnalyticsJob(); - if (checked) { + if (creationSuccess === false) { + setCreationTriggered(false); + } + + if (checked && creationSuccess === true) { setShowProgress(true); startAnalyticsJob(); } @@ -75,6 +81,7 @@ export const CreateStep: FC = ({ actions, state, step }) => { disabled={!isValid || !isAdvancedEditorValidJson} onClick={handleCreation} fill + isLoading={creationTriggered} data-test-subj="mlAnalyticsCreateJobWizardCreateButton" > {i18n.translate('xpack.ml.dataframe.analytics.create.wizardCreateButton', { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts index 1063ca703da81..7795c6645a6a0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts @@ -66,7 +66,7 @@ export type Action = // Actions wrapping the dispatcher exposed by the custom hook export interface ActionDispatchers { closeModal: () => void; - createAnalyticsJob: () => void; + createAnalyticsJob: () => Promise; initiateWizard: () => Promise; resetAdvancedEditorMessages: () => void; setAdvancedEditorRawString: (payload: State['advancedEditorRawString']) => void; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index d47a5fb71a77b..8f9afc8495884 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -108,6 +108,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { createKibanaIndexPattern(); } refresh(); + return true; } catch (e) { addRequestMessage({ error: extractErrorMessage(e), @@ -118,6 +119,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { } ), }); + return false; } }; From 8be841af1014434bdb5e3e84980ad934d8a107e9 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 23 Feb 2021 17:24:48 +0100 Subject: [PATCH 35/70] [Discover] Fix index pattern switch behavior (#92131) --- .../sidebar/discover_index_pattern.tsx | 11 +-- .../components/sidebar/discover_sidebar.tsx | 2 - .../sidebar/discover_sidebar_responsive.tsx | 1 - ...get_switch_index_pattern_app_state.test.ts | 80 ++++++++++++------- .../get_switch_index_pattern_app_state.ts | 18 +++-- 5 files changed, 67 insertions(+), 45 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx index ea3e35f607be4..021d5a0252f7c 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx @@ -19,7 +19,7 @@ import { IndexPatternRef } from './types'; import { ChangeIndexPattern } from './change_indexpattern'; import { getSwitchIndexPatternAppState } from '../../helpers/get_switch_index_pattern_app_state'; import { SortPairArr } from '../../angular/doc_table/lib/get_sort'; -import { MODIFY_COLUMNS_ON_SWITCH } from '../../../../common'; +import { MODIFY_COLUMNS_ON_SWITCH, SORT_DEFAULT_ORDER_SETTING } from '../../../../common'; import { AppState } from '../../angular/discover_state'; export interface DiscoverIndexPatternProps { /** @@ -46,10 +46,6 @@ export interface DiscoverIndexPatternProps { * Discover App state */ state: AppState; - /** - * Read from the Fields API - */ - useNewFieldsApi?: boolean; } /** @@ -62,7 +58,6 @@ export function DiscoverIndexPattern({ indexPatterns, state, setAppState, - useNewFieldsApi, }: DiscoverIndexPatternProps) { const options: IndexPatternRef[] = (indexPatternList || []).map((entity) => ({ id: entity.id, @@ -80,12 +75,12 @@ export function DiscoverIndexPattern({ state.columns || [], (state.sort || []) as SortPairArr[], config.get(MODIFY_COLUMNS_ON_SWITCH), - useNewFieldsApi + config.get(SORT_DEFAULT_ORDER_SETTING) ); setAppState(nextAppState); } }, - [selectedIndexPattern, state, config, indexPatterns, setAppState, useNewFieldsApi] + [selectedIndexPattern, state, config, indexPatterns, setAppState] ); const [selected, setSelected] = useState({ diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index c0a192550e6c4..138122d80c765 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -165,7 +165,6 @@ export function DiscoverSidebar({ indexPatterns={indexPatterns} state={state} setAppState={setAppState} - useNewFieldsApi={useNewFieldsApi} /> ); @@ -195,7 +194,6 @@ export function DiscoverSidebar({ indexPatterns={indexPatterns} state={state} setAppState={setAppState} - useNewFieldsApi={useNewFieldsApi} /> diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx index f0e7c71f9c970..0808ef47c0dc1 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx @@ -160,7 +160,6 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) indexPatterns={props.indexPatterns} state={props.state} setAppState={props.setAppState} - useNewFieldsApi={props.useNewFieldsApi} /> diff --git a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.test.ts b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.test.ts index de95c1343c99f..31addd807b78d 100644 --- a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.test.ts +++ b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.test.ts @@ -7,36 +7,33 @@ */ import { getSwitchIndexPatternAppState } from './get_switch_index_pattern_app_state'; -import { IIndexPatternFieldList, IndexPattern } from '../../../../data/common/index_patterns'; +import { IndexPattern } from '../../../../data/common/index_patterns'; -const currentIndexPattern: IndexPattern = { - id: 'prev', - getFieldByName(name) { - return this.fields.getByName(name); - }, - fields: { - getByName: (name: string) => { - const fields = [ - { name: 'category', sortable: true }, - { name: 'name', sortable: true }, - ] as IIndexPatternFieldList; - return fields.find((field) => field.name === name); +/** + * Helper function returning an index pattern + */ +const getIndexPattern = (id: string, timeFieldName: string, fields: string[]) => { + return { + id, + timeFieldName, + getFieldByName(name) { + return this.fields.getByName(name); }, - }, -} as IndexPattern; - -const nextIndexPattern = { - id: 'next', - getFieldByName(name) { - return this.fields.getByName(name); - }, - fields: { - getByName: (name: string) => { - const fields = [{ name: 'category', sortable: true }] as IIndexPatternFieldList; - return fields.find((field) => field.name === name); + fields: { + getByName: (name: string) => { + return fields + .map((field) => ({ + name: field, + sortable: true, + })) + .find((field) => field.name === name); + }, }, - }, -} as IndexPattern; + } as IndexPattern; +}; + +const currentIndexPattern = getIndexPattern('curr', '', ['category', 'name']); +const nextIndexPattern = getIndexPattern('next', '', ['category']); describe('Discover getSwitchIndexPatternAppState', () => { test('removing fields that are not part of the next index pattern, keeping unknown fields ', async () => { @@ -59,10 +56,10 @@ describe('Discover getSwitchIndexPatternAppState', () => { ['name', 'asc'], ] ); - expect(result.columns).toEqual(['_source']); + expect(result.columns).toEqual([]); expect(result.sort).toEqual([['category', 'desc']]); }); - test('removing sorted by fields that without modifying columns', async () => { + test('removing sorted by fields not available in the next index pattern without modifying columns', async () => { const result = getSwitchIndexPatternAppState( currentIndexPattern, nextIndexPattern, @@ -76,4 +73,29 @@ describe('Discover getSwitchIndexPatternAppState', () => { expect(result.columns).toEqual(['name']); expect(result.sort).toEqual([['category', 'desc']]); }); + test('keep sorting by timefield when switching between index patterns with different timeFields', async () => { + const current = getIndexPattern('a', 'timeFieldA', ['timeFieldA']); + const next = getIndexPattern('b', 'timeFieldB', ['timeFieldB']); + + const result = getSwitchIndexPatternAppState(current, next, [], [['timeFieldA', 'desc']]); + expect(result.columns).toEqual([]); + expect(result.sort).toEqual([['timeFieldB', 'desc']]); + }); + test('remove sorting by timefield when switching to an index pattern without timefield that contains the timefield column', async () => { + // Why: timefield column is prepended, keeping the sort, user would need to add the column to remove sorting in legacy grid + const current = getIndexPattern('a', 'timeFieldA', ['timeFieldA']); + const next = getIndexPattern('b', '', ['timeFieldA']); + + const result = getSwitchIndexPatternAppState(current, next, [], [['timeFieldA', 'desc']]); + expect(result.columns).toEqual([]); + expect(result.sort).toEqual([]); + }); + test('add sorting by timefield when switching from an index pattern without timefield to an indexpattern with timefield', async () => { + const current = getIndexPattern('b', '', ['timeFieldA']); + const next = getIndexPattern('a', 'timeFieldA', ['timeFieldA']); + + const result = getSwitchIndexPatternAppState(current, next, [], []); + expect(result.columns).toEqual([]); + expect(result.sort).toEqual([['timeFieldA', 'desc']]); + }); }); diff --git a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts index 32d0bda4260e1..9756fc8fd448b 100644 --- a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts +++ b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts @@ -20,7 +20,7 @@ export function getSwitchIndexPatternAppState( currentColumns: string[], currentSort: SortPairArr[], modifyColumns: boolean = true, - useNewFieldsApi: boolean = false + sortDirection: string = 'desc' ) { const nextColumns = modifyColumns ? currentColumns.filter( @@ -28,12 +28,20 @@ export function getSwitchIndexPatternAppState( nextIndexPattern.fields.getByName(column) || !currentIndexPattern.fields.getByName(column) ) : currentColumns; - const nextSort = getSortArray(currentSort, nextIndexPattern); - const defaultColumns = useNewFieldsApi ? [] : ['_source']; - const columns = nextColumns.length ? nextColumns : defaultColumns; + const columns = nextColumns.length ? nextColumns : []; + // when switching from an index pattern with timeField to an index pattern without timeField + // filter out sorting by timeField in case it is set. index patterns without timeField don't + // prepend this field in the table, so in legacy grid you would need to add this column to + // remove sorting + const nextSort = getSortArray(currentSort, nextIndexPattern).filter((value) => { + return nextIndexPattern.timeFieldName || value[0] !== currentIndexPattern.timeFieldName; + }); return { index: nextIndexPattern.id, columns, - sort: nextSort, + sort: + nextIndexPattern.timeFieldName && !nextSort.length + ? [[nextIndexPattern.timeFieldName, sortDirection]] + : nextSort, }; } From b337d49bcbb5c9088971f012335abc705e07763f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 23 Feb 2021 11:26:12 -0500 Subject: [PATCH 36/70] Catch-up release documentation (#92411) * Initial commit * Undo custom id docs (done in separate PR) * Update docs, a bit * fix create api doc * Update docs/user/alerting/action-types/email.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update docs/user/alerting/alerting-getting-started.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Indent warning Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/api/actions-and-connectors/create.asciidoc | 2 ++ docs/user/alerting/action-types/email.asciidoc | 2 ++ docs/user/alerting/alerting-getting-started.asciidoc | 1 + 3 files changed, 5 insertions(+) diff --git a/docs/api/actions-and-connectors/create.asciidoc b/docs/api/actions-and-connectors/create.asciidoc index af5ddd050e40e..22f360fe63eb3 100644 --- a/docs/api/actions-and-connectors/create.asciidoc +++ b/docs/api/actions-and-connectors/create.asciidoc @@ -27,6 +27,8 @@ Creates an action. `secrets`:: (Required, object) The secrets configuration for the action. Secrets configuration properties vary depending on the action type. For information about the secrets configuration properties, refer to <>. ++ +WARNING: Remember these values. You must provide them each time you call the <> API. [[actions-and-connectors-api-create-request-codes]] ==== Response code diff --git a/docs/user/alerting/action-types/email.asciidoc b/docs/user/alerting/action-types/email.asciidoc index 3562be1e405f6..8a5d8d156245e 100644 --- a/docs/user/alerting/action-types/email.asciidoc +++ b/docs/user/alerting/action-types/email.asciidoc @@ -4,6 +4,8 @@ The email action type uses the SMTP protocol to send mail message, using an integration of https://nodemailer.com/[Nodemailer]. Email message text is sent as both plain text and html text. +NOTE: For emails to have a footer with a link back to {kib}, set the <> configuration setting. + [float] [[email-connector-configuration]] ==== Connector configuration diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index 5bd54ae93be69..9b402e6cb9c49 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -158,6 +158,7 @@ Pre-packaged *alert types* simplify setup, hide the details complex domain-speci If you are using an *on-premises* Elastic Stack deployment: * In the kibana.yml configuration file, add the <> setting. +* For emails to have a footer with a link back to {kib}, set the <> configuration setting. If you are using an *on-premises* Elastic Stack deployment with <>: From 45155f089d96853ad489fdae149a52272d20529a Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Tue, 23 Feb 2021 17:34:59 +0100 Subject: [PATCH 37/70] [ML] Show mini histograms by default if row count below threshold. (#92021) Show mini histograms by default if row count below threshold of 10000 docs. --- x-pack/plugins/ml/common/index.ts | 2 +- x-pack/plugins/ml/common/types/es_client.ts | 8 +++++- .../components/data_grid/common.ts | 1 + .../components/data_grid/data_grid.tsx | 5 ++-- .../application/components/data_grid/index.ts | 1 + .../application/components/data_grid/types.ts | 10 ++++++- .../components/data_grid/use_data_grid.tsx | 28 +++++++++++++++++-- .../common/get_index_data.ts | 2 ++ .../configuration_step_form.tsx | 25 +++++++++++++---- .../configuration_step/use_saved_search.ts | 18 ++++++++++-- .../hooks/use_index_data.ts | 19 +++++++------ .../expandable_section_results.tsx | 11 ++++++-- x-pack/plugins/ml/public/index.ts | 1 + .../job_validation/job_validation.test.ts | 4 ++- .../transform/common/shared_imports.ts | 3 +- .../public/__mocks__/shared_imports.ts | 2 +- .../public/app/hooks/__mocks__/use_api.ts | 2 +- .../public/app/hooks/use_index_data.ts | 2 ++ .../public/app/hooks/use_pivot_data.ts | 6 +++- .../transform/public/shared_imports.ts | 1 + x-pack/test/accessibility/apps/ml.ts | 2 +- .../outlier_detection_creation.ts | 2 +- .../apps/transform/creation_index_pattern.ts | 2 +- .../ml/data_frame_analytics_creation.ts | 10 ++++--- .../functional/services/transform/wizard.ts | 10 ++++--- 25 files changed, 135 insertions(+), 42 deletions(-) diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index 9c8131a98f89f..c049b68990d2d 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export { SearchResponse7 } from './types/es_client'; +export { HitsTotalRelation, SearchResponse7, HITS_TOTAL_RELATION } from './types/es_client'; export { ANOMALY_SEVERITY, ANOMALY_THRESHOLD, SEVERITY_COLORS } from './constants/anomalies'; export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; export { composeValidators, patternValidator } from './util/validators'; diff --git a/x-pack/plugins/ml/common/types/es_client.ts b/x-pack/plugins/ml/common/types/es_client.ts index 767e8a18cb98d..0674ec6001159 100644 --- a/x-pack/plugins/ml/common/types/es_client.ts +++ b/x-pack/plugins/ml/common/types/es_client.ts @@ -7,13 +7,19 @@ import { SearchResponse, ShardsResponse } from 'elasticsearch'; +export const HITS_TOTAL_RELATION = { + EQ: 'eq', + GTE: 'gte', +} as const; +export type HitsTotalRelation = typeof HITS_TOTAL_RELATION[keyof typeof HITS_TOTAL_RELATION]; + // The types specified in `@types/elasticsearch` are out of date and still have `total: number`. interface SearchResponse7Hits { hits: SearchResponse['hits']['hits']; max_score: number; total: { value: number; - relation: string; + relation: HitsTotalRelation; }; } export interface SearchResponse7 { diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index 069c13df2470f..c2a1af4824c2d 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -53,6 +53,7 @@ import { RuntimeMappings } from '../../../../common/types/fields'; import { isPopulatedObject } from '../../../../common/util/object_utils'; export const INIT_MAX_COLUMNS = 10; +export const COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED = 10000; export const euiDataGridStyle: EuiDataGridStyle = { border: 'all', diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx index 5dad9801eb644..f4c6a41f697c9 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx @@ -312,15 +312,16 @@ export const DataGrid: FC = memo( })} > {i18n.translate('xpack.ml.dataGrid.histogramButtonText', { defaultMessage: 'Histogram charts', diff --git a/x-pack/plugins/ml/public/application/components/data_grid/index.ts b/x-pack/plugins/ml/public/application/components/data_grid/index.ts index 79a8d65f9905a..a3f1995736624 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/index.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/index.ts @@ -23,6 +23,7 @@ export { DataGridItem, EsSorting, RenderCellValue, + RowCountRelation, UseDataGridReturnType, UseIndexDataReturnType, } from './types'; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/types.ts b/x-pack/plugins/ml/public/application/components/data_grid/types.ts index e57579809f7d3..77c7bdb385469 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/types.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/types.ts @@ -10,6 +10,7 @@ import { Dispatch, SetStateAction } from 'react'; import { EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui'; import { Dictionary } from '../../../../common/types/common'; +import { HitsTotalRelation } from '../../../../common/types/es_client'; import { INDEX_STATUS } from '../../data_frame_analytics/common/analytics'; @@ -19,6 +20,10 @@ import { FeatureImportanceBaseline } from '../../../../common/types/feature_impo export type ColumnId = string; export type DataGridItem = Record; +// `undefined` is used to indicate a non-initialized state. +export type ChartsVisible = boolean | undefined; +export type RowCountRelation = HitsTotalRelation | undefined; + export type IndexPagination = Pick; export type OnChangeItemsPerPage = (pageSize: any) => void; @@ -60,6 +65,7 @@ export interface UseIndexDataReturnType | 'setPagination' | 'setVisibleColumns' | 'rowCount' + | 'rowCountRelation' | 'sortingColumns' | 'status' | 'tableItems' @@ -73,7 +79,7 @@ export interface UseIndexDataReturnType } export interface UseDataGridReturnType { - chartsVisible: boolean; + chartsVisible: ChartsVisible; chartsButtonVisible: boolean; columnsWithCharts: EuiDataGridColumn[]; errorMessage: string; @@ -85,11 +91,13 @@ export interface UseDataGridReturnType { pagination: IndexPagination; resetPagination: () => void; rowCount: number; + rowCountRelation: RowCountRelation; setColumnCharts: Dispatch>; setErrorMessage: Dispatch>; setNoDataMessage: Dispatch>; setPagination: Dispatch>; setRowCount: Dispatch>; + setRowCountRelation: Dispatch>; setSortingColumns: Dispatch>; setStatus: Dispatch>; setTableItems: Dispatch>; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx index b3a034d6614fc..4129f3a01bce9 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx @@ -9,17 +9,21 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui'; +import { HITS_TOTAL_RELATION } from '../../../../common/types/es_client'; + import { INDEX_STATUS } from '../../data_frame_analytics/common'; import { ColumnChart } from './column_chart'; -import { INIT_MAX_COLUMNS } from './common'; +import { COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED, INIT_MAX_COLUMNS } from './common'; import { + ChartsVisible, ColumnId, DataGridItem, IndexPagination, OnChangeItemsPerPage, OnChangePage, OnSort, + RowCountRelation, UseDataGridReturnType, } from './types'; import { ChartData } from './use_column_chart'; @@ -36,14 +40,17 @@ export const useDataGrid = ( const [errorMessage, setErrorMessage] = useState(''); const [status, setStatus] = useState(INDEX_STATUS.UNUSED); const [rowCount, setRowCount] = useState(0); + const [rowCountRelation, setRowCountRelation] = useState(undefined); const [columnCharts, setColumnCharts] = useState([]); const [tableItems, setTableItems] = useState([]); const [pagination, setPagination] = useState(defaultPagination); const [sortingColumns, setSortingColumns] = useState([]); - const [chartsVisible, setChartsVisible] = useState(false); + const [chartsVisible, setChartsVisible] = useState(undefined); const toggleChartVisibility = () => { - setChartsVisible(!chartsVisible); + if (chartsVisible !== undefined) { + setChartsVisible(!chartsVisible); + } }; const onChangeItemsPerPage: OnChangeItemsPerPage = useCallback((pageSize) => { @@ -131,6 +138,19 @@ export const useDataGrid = ( }); }, [columns, columnCharts, chartsVisible, JSON.stringify(visibleColumns)]); + // Initialize the mini histogram charts toggle button. + // On load `chartsVisible` is set to `undefined`, the button will be disabled. + // Once we know how many rows have been returned, + // we decide whether to show or hide the charts by default. + useEffect(() => { + if (chartsVisible === undefined && rowCount > 0 && rowCountRelation !== undefined) { + setChartsVisible( + rowCount <= COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED && + rowCountRelation !== HITS_TOTAL_RELATION.GTE + ); + } + }, [chartsVisible, rowCount, rowCountRelation]); + return { chartsVisible, chartsButtonVisible: true, @@ -144,11 +164,13 @@ export const useDataGrid = ( pagination, resetPagination, rowCount, + rowCountRelation, setColumnCharts, setErrorMessage, setNoDataMessage, setPagination, setRowCount, + setRowCountRelation, setSortingColumns, setStatus, setTableItems, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts index 47af92c790728..d0bcbd2ff63b4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts @@ -28,6 +28,7 @@ export const getIndexData = async ( pagination, setErrorMessage, setRowCount, + setRowCountRelation, setStatus, setTableItems, sortingColumns, @@ -64,6 +65,7 @@ export const getIndexData = async ( if (!options.didCancel) { setRowCount(resp.hits.total.value); + setRowCountRelation(resp.hits.total.relation); setTableItems( resp.hits.hits.map((d) => getProcessedFields(d.fields, (key: string) => diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx index 07331c208482a..aa008192bbce0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx @@ -49,13 +49,22 @@ import { DataGrid } from '../../../../../components/data_grid'; import { fetchExplainData } from '../shared'; import { useIndexData } from '../../hooks'; import { ExplorationQueryBar } from '../../../analytics_exploration/components/exploration_query_bar'; -import { useSavedSearch } from './use_saved_search'; +import { useSavedSearch, SavedSearchQuery } from './use_saved_search'; import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search'; import { ExplorationQueryBarProps } from '../../../analytics_exploration/components/exploration_query_bar/exploration_query_bar'; import { Query } from '../../../../../../../../../../src/plugins/data/common/query'; import { ScatterplotMatrix } from '../../../../../components/scatterplot_matrix'; +function getIndexDataQuery(savedSearchQuery: SavedSearchQuery, jobConfigQuery: any) { + // Return `undefined` if savedSearchQuery itself is `undefined`, meaning it hasn't been initialized yet. + if (savedSearchQuery === undefined) { + return; + } + + return savedSearchQuery !== null ? savedSearchQuery : jobConfigQuery; +} + const requiredFieldsErrorText = i18n.translate( 'xpack.ml.dataframe.analytics.createWizard.requiredFieldsErrorMessage', { @@ -130,7 +139,7 @@ export const ConfigurationStepForm: FC = ({ const indexData = useIndexData( currentIndexPattern, - savedSearchQuery !== undefined ? savedSearchQuery : jobConfigQuery, + getIndexDataQuery(savedSearchQuery, jobConfigQuery), toastNotifications ); @@ -296,7 +305,7 @@ export const ConfigurationStepForm: FC = ({ }, []); useEffect(() => { - if (savedSearchQueryStr !== undefined) { + if (typeof savedSearchQueryStr === 'string') { setFormState({ jobConfigQuery: savedSearchQuery, jobConfigQueryString: savedSearchQueryStr }); } }, [JSON.stringify(savedSearchQuery), savedSearchQueryStr]); @@ -343,12 +352,16 @@ export const ConfigurationStepForm: FC = ({ !dependentVariableEmpty)) && scatterplotFieldOptions.length > 1; + // Don't render until `savedSearchQuery` has been initialized. + // `undefined` means uninitialized, `null` means initialized but not used. + if (savedSearchQuery === undefined) return null; + return ( - {savedSearchQuery === undefined && ( + {savedSearchQuery === null && ( = ({ - {savedSearchQuery !== undefined && ( + {savedSearchQuery !== null && ( {i18n.translate('xpack.ml.dataframe.analytics.create.savedSearchLabel', { defaultMessage: 'Saved search', @@ -373,7 +386,7 @@ export const ConfigurationStepForm: FC = ({ )} - {savedSearchQuery !== undefined + {savedSearchQuery !== null ? currentSavedSearch?.attributes.title : currentIndexPattern.title} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts index 7c3349c7bc1f9..551a576ac619b 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts @@ -11,9 +11,20 @@ import { esQuery, esKuery } from '../../../../../../../../../../src/plugins/data import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search'; import { getQueryFromSavedSearch } from '../../../../../util/index_utils'; +// `undefined` is used for a non-initialized state +// `null` is set if no saved search is used +export type SavedSearchQuery = Record | null | undefined; +export type SavedSearchQueryStr = + | string + | { + [key: string]: any; + } + | null + | undefined; + export function useSavedSearch() { - const [savedSearchQuery, setSavedSearchQuery] = useState(undefined); - const [savedSearchQueryStr, setSavedSearchQueryStr] = useState(undefined); + const [savedSearchQuery, setSavedSearchQuery] = useState(undefined); + const [savedSearchQueryStr, setSavedSearchQueryStr] = useState(undefined); const mlContext = useMlContext(); const { currentSavedSearch, currentIndexPattern, kibanaConfig } = mlContext; @@ -37,6 +48,9 @@ export function useSavedSearch() { setSavedSearchQuery(qry); setSavedSearchQueryStr(qryString); + } else { + setSavedSearchQuery(null); + setSavedSearchQueryStr(null); } }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts index 8d6fc25a36ec6..ecda624c71d98 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts @@ -36,7 +36,7 @@ type IndexSearchResponse = SearchResponse7; export const useIndexData = ( indexPattern: IndexPattern, - query: any, + query: Record | undefined, toastNotifications: CoreSetup['notifications']['toasts'] ): UseIndexDataReturnType => { const indexPatternFields = useMemo(() => getFieldsFromKibanaIndexPattern(indexPattern), [ @@ -59,6 +59,7 @@ export const useIndexData = ( resetPagination, setErrorMessage, setRowCount, + setRowCountRelation, setStatus, setTableItems, sortingColumns, @@ -81,8 +82,7 @@ export const useIndexData = ( const esSearchRequest = { index: indexPattern.title, body: { - // Instead of using the default query (`*`), fall back to a more efficient `match_all` query. - query, // isDefaultQuery(query) ? matchAllQuery : query, + query, from: pagination.pageIndex * pagination.pageSize, size: pagination.pageSize, fields: ['*'], @@ -97,6 +97,7 @@ export const useIndexData = ( const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields)); setRowCount(resp.hits.total.value); + setRowCountRelation(resp.hits.total.relation); setTableItems(docs); setStatus(INDEX_STATUS.LOADED); } catch (e) { @@ -106,7 +107,9 @@ export const useIndexData = ( }; useEffect(() => { - getIndexData(); + if (query !== undefined) { + getIndexData(); + } // custom comparison }, [indexPattern.title, indexPatternFields, JSON.stringify([query, pagination, sortingColumns])]); @@ -114,7 +117,7 @@ export const useIndexData = ( indexPattern, ]); - const fetchColumnChartsData = async function () { + const fetchColumnChartsData = async function (fieldHistogramsQuery: Record) { try { const columnChartsData = await dataLoader.loadFieldHistograms( columns @@ -123,7 +126,7 @@ export const useIndexData = ( fieldName: cT.id, type: getFieldType(cT.schema), })), - query + fieldHistogramsQuery ); dataGrid.setColumnCharts(columnChartsData); } catch (e) { @@ -132,8 +135,8 @@ export const useIndexData = ( }; useEffect(() => { - if (dataGrid.chartsVisible) { - fetchColumnChartsData(); + if (dataGrid.chartsVisible && query !== undefined) { + fetchColumnChartsData(query); } // custom comparison }, [ diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx index e0080d2a881a7..654af03d102e5 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx @@ -18,10 +18,15 @@ import { isClassificationAnalysis, isRegressionAnalysis, } from '../../../../../../../common/util/analytics_utils'; +import { HITS_TOTAL_RELATION } from '../../../../../../../common/types/es_client'; import { getToastNotifications } from '../../../../../util/dependency_cache'; import { useColorRange, ColorRangeLegend } from '../../../../../components/color_range_legend'; -import { DataGrid, UseIndexDataReturnType } from '../../../../../components/data_grid'; +import { + DataGrid, + RowCountRelation, + UseIndexDataReturnType, +} from '../../../../../components/data_grid'; import { SavedSearchQuery } from '../../../../../contexts/ml'; import { @@ -59,6 +64,7 @@ const getResultsSectionHeaderItems = ( status: INDEX_STATUS, tableItems: Array>, rowCount: number, + rowCountRelation: RowCountRelation, colorRange?: ReturnType ): ExpandableSectionProps['headerItems'] => { return columnsWithCharts.length > 0 && (tableItems.length > 0 || status === INDEX_STATUS.LOADED) @@ -71,7 +77,7 @@ const getResultsSectionHeaderItems = ( defaultMessage="Total docs" /> ), - value: rowCount, + value: `${rowCountRelation === HITS_TOTAL_RELATION.GTE ? '>' : ''}${rowCount}`, }, ...(colorRange !== undefined ? [ @@ -120,6 +126,7 @@ export const ExpandableSectionResults: FC = ({ status, tableItems, indexData.rowCount, + indexData.rowCountRelation, colorRange ); const analysisType = diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index c88ce2d7f95d2..9280f4603b343 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -50,6 +50,7 @@ export { getSeverityType, getFormattedSeverityScore, } from '../common/util/anomaly_utils'; +export { HITS_TOTAL_RELATION } from '../common/types/es_client'; export { ANOMALY_SEVERITY } from '../common'; export { useMlHref, ML_PAGES, MlUrlGenerator } from './ml_url_generator'; diff --git a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts index ec2d852d0e29b..c3c3d52465d40 100644 --- a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts +++ b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts @@ -9,11 +9,13 @@ import { IScopedClusterClient } from 'kibana/server'; import { validateJob, ValidateJobPayload } from './job_validation'; import { JobValidationMessage } from '../../../common/constants/messages'; +import { HITS_TOTAL_RELATION } from '../../../common/types/es_client'; import type { MlClient } from '../../lib/ml_client'; const callAs = { fieldCaps: () => Promise.resolve({ body: { fields: [] } }), - search: () => Promise.resolve({ body: { hits: { total: { value: 1, relation: 'eq' } } } }), + search: () => + Promise.resolve({ body: { hits: { total: { value: 1, relation: HITS_TOTAL_RELATION.EQ } } } }), }; const mlClusterClient = ({ diff --git a/x-pack/plugins/transform/common/shared_imports.ts b/x-pack/plugins/transform/common/shared_imports.ts index a2fde05a54403..4506083a1876f 100644 --- a/x-pack/plugins/transform/common/shared_imports.ts +++ b/x-pack/plugins/transform/common/shared_imports.ts @@ -5,5 +5,6 @@ * 2.0. */ -export type { SearchResponse7 } from '../../ml/common'; +export type { HitsTotalRelation, SearchResponse7 } from '../../ml/common'; +export { HITS_TOTAL_RELATION } from '../../ml/common'; export { composeValidators, patternValidator } from '../../ml/common'; diff --git a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts index 628cabec5ecc1..00a92865789ff 100644 --- a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts +++ b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts @@ -16,4 +16,4 @@ export const useRequest = jest.fn(() => ({ export const createSavedSearchesLoader = jest.fn(); // just passing through the reimports -export { getMlSharedImports } from '../../../ml/public'; +export { getMlSharedImports, HITS_TOTAL_RELATION } from '../../../ml/public'; diff --git a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts index ac5b3e2592adc..7aaca793c2a1f 100644 --- a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts @@ -140,7 +140,7 @@ const apiFactory = () => ({ hits: [], total: { value: 0, - relation: 'the-relation', + relation: 'eq', }, max_score: 0, }, diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index abc63d886dbcc..dde4c7eb0f3a0 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -87,6 +87,7 @@ export const useIndexData = ( setColumnCharts, setErrorMessage, setRowCount, + setRowCountRelation, setStatus, setTableItems, sortingColumns, @@ -135,6 +136,7 @@ export const useIndexData = ( const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields)); setRowCount(resp.hits.total.value); + setRowCountRelation(resp.hits.total.relation); setTableItems(docs); setStatus(INDEX_STATUS.LOADED); }; diff --git a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts index 62b3a077df5e6..32d8ed16a643b 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts @@ -18,7 +18,7 @@ import type { PreviewMappingsProperties } from '../../../common/api_schemas/tran import { isPostTransformsPreviewResponseSchema } from '../../../common/api_schemas/type_guards'; import { getNestedProperty } from '../../../common/utils/object_utils'; -import { RenderCellValue, UseIndexDataReturnType } from '../../shared_imports'; +import { RenderCellValue, UseIndexDataReturnType, HITS_TOTAL_RELATION } from '../../shared_imports'; import { getErrorMessage } from '../../../common/utils/errors'; import { useAppDependencies } from '../app_dependencies'; @@ -117,6 +117,7 @@ export const usePivotData = ( setErrorMessage, setNoDataMessage, setRowCount, + setRowCountRelation, setStatus, setTableItems, sortingColumns, @@ -127,6 +128,7 @@ export const usePivotData = ( if (!validationStatus.isValid) { setTableItems([]); setRowCount(0); + setRowCountRelation(HITS_TOTAL_RELATION.EQ); setNoDataMessage(validationStatus.errorMessage!); return; } @@ -147,6 +149,7 @@ export const usePivotData = ( setErrorMessage(getErrorMessage(resp)); setTableItems([]); setRowCount(0); + setRowCountRelation(HITS_TOTAL_RELATION.EQ); setPreviewMappingsProperties({}); setStatus(INDEX_STATUS.ERROR); return; @@ -154,6 +157,7 @@ export const usePivotData = ( setTableItems(resp.preview); setRowCount(resp.preview.length); + setRowCountRelation(HITS_TOTAL_RELATION.EQ); setPreviewMappingsProperties(resp.generated_dest_index.mappings.properties); setStatus(INDEX_STATUS.LOADED); diff --git a/x-pack/plugins/transform/public/shared_imports.ts b/x-pack/plugins/transform/public/shared_imports.ts index 1a219e97ddcf8..ddf5cf7cb5cb1 100644 --- a/x-pack/plugins/transform/public/shared_imports.ts +++ b/x-pack/plugins/transform/public/shared_imports.ts @@ -15,6 +15,7 @@ export { UseIndexDataReturnType, EsSorting, RenderCellValue, + HITS_TOTAL_RELATION, } from '../../ml/public'; import { XJson } from '../../../../src/plugins/es_ui_shared/public'; diff --git a/x-pack/test/accessibility/apps/ml.ts b/x-pack/test/accessibility/apps/ml.ts index 0dbc7cbb041d7..323fe5d783c70 100644 --- a/x-pack/test/accessibility/apps/ml.ts +++ b/x-pack/test/accessibility/apps/ml.ts @@ -265,7 +265,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('displays the source data preview'); await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); await ml.testExecution.logTestStep('enables the source data preview histogram charts'); - await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(); + await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true); await ml.testExecution.logTestStep('displays the include fields selection'); await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); // EuiDataGrid does not have row roles diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index 8b291fa36867a..33bb05abbbb8d 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -117,7 +117,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); await ml.testExecution.logTestStep('enables the source data preview histogram charts'); - await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(); + await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true); await ml.testExecution.logTestStep('displays the source data preview histogram charts'); await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramCharts( diff --git a/x-pack/test/functional/apps/transform/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation_index_pattern.ts index c28b3cfec85ac..fbd98e5067387 100644 --- a/x-pack/test/functional/apps/transform/creation_index_pattern.ts +++ b/x-pack/test/functional/apps/transform/creation_index_pattern.ts @@ -396,7 +396,7 @@ export default function ({ getService }: FtrProviderContext) { await transform.wizard.assertAdvancedQueryEditorSwitchCheckState(false); await transform.testExecution.logTestStep('enables the index preview histogram charts'); - await transform.wizard.enableIndexPreviewHistogramCharts(); + await transform.wizard.enableIndexPreviewHistogramCharts(true); await transform.testExecution.logTestStep('displays the index preview histogram charts'); await transform.wizard.assertIndexPreviewHistogramCharts( diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 66c2599127431..509f6170a20f1 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -116,10 +116,12 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( await testSubjects.existOrFail('mlAnalyticsCreationDataGridHistogramButton'); }, - async enableSourceDataPreviewHistogramCharts() { - await this.assertSourceDataPreviewHistogramChartButtonCheckState(false); - await testSubjects.click('mlAnalyticsCreationDataGridHistogramButton'); - await this.assertSourceDataPreviewHistogramChartButtonCheckState(true); + async enableSourceDataPreviewHistogramCharts(expectedDefaultButtonState: boolean) { + await this.assertSourceDataPreviewHistogramChartButtonCheckState(expectedDefaultButtonState); + if (expectedDefaultButtonState === false) { + await testSubjects.click('mlAnalyticsCreationDataGridHistogramButton'); + await this.assertSourceDataPreviewHistogramChartButtonCheckState(true); + } }, async assertSourceDataPreviewHistogramChartButtonCheckState(expectedCheckState: boolean) { diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 518accdeaf47e..95695656130a7 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -177,10 +177,12 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) { await testSubjects.existOrFail('transformIndexPreviewHistogramButton'); }, - async enableIndexPreviewHistogramCharts() { - await this.assertIndexPreviewHistogramChartButtonCheckState(false); - await testSubjects.click('transformIndexPreviewHistogramButton'); - await this.assertIndexPreviewHistogramChartButtonCheckState(true); + async enableIndexPreviewHistogramCharts(expectedDefaultButtonState: boolean) { + await this.assertIndexPreviewHistogramChartButtonCheckState(expectedDefaultButtonState); + if (expectedDefaultButtonState === false) { + await testSubjects.click('transformIndexPreviewHistogramButton'); + await this.assertIndexPreviewHistogramChartButtonCheckState(true); + } }, async assertIndexPreviewHistogramChartButtonCheckState(expectedCheckState: boolean) { From 0e6c38d630a485f3e46ff007636f2385ec6dcdbf Mon Sep 17 00:00:00 2001 From: igoristic Date: Tue, 23 Feb 2021 12:19:10 -0500 Subject: [PATCH 38/70] [Monitoring] Added cgroup option for APM cpu usage (#90873) * Added APM cpu cgroup * Fixed tests * Fixed i18n * Removed agent logic and fixed tests * Fixed test * Fixed tests and backup field * Removed backup field fix * Fixed cluster tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/apm/apm_metrics.tsx | 85 +++++++++++++++++++ .../components/apm/instance/instance.js | 60 +++---------- .../public/components/apm/instance/status.js | 2 +- .../public/components/apm/overview/index.js | 58 +++---------- .../components/cluster/overview/apm_panel.js | 2 +- .../plugins/monitoring/server/config.test.ts | 3 + x-pack/plugins/monitoring/server/config.ts | 3 + .../monitoring/server/lib/apm/get_apm_info.ts | 11 ++- .../server/lib/apm/get_apms_for_clusters.js | 3 + .../lib/cluster/get_clusters_from_request.js | 6 +- .../__snapshots__/metrics.test.js.snap | 49 +++++++++++ .../server/lib/metrics/apm/metrics.js | 26 ++++++ .../api/v1/apm/_get_apm_cluster_status.js | 9 +- .../server/routes/api/v1/apm/instance.js | 6 ++ .../server/routes/api/v1/apm/overview.js | 6 ++ .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../apis/monitoring/apm/fixtures/cluster.json | 3 + .../monitoring/apm/fixtures/instance.json | 3 + .../cluster/fixtures/multicluster.json | 9 ++ .../monitoring/cluster/fixtures/overview.json | 3 + .../standalone_cluster/fixtures/cluster.json | 3 + .../standalone_cluster/fixtures/clusters.json | 6 ++ 23 files changed, 252 insertions(+), 108 deletions(-) create mode 100644 x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx diff --git a/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx b/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx new file mode 100644 index 0000000000000..7efddcfe66b0b --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx @@ -0,0 +1,85 @@ +/* + * 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 from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiPage, + EuiPageBody, + EuiFlexGroup, + EuiPageContent, + EuiScreenReaderOnly, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +// @ts-ignore could not find declaration file +import { MonitoringTimeseriesContainer } from '../chart'; +// @ts-ignore could not find declaration file +import { Status } from './instance/status'; + +interface Props { + stats: unknown; + metrics: { [key: string]: unknown }; + seriesToShow: unknown[]; + title: string; +} + +const createCharts = (series: unknown[], props: Partial) => { + return series.map((data, index) => { + return ( + + + + ); + }); +}; + +export const ApmMetrics = ({ stats, metrics, seriesToShow, title, ...props }: Props) => { + const topSeries = [metrics.apm_cpu, metrics.apm_memory, metrics.apm_os_load]; + + return ( + + + +

+ +

+ + + + + + + +

+ {i18n.translate('xpack.monitoring.apm.metrics.topCharts.nonAgentTitle', { + defaultMessage: 'APM Server - Resource Usage', + })} +

+
+ + {createCharts(topSeries, props)} +
+ + + +

{title}

+
+ + {createCharts(seriesToShow, props)} +
+ + + ); +}; diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js index e66cca480ba44..f52ca7cf8ad49 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js +++ b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js @@ -6,66 +6,28 @@ */ import React from 'react'; -import { MonitoringTimeseriesContainer } from '../../chart'; -import { - EuiFlexItem, - EuiPanel, - EuiSpacer, - EuiPage, - EuiPageBody, - EuiFlexGroup, - EuiPageContent, - EuiScreenReaderOnly, -} from '@elastic/eui'; -import { Status } from './status'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { ApmMetrics } from '../apm_metrics'; -export function ApmServerInstance({ summary, metrics, ...props }) { +const title = i18n.translate('xpack.monitoring.apm.instance.panels.title', { + defaultMessage: 'APM Server - Metrics', +}); + +export function ApmServerInstance(props) { + const { metrics } = props; const seriesToShow = [ metrics.apm_requests, metrics.apm_responses_valid, - metrics.apm_responses_errors, metrics.apm_acm_request_count, - metrics.apm_acm_response, metrics.apm_acm_response_errors, - metrics.apm_output_events_rate_success, metrics.apm_output_events_rate_failure, - metrics.apm_transformations, - metrics.apm_cpu, - - metrics.apm_memory, - metrics.apm_os_load, ]; - const charts = seriesToShow.map((data, index) => ( - - - - )); - - return ( - - - -

- -

-
- - - - - - {charts} - -
-
- ); + const stats = props.summary; + const metricProps = { ...props, title, seriesToShow, stats }; + return ; } diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/status.js b/x-pack/plugins/monitoring/public/components/apm/instance/status.js index 2fb70edc87dc8..9a199d6f6f9c0 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instance/status.js +++ b/x-pack/plugins/monitoring/public/components/apm/instance/status.js @@ -15,7 +15,7 @@ import { CALCULATE_DURATION_SINCE } from '../../../../common/constants'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -export function Status({ alerts, stats }) { +export function Status({ alerts = null, stats }) { const { name, output, version, uptime, timeOfLastEvent } = stats; const metrics = [ diff --git a/x-pack/plugins/monitoring/public/components/apm/overview/index.js b/x-pack/plugins/monitoring/public/components/apm/overview/index.js index 2a57bb09c89a2..ec622535444bc 100644 --- a/x-pack/plugins/monitoring/public/components/apm/overview/index.js +++ b/x-pack/plugins/monitoring/public/components/apm/overview/index.js @@ -6,62 +6,24 @@ */ import React from 'react'; -import { MonitoringTimeseriesContainer } from '../../chart'; -import { - EuiSpacer, - EuiPage, - EuiFlexGroup, - EuiFlexItem, - EuiPageBody, - EuiPanel, - EuiPageContent, - EuiScreenReaderOnly, -} from '@elastic/eui'; -import { Status } from '../instances/status'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { ApmMetrics } from '../apm_metrics'; -export function ApmOverview({ stats, metrics, alerts, ...props }) { +const title = i18n.translate('xpack.monitoring.apm.overview.panels.title', { + defaultMessage: 'APM Server - Metrics', +}); + +export function ApmOverview(props) { + const { metrics } = props; const seriesToShow = [ metrics.apm_responses_valid, metrics.apm_responses_errors, - metrics.apm_output_events_rate_success, metrics.apm_output_events_rate_failure, - metrics.apm_requests, metrics.apm_transformations, - - metrics.apm_cpu, - metrics.apm_memory, - - metrics.apm_os_load, ]; - const charts = seriesToShow.map((data, index) => ( - - - - )); - - return ( - - - -

- -

-
- - - - - - {charts} - -
-
- ); + const metricProps = { ...props, title, seriesToShow }; + return ; } diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js index 7f72620cb02da..a8b71bbfb234d 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -135,7 +135,7 @@ export function ApmPanel(props) { {apmsTotal} }} + values={{ apmsTotal }} /> diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 0d366641d5760..c285ff27c5a63 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -56,6 +56,9 @@ describe('config schema', () => { "enabled": true, }, "container": Object { + "apm": Object { + "enabled": false, + }, "elasticsearch": Object { "enabled": false, }, diff --git a/x-pack/plugins/monitoring/server/config.ts b/x-pack/plugins/monitoring/server/config.ts index 978a571f8a586..860c564ce3249 100644 --- a/x-pack/plugins/monitoring/server/config.ts +++ b/x-pack/plugins/monitoring/server/config.ts @@ -40,6 +40,9 @@ export const configSchema = schema.object({ elasticsearch: schema.object({ enabled: schema.boolean({ defaultValue: false }), }), + apm: schema.object({ + enabled: schema.boolean({ defaultValue: false }), + }), logstash: schema.object({ enabled: schema.boolean({ defaultValue: false }), }), diff --git a/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts b/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts index 049dd21dc0314..df74d6b609f9c 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts +++ b/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts @@ -18,7 +18,11 @@ import { getTimeOfLastEvent } from './_get_time_of_last_event'; import { LegacyRequest } from '../../types'; import { ElasticsearchResponse } from '../../../common/types/es'; -export function handleResponse(response: ElasticsearchResponse, apmUuid: string) { +export function handleResponse( + response: ElasticsearchResponse, + apmUuid: string, + config: { get: (key: string) => string | undefined } +) { if (!response.hits || response.hits.hits.length === 0) { return {}; } @@ -59,6 +63,9 @@ export function handleResponse(response: ElasticsearchResponse, apmUuid: string) eventsEmitted: getDiffCalculation(eventsEmittedLast, eventsEmittedFirst), eventsDropped: getDiffCalculation(eventsDroppedLast, eventsDroppedFirst), bytesWritten: getDiffCalculation(bytesWrittenLast, bytesWrittenFirst), + config: { + container: config.get('monitoring.ui.container.apm.enabled'), + }, }; } @@ -138,7 +145,7 @@ export async function getApmInfo( }), ]); - const formattedResponse = handleResponse(response, apmUuid); + const formattedResponse = handleResponse(response, apmUuid, req.server.config()); return { ...formattedResponse, timeOfLastEvent, diff --git a/x-pack/plugins/monitoring/server/lib/apm/get_apms_for_clusters.js b/x-pack/plugins/monitoring/server/lib/apm/get_apms_for_clusters.js index aa494cdce3382..446cf19adf2a0 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/get_apms_for_clusters.js +++ b/x-pack/plugins/monitoring/server/lib/apm/get_apms_for_clusters.js @@ -73,6 +73,9 @@ export function getApmsForClusters(req, apmIndexPattern, clusters) { const formattedResponse = handleResponse(clusterUuid, response); return { ...formattedResponse, + config: { + container: config.get('monitoring.ui.container.apm.enabled'), + }, stats: { ...formattedResponse.stats, timeOfLastEvent, diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js index 0bed25a70d048..990a4df85c5fa 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js @@ -258,7 +258,11 @@ export async function getClustersFromRequest( : []; apmsByCluster.forEach((apm) => { const clusterIndex = findIndex(clusters, { cluster_uuid: apm.clusterUuid }); - set(clusters[clusterIndex], 'apm', apm.stats); + const { stats, config } = apm; + clusters[clusterIndex].apm = { + ...stats, + config, + }; }); // check ccr configuration diff --git a/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap b/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap index 74916fb0a0789..e9c89037c9053 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap +++ b/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap @@ -412,6 +412,55 @@ Object { "units": "/s", "uuidField": "cluster_uuid", }, + "apm_cgroup_cpu": QuotaMetric { + "aggs": Object { + "periods": Object { + "max": Object { + "field": "beats_stats.metrics.beat.cgroup.cpu.stats.periods", + }, + }, + "periods_deriv": Object { + "derivative": Object { + "buckets_path": "periods", + "gap_policy": "skip", + "unit": "1s", + }, + }, + "quota": Object { + "min": Object { + "field": "beats_stats.metrics.beat.cgroup.cpu.cfs.quota.us", + }, + }, + "usage": Object { + "max": Object { + "field": "beats_stats.metrics.beat.cgroup.cpuacct.total.ns", + }, + }, + "usage_deriv": Object { + "derivative": Object { + "buckets_path": "usage", + "gap_policy": "skip", + "unit": "1s", + }, + }, + }, + "app": "apm", + "calculation": [Function], + "derivative": true, + "description": "CPU Usage time compared to the CPU quota shown in percentage. If CPU quotas are not set, then no data will be shown.", + "field": "beats_stats.metrics.beat.cpu.total.value", + "fieldSource": "beats_stats.metrics.beat.cgroup", + "format": "0,0.[00]", + "label": "Cgroup CPU Utilization", + "metricAgg": "max", + "periodsField": "cpu.stats.periods", + "quotaField": "cpu.cfs.quota.us", + "timestampField": "beats_stats.timestamp", + "title": "CPU Utilization", + "units": "%", + "usageField": "cpuacct.total.ns", + "uuidField": "beats_stats.beat.uuid", + }, "apm_cpu_total": ApmCpuUtilizationMetric { "app": "apm", "calculation": [Function], diff --git a/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.js b/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.js index 66cb5159a21fb..ecbd4c4204be0 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.js +++ b/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.js @@ -8,6 +8,7 @@ import { LARGE_BYTES, LARGE_FLOAT } from '../../../../common/formatting'; import { ApmMetric, ApmCpuUtilizationMetric, ApmEventsRateClusterMetric } from './classes'; import { i18n } from '@kbn/i18n'; +import { QuotaMetric } from '../classes'; const instanceSystemLoadTitle = i18n.translate( 'xpack.monitoring.metrics.apmInstance.systemLoadTitle', @@ -39,6 +40,31 @@ export const metrics = { ), field: 'beats_stats.metrics.beat.cpu.total.value', }), + apm_cgroup_cpu: new QuotaMetric({ + app: 'apm', + ...ApmMetric.getMetricFields(), + fieldSource: 'beats_stats.metrics.beat.cgroup', + usageField: 'cpuacct.total.ns', + periodsField: 'cpu.stats.periods', + quotaField: 'cpu.cfs.quota.us', + field: 'beats_stats.metrics.beat.cpu.total.value', // backup field if quota is not configured + title: i18n.translate('xpack.monitoring.metrics.apmInstance.cpuUtilizationTitle', { + defaultMessage: 'CPU Utilization', + }), + label: i18n.translate( + 'xpack.monitoring.metrics.apmInstance.cpuUtilization.cgroupCpuUtilizationLabel', + { + defaultMessage: 'Cgroup CPU Utilization', + } + ), + description: i18n.translate( + 'xpack.monitoring.metrics.apmInstance.cpuUtilization.cgroupCpuUtilizationDescription', + { + defaultMessage: + 'CPU Usage time compared to the CPU quota shown in percentage. If CPU quotas are not set, then no data will be shown.', + } + ), + }), apm_system_os_load_1: new ApmMetric({ field: 'beats_stats.metrics.system.load.1', label: i18n.translate('xpack.monitoring.metrics.apmInstance.systemLoad.last1MinuteLabel', { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/apm/_get_apm_cluster_status.js b/x-pack/plugins/monitoring/server/routes/api/v1/apm/_get_apm_cluster_status.js index a7b586e550769..a28312de78af0 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/apm/_get_apm_cluster_status.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/apm/_get_apm_cluster_status.js @@ -5,10 +5,15 @@ * 2.0. */ -import { get } from 'lodash'; import { getApmsForClusters } from '../../../../lib/apm/get_apms_for_clusters'; export const getApmClusterStatus = (req, apmIndexPattern, { clusterUuid }) => { const clusters = [{ cluster_uuid: clusterUuid }]; - return getApmsForClusters(req, apmIndexPattern, clusters).then((apms) => get(apms, '[0].stats')); + return getApmsForClusters(req, apmIndexPattern, clusters).then((apms) => { + const [{ stats, config }] = apms; + return { + ...stats, + config, + }; + }); }; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.js b/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.js index a7c1872c35af8..4884b8151f61f 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.js @@ -39,6 +39,12 @@ export function apmInstanceRoute(server) { const ccs = req.payload.ccs; const apmIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_BEATS, ccs); + const showCgroupMetrics = config.get('monitoring.ui.container.apm.enabled'); + if (showCgroupMetrics) { + const metricCpu = metricSet.find((m) => m.name === 'apm_cpu'); + metricCpu.keys = ['apm_cgroup_cpu']; + } + try { const [metrics, apmSummary] = await Promise.all([ getMetrics(req, apmIndexPattern, metricSet, [ diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.js b/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.js index bca9150a8a8cd..7a772594b4bc2 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.js @@ -37,6 +37,12 @@ export function apmOverviewRoute(server) { const clusterUuid = req.params.clusterUuid; const apmIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_BEATS, ccs); + const showCgroupMetrics = config.get('monitoring.ui.container.apm.enabled'); + if (showCgroupMetrics) { + const metricCpu = metricSet.find((m) => m.name === 'apm_cpu'); + metricCpu.keys = ['apm_cgroup_cpu']; + } + try { const [stats, metrics] = await Promise.all([ getApmClusterStatus(req, apmIndexPattern, { clusterUuid }), diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8b3912bb93595..3496d7b0b52f0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -14451,7 +14451,6 @@ "xpack.monitoring.alerts.validation.threshold": "有効な数字が必要です。", "xpack.monitoring.alerts.writeThreadPoolRejections.description": "書き込みスレッドプールの拒否数がしきい値を超過するときにアラートを発行します。", "xpack.monitoring.apm.healthStatusLabel": "ヘルス: {status}", - "xpack.monitoring.apm.instance.heading": "APMサーバーインスタンス", "xpack.monitoring.apm.instance.pageTitle": "APMサーバーインスタンス:{instanceName}", "xpack.monitoring.apm.instance.routeTitle": "{apm} - インスタンス", "xpack.monitoring.apm.instance.status.lastEventDescription": "{timeOfLastEvent} 前", @@ -14480,7 +14479,6 @@ "xpack.monitoring.apm.instances.totalEventsRateTitle": "合計イベントレート", "xpack.monitoring.apm.instances.versionFilter": "バージョン", "xpack.monitoring.apm.instances.versionTitle": "バージョン", - "xpack.monitoring.apm.overview.heading": "APMサーバー概要", "xpack.monitoring.apm.overview.pageTitle": "APMサーバー概要", "xpack.monitoring.apm.overview.routeTitle": "APMサーバー", "xpack.monitoring.apmNavigation.instancesLinkText": "インスタンス", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2561d76bca1aa..0e87e36bef825 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -14492,7 +14492,6 @@ "xpack.monitoring.alerts.validation.threshold": "需要有效的数字。", "xpack.monitoring.alerts.writeThreadPoolRejections.description": "当写入线程池中的拒绝数量超过阈值时告警。", "xpack.monitoring.apm.healthStatusLabel": "运行状况:{status}", - "xpack.monitoring.apm.instance.heading": "APM 服务器实例", "xpack.monitoring.apm.instance.pageTitle": "APM 服务器实例:{instanceName}", "xpack.monitoring.apm.instance.routeTitle": "{apm} - 实例", "xpack.monitoring.apm.instance.status.lastEventDescription": "{timeOfLastEvent}前", @@ -14521,7 +14520,6 @@ "xpack.monitoring.apm.instances.totalEventsRateTitle": "事件合计速率", "xpack.monitoring.apm.instances.versionFilter": "版本", "xpack.monitoring.apm.instances.versionTitle": "版本", - "xpack.monitoring.apm.overview.heading": "APM 服务器概览", "xpack.monitoring.apm.overview.pageTitle": "APM 服务器概览", "xpack.monitoring.apm.overview.routeTitle": "APM 服务器", "xpack.monitoring.apmNavigation.instancesLinkText": "实例", diff --git a/x-pack/test/api_integration/apis/monitoring/apm/fixtures/cluster.json b/x-pack/test/api_integration/apis/monitoring/apm/fixtures/cluster.json index e54a1c2210d48..f56440f2e4c4f 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/fixtures/cluster.json +++ b/x-pack/test/api_integration/apis/monitoring/apm/fixtures/cluster.json @@ -6,6 +6,9 @@ "apms": { "total": 2 }, + "config": { + "container": false + }, "timeOfLastEvent": "2018-08-31T13:59:21.201Z" }, "metrics": { diff --git a/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json b/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json index f1747507b71d5..089ad3db54069 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json +++ b/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json @@ -899,6 +899,9 @@ "eventsEmitted": 6, "eventsDropped": 0, "bytesWritten": 10478, + "config": { + "container": false + }, "timeOfLastEvent": "2018-08-31T13:59:21.201Z" } } diff --git a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json index a000324d121ea..48861c88e86ad 100644 --- a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json +++ b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json @@ -98,6 +98,9 @@ "memTotal": null, "apms": { "total": null + }, + "config": { + "container": false } }, "alerts": { @@ -214,6 +217,9 @@ "memTotal": null, "apms": { "total": null + }, + "config": { + "container": false } }, "alerts": { @@ -326,6 +332,9 @@ "memTotal": null, "apms": { "total": null + }, + "config": { + "container": false } }, "alerts": { diff --git a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json index 15ff905478933..8fed03d9a8a34 100644 --- a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json +++ b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json @@ -112,6 +112,9 @@ "memTotal": null, "apms": { "total": null + }, + "config": { + "container": false } }, "isCcrEnabled": true, diff --git a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json index f0fe8c152b49f..b3b5b38faedb7 100644 --- a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json +++ b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json @@ -43,6 +43,9 @@ "memTotal": 0, "apms": { "total": 0 + }, + "config": { + "container": false } }, "isPrimary": false diff --git a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json index 7091e584344e7..602e6d5c2be4f 100644 --- a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json +++ b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json @@ -98,6 +98,9 @@ "memTotal": 0, "apms": { "total": 0 + }, + "config": { + "container": false } }, "alerts": { @@ -166,6 +169,9 @@ "memTotal": 0, "apms": { "total": 0 + }, + "config": { + "container": false } }, "alerts": { From 4891f7c09e23ddf20135c6d1f76c994a2dda0ad1 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 23 Feb 2021 18:43:47 +0100 Subject: [PATCH 39/70] [ML] Fix Anomaly detection alert condition validation (#92377) * [ML] config validation * [ML] validation messages * [ML] fix message * [ML] refactor resolveBucketSpanInSeconds * [ML] fix messages * [ML] change validation messages * [ML] change validation messages * [ML] double bucket span outside of resolveBucketSpanInSeconds * [ML] check only for started datafeed, update bucket span check --- .../types/guards.ts} | 10 +- .../plugins/ml/common/util/anomaly_utils.ts | 2 +- .../plugins/ml/common/util/job_utils.test.ts | 7 + x-pack/plugins/ml/common/util/job_utils.ts | 18 +- .../ml/public/alerting/config_validator.tsx | 95 ++++++++++ .../ml/public/alerting/job_selector.tsx | 22 +-- .../alerting/ml_anomaly_alert_trigger.tsx | 55 +++++- .../public/alerting/result_type_selector.tsx | 163 +++++++++--------- .../ml/server/lib/alerts/alerting_service.ts | 24 +-- 9 files changed, 274 insertions(+), 122 deletions(-) rename x-pack/plugins/ml/{server/lib/alerts/alerting_service.test.ts => common/types/guards.ts} (50%) create mode 100644 x-pack/plugins/ml/public/alerting/config_validator.tsx diff --git a/x-pack/plugins/ml/server/lib/alerts/alerting_service.test.ts b/x-pack/plugins/ml/common/types/guards.ts similarity index 50% rename from x-pack/plugins/ml/server/lib/alerts/alerting_service.test.ts rename to x-pack/plugins/ml/common/types/guards.ts index f029fa24f9607..ead91eafc2d4e 100644 --- a/x-pack/plugins/ml/server/lib/alerts/alerting_service.test.ts +++ b/x-pack/plugins/ml/common/types/guards.ts @@ -5,10 +5,6 @@ * 2.0. */ -import { resolveBucketSpanInSeconds } from './alerting_service'; - -describe('Alerting Service', () => { - test('should resolve maximum bucket interval', () => { - expect(resolveBucketSpanInSeconds(['15m', '1h', '6h', '90s'])).toBe(43200); - }); -}); +export function isDefined(argument: T | undefined | null): argument is T { + return argument !== undefined && argument !== null; +} diff --git a/x-pack/plugins/ml/common/util/anomaly_utils.ts b/x-pack/plugins/ml/common/util/anomaly_utils.ts index 68605f29c7be9..de1adfabcd7da 100644 --- a/x-pack/plugins/ml/common/util/anomaly_utils.ts +++ b/x-pack/plugins/ml/common/util/anomaly_utils.ts @@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n'; import { CONDITIONS_NOT_SUPPORTED_FUNCTIONS } from '../constants/detector_rule'; import { MULTI_BUCKET_IMPACT } from '../constants/multi_bucket_impact'; import { ANOMALY_SEVERITY, ANOMALY_THRESHOLD, SEVERITY_COLORS } from '../constants/anomalies'; -import { AnomalyRecordDoc } from '../types/anomalies'; +import type { AnomalyRecordDoc } from '../types/anomalies'; export interface SeverityType { id: ANOMALY_SEVERITY; diff --git a/x-pack/plugins/ml/common/util/job_utils.test.ts b/x-pack/plugins/ml/common/util/job_utils.test.ts index 64383ab243b8c..59f8c8a4dae3a 100644 --- a/x-pack/plugins/ml/common/util/job_utils.test.ts +++ b/x-pack/plugins/ml/common/util/job_utils.test.ts @@ -20,6 +20,7 @@ import { getSafeAggregationName, getLatestDataOrBucketTimestamp, getEarliestDatafeedStartTime, + resolveBucketSpanInSeconds, } from './job_utils'; import { CombinedJob, Job } from '../types/anomaly_detection_jobs'; import moment from 'moment'; @@ -602,4 +603,10 @@ describe('ML - job utils', () => { expect(getLatestDataOrBucketTimestamp(undefined, undefined)).toBe(undefined); }); }); + + describe('resolveBucketSpanInSeconds', () => { + test('should resolve maximum bucket interval', () => { + expect(resolveBucketSpanInSeconds(['15m', '1h', '6h', '90s'])).toBe(21600); + }); + }); }); diff --git a/x-pack/plugins/ml/common/util/job_utils.ts b/x-pack/plugins/ml/common/util/job_utils.ts index ab56726e160f7..4b80661f13c09 100644 --- a/x-pack/plugins/ml/common/util/job_utils.ts +++ b/x-pack/plugins/ml/common/util/job_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isEmpty, isEqual, each, pick } from 'lodash'; +import { each, isEmpty, isEqual, pick } from 'lodash'; import semverGte from 'semver/functions/gte'; import moment, { Duration } from 'moment'; // @ts-ignore @@ -16,7 +16,7 @@ import { ALLOWED_DATA_UNITS, JOB_ID_MAX_LENGTH } from '../constants/validation'; import { parseInterval } from './parse_interval'; import { maxLengthValidator } from './validators'; import { CREATED_BY_LABEL } from '../constants/new_job'; -import { CombinedJob, CustomSettings, Datafeed, JobId, Job } from '../types/anomaly_detection_jobs'; +import { CombinedJob, CustomSettings, Datafeed, Job, JobId } from '../types/anomaly_detection_jobs'; import { EntityField } from './anomaly_utils'; import { MlServerLimits } from '../types/ml_server_info'; import { JobValidationMessage, JobValidationMessageId } from '../constants/messages'; @@ -29,6 +29,7 @@ import { } from './datafeed_utils'; import { findAggField } from './validation_utils'; import { isPopulatedObject } from './object_utils'; +import { isDefined } from '../types/guards'; export interface ValidationResults { valid: boolean; @@ -801,3 +802,16 @@ export function splitIndexPatternNames(indexPatternName: string): string[] { ? indexPatternName.split(',').map((i) => i.trim()) : [indexPatternName]; } + +/** + * Resolves the longest bucket span from the list. + * @param bucketSpans Collection of bucket spans + */ +export function resolveBucketSpanInSeconds(bucketSpans: string[]): number { + return Math.max( + ...bucketSpans + .map((b) => parseInterval(b)) + .filter(isDefined) + .map((v) => v.asSeconds()) + ); +} diff --git a/x-pack/plugins/ml/public/alerting/config_validator.tsx b/x-pack/plugins/ml/public/alerting/config_validator.tsx new file mode 100644 index 0000000000000..5881a3b36dcbd --- /dev/null +++ b/x-pack/plugins/ml/public/alerting/config_validator.tsx @@ -0,0 +1,95 @@ +/* + * 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, { FC, useMemo } from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { parseInterval } from '../../common/util/parse_interval'; +import { CombinedJobWithStats } from '../../common/types/anomaly_detection_jobs'; +import { DATAFEED_STATE } from '../../common/constants/states'; +import { resolveBucketSpanInSeconds } from '../../common/util/job_utils'; + +interface ConfigValidatorProps { + alertInterval: string; + jobConfigs: CombinedJobWithStats[]; +} + +/** + * Validated alert configuration + */ +export const ConfigValidator: FC = React.memo( + ({ jobConfigs = [], alertInterval }) => { + const resultBucketSpanInSeconds = useMemo( + () => resolveBucketSpanInSeconds(jobConfigs.map((v) => v.analysis_config.bucket_span)), + [jobConfigs] + ); + + const resultBucketSpanString = + resultBucketSpanInSeconds % 60 === 0 + ? `${resultBucketSpanInSeconds / 60}m` + : `${resultBucketSpanInSeconds}s`; + + if (jobConfigs.length === 0) return null; + + const alertIntervalInSeconds = parseInterval(alertInterval)!.asSeconds(); + + const isAlertIntervalTooHigh = resultBucketSpanInSeconds < alertIntervalInSeconds; + + const jobWithoutStartedDatafeed = jobConfigs + .filter((job) => job.datafeed_config.state !== DATAFEED_STATE.STARTED) + .map((job) => job.job_id); + + const configContainsIssues = isAlertIntervalTooHigh || jobWithoutStartedDatafeed.length > 0; + + if (!configContainsIssues) return null; + + return ( + <> + + + } + color="warning" + size={'s'} + > +
    + {isAlertIntervalTooHigh ? ( +
  • + +
  • + ) : null} + + {jobWithoutStartedDatafeed.length > 0 ? ( +
  • + +
  • + ) : null} +
+
+ + + ); + } +); diff --git a/x-pack/plugins/ml/public/alerting/job_selector.tsx b/x-pack/plugins/ml/public/alerting/job_selector.tsx index 60bb7517406b8..11dd8362fd443 100644 --- a/x-pack/plugins/ml/public/alerting/job_selector.tsx +++ b/x-pack/plugins/ml/public/alerting/job_selector.tsx @@ -18,7 +18,7 @@ interface JobSelection { } export interface JobSelectorControlProps { - jobSelection?: JobSelection; + jobsAndGroupIds?: string[]; onChange: (jobSelection: JobSelection) => void; adJobsApiService: MlApiServices['jobs']; /** @@ -28,7 +28,7 @@ export interface JobSelectorControlProps { } export const JobSelectorControl: FC = ({ - jobSelection, + jobsAndGroupIds, onChange, adJobsApiService, errors, @@ -37,6 +37,14 @@ export const JobSelectorControl: FC = ({ const jobIds = useMemo(() => new Set(), []); const groupIds = useMemo(() => new Set(), []); + const selectedOptions = useMemo( + () => + (jobsAndGroupIds ?? []).map((v) => ({ + label: v, + })), + [jobsAndGroupIds] + ); + const fetchOptions = useCallback(async () => { try { const { @@ -71,10 +79,10 @@ export const JobSelectorControl: FC = ({ }, [adJobsApiService]); const onSelectionChange: EuiComboBoxProps['onChange'] = useCallback( - (selectedOptions) => { + (selectionUpdate) => { const selectedJobIds: JobId[] = []; const selectedGroupIds: string[] = []; - selectedOptions.forEach(({ label }: { label: string }) => { + selectionUpdate.forEach(({ label }: { label: string }) => { if (jobIds.has(label)) { selectedJobIds.push(label); } else if (groupIds.has(label)) { @@ -93,12 +101,6 @@ export const JobSelectorControl: FC = ({ fetchOptions(); }, []); - const selectedOptions = Object.values(jobSelection ?? {}) - .flat() - .map((v) => ({ - label: v, - })); - return ( void; setAlertProperty: (prop: string, update: Partial) => void; errors: Record; + alertInterval: string; } const MlAnomalyAlertTrigger: FC = ({ @@ -36,14 +40,18 @@ const MlAnomalyAlertTrigger: FC = ({ setAlertParams, setAlertProperty, errors, + alertInterval, }) => { const { services: { http }, + notifications: { toasts }, } = useMlKibana(); const mlHttpService = useMemo(() => new HttpService(http), [http]); const adJobsApiService = useMemo(() => jobsApiProvider(mlHttpService), [mlHttpService]); const alertingApiService = useMemo(() => alertingApiProvider(mlHttpService), [mlHttpService]); + const [jobConfigs, setJobConfigs] = useState([]); + const onAlertParamChange = useCallback( (param: T) => ( update: MlAnomalyDetectionAlertParams[T] @@ -53,6 +61,45 @@ const MlAnomalyAlertTrigger: FC = ({ [] ); + const jobsAndGroupIds: string[] = useMemo( + () => (Object.values(alertParams.jobSelection ?? {}) as string[][]).flat(), + [alertParams.jobSelection] + ); + + /** + * Extract alert related information based on the job selection + */ + const fetchJobsConfig = useCallback(async () => { + try { + const jobs = await adJobsApiService.jobs(jobsAndGroupIds); + setJobConfigs(jobs); + } catch (e) { + toasts.danger({ + title: i18n.translate('xpack.ml.anomalyDetectionAlert.errorFetchingJobs', { + defaultMessage: 'Unable to fetch jobs configuration', + }), + body: e.message, + toastLifeTimeMs: 5000, + }); + } + }, [jobsAndGroupIds]); + + const availableResultTypes = useMemo(() => { + if (jobConfigs.length === 0) return Object.values(ANOMALY_RESULT_TYPE); + + return (jobConfigs ?? []).some((v) => v.analysis_config.influencers.length > 0) + ? Object.values(ANOMALY_RESULT_TYPE) + : [ANOMALY_RESULT_TYPE.BUCKET, ANOMALY_RESULT_TYPE.RECORD]; + }, [jobConfigs]); + + useEffect( + function checkJobsConfiguration() { + if (jobsAndGroupIds.length === 0) return; + fetchJobsConfig(); + }, + [jobsAndGroupIds] + ); + useMount(function setDefaults() { const { jobSelection, ...rest } = alertParams; if (Object.keys(rest).length === 0) { @@ -70,13 +117,17 @@ const MlAnomalyAlertTrigger: FC = ({ return ( + + + void; } -export const ResultTypeSelector: FC = ({ - value: selectedResultType = [], - onChange, -}) => { - const resultTypeOptions = [ - { - value: ANOMALY_RESULT_TYPE.BUCKET, - title: , - description: ( - - ), - }, - { - value: ANOMALY_RESULT_TYPE.RECORD, - title: , - description: ( - - ), - }, - { - value: ANOMALY_RESULT_TYPE.INFLUENCER, - title: ( - - ), - description: ( - - ), - }, - ]; +export const ResultTypeSelector: FC = React.memo( + ({ value: selectedResultType = [], onChange, availableOption }) => { + const resultTypeOptions = useMemo(() => { + return [ + { + value: ANOMALY_RESULT_TYPE.BUCKET, + title: , + description: ( + + ), + }, + { + value: ANOMALY_RESULT_TYPE.RECORD, + title: , + description: ( + + ), + }, + { + value: ANOMALY_RESULT_TYPE.INFLUENCER, + title: ( + + ), + description: ( + + ), + }, + ].filter((v) => availableOption.includes(v.value)); + }, [availableOption]); - return ( - - } - > - - {resultTypeOptions.map(({ value, title, description }) => { - return ( - - {description}} - selectable={{ - onClick: () => { - if (selectedResultType === value) { - // don't allow de-select - return; - } - onChange(value); - }, - isSelected: value === selectedResultType, - }} - data-test-subj={`mlAnomalyAlertResult_${value}${ - value === selectedResultType ? '_selected' : '' - }`} - /> - - ); - })} - - - ); -}; + return ( + + } + > + + {resultTypeOptions.map(({ value, title, description }) => { + return ( + + {description}} + selectable={{ + onClick: () => { + if (selectedResultType === value) { + // don't allow de-select + return; + } + onChange(value); + }, + isSelected: value === selectedResultType, + }} + data-test-subj={`mlAnomalyAlertResult_${value}${ + value === selectedResultType ? '_selected' : '' + }`} + /> + + ); + })} + + + ); + } +); diff --git a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts index 6e7cd77e450bc..b6d0e9ae261c7 100644 --- a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts +++ b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts @@ -25,30 +25,11 @@ import { RecordAnomalyAlertDoc, TopHitsResultsKeys, } from '../../../common/types/alerts'; -import { parseInterval } from '../../../common/util/parse_interval'; import { AnomalyDetectionAlertContext } from './register_anomaly_detection_alert_type'; import { MlJobsResponse } from '../../../common/types/job_service'; import { ANOMALY_SCORE_MATCH_GROUP_ID } from '../../../common/constants/alerts'; import { getEntityFieldName, getEntityFieldValue } from '../../../common/util/anomaly_utils'; - -function isDefined(argument: T | undefined | null): argument is T { - return argument !== undefined && argument !== null; -} - -/** - * Resolves the longest bucket span from the list and multiply it by 2. - * @param bucketSpans Collection of bucket spans - */ -export function resolveBucketSpanInSeconds(bucketSpans: string[]): number { - return ( - Math.max( - ...bucketSpans - .map((b) => parseInterval(b)) - .filter(isDefined) - .map((v) => v.asSeconds()) - ) * 2 - ); -} +import { resolveBucketSpanInSeconds } from '../../../common/util/job_utils'; /** * Alerting related server-side methods @@ -313,7 +294,8 @@ export function alertingServiceProvider(mlClient: MlClient, esClient: Elasticsea * We need to check the biggest time range to make sure anomalies are not missed. */ const lookBackTimeInterval = `${Math.max( - resolveBucketSpanInSeconds(jobsResponse.map((v) => v.analysis_config.bucket_span)), + // Double the max bucket span + resolveBucketSpanInSeconds(jobsResponse.map((v) => v.analysis_config.bucket_span)) * 2, checkIntervalGap ? checkIntervalGap.asSeconds() : 0 )}s`; From 0cc3fa61ce242adc6af7ef6aa9ff9b6d855ced72 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 23 Feb 2021 18:54:12 +0100 Subject: [PATCH 40/70] =?UTF-8?q?Upgrade=20`merge-deep`=20dependency=20(`3?= =?UTF-8?q?.0.2`=20=E2=86=92=20`3.0.3`).=20(#92418)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 08bf4c8ea752c..4a3399ece1fd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20613,9 +20613,9 @@ meow@^8.0.0: yargs-parser "^20.2.3" merge-deep@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" - integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA== + version "3.0.3" + resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.3.tgz#1a2b2ae926da8b2ae93a0ac15d90cd1922766003" + integrity sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA== dependencies: arr-union "^3.1.0" clone-deep "^0.2.4" From e1166999e0f7624d083aaf7fc038d8393714ab6c Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Tue, 23 Feb 2021 12:55:42 -0500 Subject: [PATCH 41/70] [Enterprise Search] Shared unsaved changes prompt component (#92277) --- .../shared/unsaved_changes_prompt/index.ts | 8 ++ .../unsaved_changes_prompt.test.tsx | 109 ++++++++++++++++++ .../unsaved_changes_prompt.tsx | 41 +++++++ 3 files changed, 158 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/index.ts new file mode 100644 index 0000000000000..487c1350cbc08 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { UnsavedChangesPrompt } from './unsaved_changes_prompt'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.test.tsx new file mode 100644 index 0000000000000..dad6ea32b81aa --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.test.tsx @@ -0,0 +1,109 @@ +/* + * 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 from 'react'; + +jest.mock('react-router-dom', () => ({ + Prompt: () => null, +})); +import { Prompt } from 'react-router-dom'; + +import { shallow, mount, ReactWrapper } from 'enzyme'; + +import { UnsavedChangesPrompt } from './unsaved_changes_prompt'; + +describe('UnsavedChangesPrompt', () => { + let addEventListenerSpy: jest.SpyInstance; + let removeEventListenerSpy: jest.SpyInstance; + + beforeAll(() => { + addEventListenerSpy = jest.spyOn(window, 'addEventListener').mockImplementation(() => true); + removeEventListenerSpy = jest + .spyOn(window, 'removeEventListener') + .mockImplementation(() => true); + }); + + afterAll(() => { + addEventListenerSpy.mockRestore(); + removeEventListenerSpy.mockRestore(); + }); + + it('renders a React Router Prompt, which will show users a confirmation message when navigating within the SPA if hasUnsavedChanges is true', () => { + const wrapper = shallow(); + const prompt = wrapper.find(Prompt); + expect(prompt.exists()).toBe(true); + expect(prompt.prop('when')).toBe(true); + expect(prompt.prop('message')).toBe( + 'Your changes have not been saved. Are you sure you want to leave?' + ); + }); + + it('the message text of the prompt can be customized', () => { + const wrapper = shallow( + + ); + expect(wrapper.find(Prompt).prop('message')).toBe('Some custom text'); + }); + + describe('external navigation', () => { + let wrapper: ReactWrapper; + const getAddBeforeUnloadEventCalls = () => + addEventListenerSpy.mock.calls.filter((call) => call[0] === 'beforeunload'); + const getRemoveBeforeUnloadEventCalls = () => + removeEventListenerSpy.mock.calls.filter((call) => call[0] === 'beforeunload'); + const getLastRegisteredBeforeUnloadEventHandler = () => { + const calls = getAddBeforeUnloadEventCalls(); + return calls[calls.length - 1][1]; + }; + + beforeAll(() => { + wrapper = mount(); + }); + + it('sets up a handler for the beforeunload event', () => { + const calls = getAddBeforeUnloadEventCalls(); + expect(calls.length).toBe(1); + }); + + it('that handler will show users a confirmation message when navigating outside the SPA if hasUnsavedChanges is true', () => { + const handler = getLastRegisteredBeforeUnloadEventHandler(); + const event = { returnValue: null, preventDefault: jest.fn() }; + + handler(event); + expect(event.returnValue).toEqual(''); + expect(event.preventDefault).toHaveBeenCalled(); + }); + + it('will not register a new handler if there is a re-render and hasUnsavedChanges is still true', () => { + wrapper.setProps({ hasUnsavedChanges: true, messageText: 'custom message text' }); + const calls = getAddBeforeUnloadEventCalls(); + expect(calls.length).toBe(1); + }); + + it('when the hasUnsavedChanges prop changes to false, it will deregister the old handler and create a new one, which will not show users a confirmation', () => { + const initialHandler = getLastRegisteredBeforeUnloadEventHandler(); + + wrapper.setProps({ hasUnsavedChanges: false }); + + // The old handler is unregistered + const unregisterCalls = getRemoveBeforeUnloadEventCalls(); + expect(unregisterCalls.length).toBe(1); + expect(unregisterCalls[0][1]).toBe(initialHandler); + + // The new handler is registered + const calls = getAddBeforeUnloadEventCalls(); + expect(calls.length).toBe(2); + const newHandler = getLastRegisteredBeforeUnloadEventHandler(); + + // The new handler does not show a confirmation message + const event = { returnValue: null, preventDefault: jest.fn() }; + newHandler(event); + expect(event.returnValue).toEqual(null); + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.tsx new file mode 100644 index 0000000000000..18cc4db5055d4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/unsaved_changes_prompt/unsaved_changes_prompt.tsx @@ -0,0 +1,41 @@ +/* + * 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, { useEffect } from 'react'; +import { Prompt } from 'react-router-dom'; + +import { i18n } from '@kbn/i18n'; + +const DEFAULT_MESSAGE_TEXT = i18n.translate('xpack.enterpriseSearch.shared.unsavedChangesMessage', { + defaultMessage: 'Your changes have not been saved. Are you sure you want to leave?', +}); +interface Props { + hasUnsavedChanges: boolean; + messageText?: string; +} + +export const UnsavedChangesPrompt: React.FC = ({ + hasUnsavedChanges, + messageText = DEFAULT_MESSAGE_TEXT, +}) => { + useEffect(() => { + const handler = (event: BeforeUnloadEvent) => { + if (hasUnsavedChanges) { + // These 2 lines of code are the recommendation from MDN for triggering a browser prompt for confirming + // whether or not a user wants to leave the current site. + event.preventDefault(); + event.returnValue = ''; + } + }; + // Adding this handler will prompt users if they are navigating to a new page, outside of the Kibana SPA + window.addEventListener('beforeunload', handler); + return () => window.removeEventListener('beforeunload', handler); + }, [hasUnsavedChanges]); + + // Adding this Prompt will prompt users if they are navigating to a new page, within the Kibana SPA + return ; +}; From accf6b8c2ed75bba5ea9743d0bafd7eada748bb9 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Tue, 23 Feb 2021 12:22:38 -0600 Subject: [PATCH 42/70] [ML] Fix Total feature importance section not expandable when set to False (#92450) --- .../expandable_section/expandable_section.tsx | 10 +++++++--- .../hooks/use_exploration_url_state.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx index 5ecc80b6ed255..135f47a959950 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx @@ -7,7 +7,7 @@ import './expandable_section.scss'; -import React, { FC, ReactNode, useCallback } from 'react'; +import React, { FC, ReactNode, useCallback, useMemo } from 'react'; import { EuiBadge, @@ -58,11 +58,15 @@ export const ExpandableSection: FC = ({ docsLink, urlStateKey, }) => { - const [pageUrlState, setPageUrlState] = useExplorationUrlState(); + const overrides = useMemo( + () => (isExpandedDefault !== undefined ? { [urlStateKey]: isExpandedDefault } : undefined), + [urlStateKey, isExpandedDefault] + ); + const [pageUrlState, setPageUrlState] = useExplorationUrlState(overrides); const isExpanded = isExpandedDefault !== undefined && - pageUrlState[urlStateKey] === getDefaultExplorationPageUrlState()[urlStateKey] + pageUrlState[urlStateKey] === getDefaultExplorationPageUrlState(overrides)[urlStateKey] ? isExpandedDefault : pageUrlState[urlStateKey]; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_exploration_url_state.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_exploration_url_state.ts index 1449215dc3457..3b8d7c4f051f0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_exploration_url_state.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_exploration_url_state.ts @@ -9,8 +9,11 @@ import { usePageUrlState } from '../../../../util/url_state'; import { ML_PAGES } from '../../../../../../common/constants/ml_url_generator'; import { SEARCH_QUERY_LANGUAGE } from '../../../../../../common/constants/search'; import { ExplorationPageUrlState } from '../../../../../../common/types/ml_url_generator'; +import { isPopulatedObject } from '../../../../../../common/util/object_utils'; -export function getDefaultExplorationPageUrlState(): ExplorationPageUrlState { +export function getDefaultExplorationPageUrlState( + overrides?: Partial +): ExplorationPageUrlState { return { queryText: '', queryLanguage: SEARCH_QUERY_LANGUAGE.KUERY, @@ -21,12 +24,13 @@ export function getDefaultExplorationPageUrlState(): ExplorationPageUrlState { feature_importance: true, results: true, splom: true, + ...(isPopulatedObject(overrides) ? overrides : {}), }; } -export function useExplorationUrlState() { +export function useExplorationUrlState(overrides?: Partial) { return usePageUrlState( ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION, - getDefaultExplorationPageUrlState() + getDefaultExplorationPageUrlState(overrides) ); } From 5ab2d2500657e2934bddb1cc9c8f9076832ffb29 Mon Sep 17 00:00:00 2001 From: Constance Date: Tue, 23 Feb 2021 10:26:27 -0800 Subject: [PATCH 43/70] [App Search] Standardize date/timestamps displayed in tables (#92287) * Add new reusable FormattedDateTime helper * Update Analytics RecentQueriesTable to use helper * Update CurationsTable to use new helper - requires adding a utility fn to convert the server-sent string to a Date-parseable string * Update EnginesTable to use FormattedDateTime - mostly to standardize/DRY out the FormattedDate display style - adds a new hasTime flag since the EnginesTable does not display time * [PR feedback] hasTime -> hideTime --- .../recent_queries_table.test.tsx | 6 ++-- .../analytics_tables/recent_queries_table.tsx | 15 ++++------ .../components/curations/utils.test.ts | 18 ++++++++++++ .../app_search/components/curations/utils.ts | 16 +++++++++++ .../curations/views/curations.test.tsx | 17 +++++++---- .../components/curations/views/curations.tsx | 3 ++ .../components/engines/engines_table.tsx | 8 ++---- .../utils/formatted_date_time/index.test.tsx | 28 +++++++++++++++++++ .../utils/formatted_date_time/index.tsx | 27 ++++++++++++++++++ 9 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.test.tsx index f90d86908d470..0670624492db5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.test.tsx @@ -48,9 +48,9 @@ describe('RecentQueriesTable', () => { expect(tableContent).toContain('""'); expect(tableContent).toContain('Time'); - expect(tableContent).toContain('1/3/1970'); - expect(tableContent).toContain('1/2/1970'); - expect(tableContent).toContain('1/1/1970'); + expect(tableContent).toContain('Jan 3, 1970'); + expect(tableContent).toContain('Jan 2, 1970'); + expect(tableContent).toContain('Jan 1, 1970'); expect(tableContent).toContain('Analytics tags'); expect(tableContent).toContain('tagA'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.tsx index 7724ac5c393ec..39b0e3e7165b1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_tables/recent_queries_table.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { EuiBasicTable, EuiBasicTableColumn, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedDate, FormattedTime } from '@kbn/i18n/react'; +import { FormattedDateTime } from '../../../../utils/formatted_date_time'; import { RecentQuery } from '../../types'; import { @@ -36,15 +36,10 @@ export const RecentQueriesTable: React.FC = ({ items }) => { name: i18n.translate('xpack.enterpriseSearch.appSearch.engine.analytics.table.timeColumn', { defaultMessage: 'Time', }), - render: (timestamp: RecentQuery['timestamp']) => { - const date = new Date(timestamp); - return ( - <> - - - ); - }, - width: '175px', + render: (timestamp: RecentQuery['timestamp']) => ( + + ), + width: '200px', }; const RESULTS_COLUMN = { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.test.ts new file mode 100644 index 0000000000000..435b76458db06 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.test.ts @@ -0,0 +1,18 @@ +/* + * 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 { convertToDate } from './utils'; + +describe('convertToDate', () => { + it('converts the English-only server timestamps to a parseable Date', () => { + const serverDateString = 'January 01, 1970 at 12:00PM'; + const date = convertToDate(serverDateString); + + expect(date).toBeInstanceOf(Date); + expect(date.getFullYear()).toEqual(1970); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.ts new file mode 100644 index 0000000000000..2ef73e1de4e91 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/utils.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ + +// The server API feels us an English datestring, but we want to convert +// it to an actual Date() instance so that we can localize date formats. +export const convertToDate = (serverDateString: string): Date => { + const readableDateString = serverDateString + .replace(' at ', ' ') + .replace('PM', ' PM') + .replace('AM', ' AM'); + return new Date(readableDateString); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx index c952de9b30a4d..fd5d5b7ea64a9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx @@ -5,12 +5,17 @@ * 2.0. */ -import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__'; +import { + mountWithIntl, + mockKibanaValues, + setMockActions, + setMockValues, +} from '../../../../__mocks__'; import '../../../__mocks__/engine_logic.mock'; import React from 'react'; -import { shallow, mount, ReactWrapper } from 'enzyme'; +import { shallow, ReactWrapper } from 'enzyme'; import { EuiBasicTable, EuiEmptyPrompt } from '@elastic/eui'; @@ -71,7 +76,7 @@ describe('Curations', () => { }); it('calls loadCurations on page load', () => { - mount(); + mountWithIntl(); expect(actions.loadCurations).toHaveBeenCalledTimes(1); }); @@ -95,7 +100,7 @@ describe('Curations', () => { let wrapper: ReactWrapper; beforeAll(() => { - wrapper = mount(); + wrapper = mountWithIntl(); }); it('renders queries and last updated columns', () => { @@ -106,8 +111,8 @@ describe('Curations', () => { expect(tableContent).toContain('mountains, valleys'); expect(tableContent).toContain('Last updated'); - expect(tableContent).toContain('January 1, 1970 at 12:00PM'); - expect(tableContent).toContain('January 2, 1970 at 12:00PM'); + expect(tableContent).toContain('Jan 1, 1970 12:00 PM'); + expect(tableContent).toContain('Jan 2, 1970 12:00 PM'); }); it('renders queries with curation links', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index d6aa3583e24a7..863920d209d5b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -25,11 +25,13 @@ import { KibanaLogic } from '../../../../shared/kibana'; import { Loading } from '../../../../shared/loading'; import { EuiButtonTo, EuiLinkTo } from '../../../../shared/react_router_helpers'; import { ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH } from '../../../routes'; +import { FormattedDateTime } from '../../../utils/formatted_date_time'; import { generateEnginePath } from '../../engine'; import { CURATIONS_OVERVIEW_TITLE, CREATE_NEW_CURATION_TITLE } from '../constants'; import { CurationsLogic } from '../curations_logic'; import { Curation } from '../types'; +import { convertToDate } from '../utils'; export const Curations: React.FC = () => { const { dataLoading, curations, meta } = useValues(CurationsLogic); @@ -101,6 +103,7 @@ export const CurationsTable: React.FC = () => { ), width: '30%', dataType: 'string', + render: (dateString: string) => , }, { width: '120px', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index d41c5c908c08f..85a9bb338b7f0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -11,7 +11,7 @@ import { useActions } from 'kea'; import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage, FormattedDate, FormattedNumber } from '@kbn/i18n/react'; +import { FormattedMessage, FormattedNumber } from '@kbn/i18n/react'; import { ENGINES_PAGE_SIZE } from '../../../../../common/constants'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; @@ -19,6 +19,7 @@ import { TelemetryLogic } from '../../../shared/telemetry'; import { UNIVERSAL_LANGUAGE } from '../../constants'; import { ENGINE_PATH } from '../../routes'; import { generateEncodedPath } from '../../utils/encode_path_params'; +import { FormattedDateTime } from '../../utils/formatted_date_time'; import { EngineDetails } from '../engine/types'; interface EnginesTablePagination { @@ -82,10 +83,7 @@ export const EnginesTable: React.FC = ({ } ), dataType: 'string', - render: (dateString: string) => ( - // e.g., Jan 1, 1970 - - ), + render: (dateString: string) => , }, { field: 'language', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.test.tsx new file mode 100644 index 0000000000000..5137a60ffe59d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.test.tsx @@ -0,0 +1,28 @@ +/* + * 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 { mountWithIntl } from '../../../__mocks__'; + +import React from 'react'; + +import { FormattedDateTime } from './'; + +describe('FormattedDateTime', () => { + it('renders a standard i18n-friendly combined date & time stamp', () => { + const date = new Date('1970-01-01T12:00:00'); + const wrapper = mountWithIntl(); + + expect(wrapper.text()).toEqual('Jan 1, 1970 12:00 PM'); + }); + + it('does not render time if hideTime is passed', () => { + const date = new Date('1970-01-01T12:00:00'); + const wrapper = mountWithIntl(); + + expect(wrapper.text()).toEqual('Jan 1, 1970'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.tsx new file mode 100644 index 0000000000000..87a377dbc21b6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/formatted_date_time/index.tsx @@ -0,0 +1,27 @@ +/* + * 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 from 'react'; + +import { FormattedDate, FormattedTime } from '@kbn/i18n/react'; + +interface Props { + date: Date; + hideTime?: boolean; +} + +export const FormattedDateTime: React.FC = ({ date, hideTime = false }) => ( + <> + + {!hideTime && ( + <> + {' '} + + + )} + +); From 50b23e217661ac2d06bd60eaba09584024d27adc Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Tue, 23 Feb 2021 12:36:43 -0600 Subject: [PATCH 44/70] [ML] Fix geo_shape not aggregetable and misaligned examples list in other_content (#92198) --- x-pack/plugins/ml/common/constants/field_types.ts | 1 + .../components/field_type_icon/field_type_icon.tsx | 1 + .../index_based/components/expanded_row/expanded_row.tsx | 1 + .../components/expanded_row/geo_point_content.tsx | 7 ++++--- .../components/search_panel/field_type_filter.tsx | 1 + .../components/field_data_expanded_row/other_content.tsx | 7 ++++++- .../ml/public/application/util/field_types_utils.ts | 4 ++++ .../ml/server/models/data_visualizer/data_visualizer.ts | 4 +++- 8 files changed, 21 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/ml/common/constants/field_types.ts b/x-pack/plugins/ml/common/constants/field_types.ts index 69f5ea9c4062e..04e1100e9f776 100644 --- a/x-pack/plugins/ml/common/constants/field_types.ts +++ b/x-pack/plugins/ml/common/constants/field_types.ts @@ -9,6 +9,7 @@ export const ML_JOB_FIELD_TYPES = { BOOLEAN: 'boolean', DATE: 'date', GEO_POINT: 'geo_point', + GEO_SHAPE: 'geo_shape', IP: 'ip', KEYWORD: 'keyword', NUMBER: 'number', diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.tsx b/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.tsx index 1ca8ef18f2ba5..79ab210ce1dfe 100644 --- a/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.tsx +++ b/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.tsx @@ -52,6 +52,7 @@ export const FieldTypeIcon: FC = ({ color = 'euiColorVis7'; break; case ML_JOB_FIELD_TYPES.GEO_POINT: + case ML_JOB_FIELD_TYPES.GEO_SHAPE: iconType = 'tokenGeo'; color = 'euiColorVis8'; break; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/expanded_row.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/expanded_row.tsx index 8a0656abe95cc..bd8eb09128d1d 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/expanded_row.tsx @@ -52,6 +52,7 @@ export const IndexBasedDataVisualizerExpandedRow = ({ return ; case ML_JOB_FIELD_TYPES.GEO_POINT: + case ML_JOB_FIELD_TYPES.GEO_SHAPE: return ( - + diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/field_type_filter.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/field_type_filter.tsx index 2f30dd156d018..7bc7260acf544 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/field_type_filter.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/field_type_filter.tsx @@ -17,6 +17,7 @@ export const ML_JOB_FIELD_TYPES_OPTIONS = { [ML_JOB_FIELD_TYPES.BOOLEAN]: { name: 'Boolean', icon: 'tokenBoolean' }, [ML_JOB_FIELD_TYPES.DATE]: { name: 'Date', icon: 'tokenDate' }, [ML_JOB_FIELD_TYPES.GEO_POINT]: { name: 'Geo point', icon: 'tokenGeo' }, + [ML_JOB_FIELD_TYPES.GEO_SHAPE]: { name: 'Geo shape', icon: 'tokenGeo' }, [ML_JOB_FIELD_TYPES.IP]: { name: 'IP address', icon: 'tokenIP' }, [ML_JOB_FIELD_TYPES.KEYWORD]: { name: 'Keyword', icon: 'tokenKeyword' }, [ML_JOB_FIELD_TYPES.NUMBER]: { name: 'Number', icon: 'tokenNumber' }, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/other_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/other_content.tsx index e2bb42d27d200..0734048e2bc2a 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/other_content.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/other_content.tsx @@ -6,6 +6,7 @@ */ import React, { FC } from 'react'; +import { EuiFlexItem } from '@elastic/eui'; import type { FieldDataRowProps } from '../../types/field_data_row'; import { ExamplesList } from '../../../index_based/components/field_data_row/examples_list'; import { DocumentStatsTable } from './document_stats'; @@ -17,7 +18,11 @@ export const OtherContent: FC = ({ config }) => { return ( - {Array.isArray(stats.examples) && } + {Array.isArray(stats.examples) && ( + + + + )} ); }; diff --git a/x-pack/plugins/ml/public/application/util/field_types_utils.ts b/x-pack/plugins/ml/public/application/util/field_types_utils.ts index a981432f116eb..0cb21fec1862f 100644 --- a/x-pack/plugins/ml/public/application/util/field_types_utils.ts +++ b/x-pack/plugins/ml/public/application/util/field_types_utils.ts @@ -35,6 +35,10 @@ export function kbnTypeToMLJobType(field: IFieldType) { case KBN_FIELD_TYPES.GEO_POINT: type = ML_JOB_FIELD_TYPES.GEO_POINT; break; + case KBN_FIELD_TYPES.GEO_SHAPE: + type = ML_JOB_FIELD_TYPES.GEO_SHAPE; + break; + default: break; } diff --git a/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts b/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts index 34af9117762a7..69ebfe5f0bc11 100644 --- a/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts +++ b/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts @@ -19,6 +19,7 @@ import { import { AggCardinality } from '../../../common/types/fields'; import { getDatafeedAggregations } from '../../../common/util/datafeed_utils'; import { Datafeed } from '../../../common/types/anomaly_detection_jobs'; +import { isPopulatedObject } from '../../../common/util/object_utils'; const SAMPLER_TOP_TERMS_THRESHOLD = 100000; const SAMPLER_TOP_TERMS_SHARD_SIZE = 5000; @@ -638,7 +639,7 @@ export class DataVisualizer { filter: filterCriteria, }, }, - aggs: buildSamplerAggregation(aggs, samplerShardSize), + ...(isPopulatedObject(aggs) ? { aggs: buildSamplerAggregation(aggs, samplerShardSize) } : {}), ...runtimeMappings, }; @@ -648,6 +649,7 @@ export class DataVisualizer { size, body: searchBody, }); + const aggregations = body.aggregations; const totalCount = body.hits.total.value; const stats = { From 301df28a66438e70dc4578f3b600e8ef7e2c9ef0 Mon Sep 17 00:00:00 2001 From: Constance Date: Tue, 23 Feb 2021 10:41:35 -0800 Subject: [PATCH 45/70] [App Search] Standardize table pagination logic/UX (#92293) * Create table pagination helpers * Update Curations table to use new pagination helpers * Update Credentials table to use new helpers + update to use DEFAULT_META and pass page[size] customization + update useEffect to manually call fetchCredentials on current page change + update table to pass empty prompt within table (rather than instead of), and add a loading indicator - update deleteApiKey to re-fetch credentials from API rather than modify the in-memory array (which more correctly handles pagination) * Update Engines table to use new helpers Server: + update server route to take page[current] (consistent w/ other routes) and page[size] - remove custom param translation (passing as-is) - remove ENGINES_PAGE_SIZE constant, basically stored within DEFAULT_META now and page size is passed from client-side - remove misc header cruft from engines table EnginesLogic: - Update http calls to send page[current] & page[size] (same as other files) - Change individual *Total/*Page vars to store the meta obj coming back from the API (same as other logic files) - Change on*Load actions to simply pass the API response (update typing to clarify) - Add new individual enginesLoading/metaEnginesLoading vars so that tables can show individual loading indicators between pagination - Change dataLoading to a selector (should only show on initial page load, and should use table loading indicators after) EnginesTable: - Update props to more closely match EuiBasicTable (items, onChange, add loading) so users can do a simple pass-through - Reorganize tests so that language block doesn't need its own custom wrapper vars - Remove empty data test (no longer really testing anything meaningful) and add loading test EnginesOverview: - Update to use new helpers, values, props, etc. - Add new test for onPaginate handlers --- .../enterprise_search/common/constants.ts | 2 - .../credentials/credentials.test.tsx | 16 +- .../components/credentials/credentials.tsx | 10 +- .../credentials_list.test.tsx | 43 ++--- .../credentials_list/credentials_list.tsx | 57 +++--- .../credentials/credentials_logic.test.ts | 78 +++----- .../credentials/credentials_logic.ts | 37 ++-- .../components/curations/curations_logic.ts | 8 +- .../components/curations/views/curations.tsx | 11 +- .../components/engines/engines_logic.test.ts | 178 +++++++++++------- .../components/engines/engines_logic.ts | 95 +++++----- .../engines/engines_overview.test.tsx | 57 +++++- .../components/engines/engines_overview.tsx | 31 +-- .../components/engines/engines_table.test.tsx | 104 +++++----- .../components/engines/engines_table.tsx | 43 ++--- .../shared/table_pagination/index.test.ts | 53 ++++++ .../shared/table_pagination/index.ts | 39 ++++ .../routes/app_search/credentials.test.ts | 9 +- .../server/routes/app_search/credentials.ts | 1 + .../server/routes/app_search/engines.test.ts | 48 ++--- .../server/routes/app_search/engines.ts | 11 +- 21 files changed, 528 insertions(+), 403 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.ts diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index cb9746ec9b92a..857f6037c53c8 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -81,5 +81,3 @@ export const JSON_HEADER = { }; export const READ_ONLY_MODE_HEADER = 'x-ent-search-read-only-mode'; - -export const ENGINES_PAGE_SIZE = 10; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.test.tsx index 48fcf4b8c5b66..3785873461f16 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.test.tsx @@ -14,6 +14,7 @@ import { shallow } from 'enzyme'; import { EuiCopy, EuiLoadingContent, EuiPageContentBody } from '@elastic/eui'; +import { DEFAULT_META } from '../../../shared/constants'; import { externalUrl } from '../../../shared/enterprise_search_url'; import { Credentials } from './credentials'; @@ -23,10 +24,12 @@ import { CredentialsFlyout } from './credentials_flyout'; describe('Credentials', () => { // Kea mocks const values = { + meta: DEFAULT_META, dataLoading: false, }; const actions = { - initializeCredentialsData: jest.fn(), + fetchCredentials: jest.fn(), + fetchDetails: jest.fn(), resetCredentials: jest.fn(), showCredentialsForm: jest.fn(), }; @@ -42,9 +45,10 @@ describe('Credentials', () => { expect(wrapper.find(EuiPageContentBody)).toHaveLength(1); }); - it('initializes data on mount', () => { + it('fetches data on mount', () => { shallow(); - expect(actions.initializeCredentialsData).toHaveBeenCalledTimes(1); + expect(actions.fetchCredentials).toHaveBeenCalledTimes(1); + expect(actions.fetchDetails).toHaveBeenCalledTimes(1); }); it('calls resetCredentials on unmount', () => { @@ -54,7 +58,7 @@ describe('Credentials', () => { }); it('renders a limited UI if data is still loading', () => { - setMockValues({ dataLoading: true }); + setMockValues({ ...values, dataLoading: true }); const wrapper = shallow(); expect(wrapper.find('[data-test-subj="CreateAPIKeyButton"]')).toHaveLength(0); expect(wrapper.find(EuiLoadingContent)).toHaveLength(1); @@ -78,13 +82,13 @@ describe('Credentials', () => { }); it('will render CredentialsFlyout if shouldShowCredentialsForm is true', () => { - setMockValues({ shouldShowCredentialsForm: true }); + setMockValues({ ...values, shouldShowCredentialsForm: true }); const wrapper = shallow(); expect(wrapper.find(CredentialsFlyout)).toHaveLength(1); }); it('will NOT render CredentialsFlyout if shouldShowCredentialsForm is false', () => { - setMockValues({ shouldShowCredentialsForm: false }); + setMockValues({ ...values, shouldShowCredentialsForm: false }); const wrapper = shallow(); expect(wrapper.find(CredentialsFlyout)).toHaveLength(0); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx index 266e9467c300d..fc411c3dff866 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx @@ -35,14 +35,18 @@ import { CredentialsList } from './credentials_list'; import { CredentialsLogic } from './credentials_logic'; export const Credentials: React.FC = () => { - const { initializeCredentialsData, resetCredentials, showCredentialsForm } = useActions( + const { fetchCredentials, fetchDetails, resetCredentials, showCredentialsForm } = useActions( CredentialsLogic ); - const { dataLoading, shouldShowCredentialsForm } = useValues(CredentialsLogic); + const { meta, dataLoading, shouldShowCredentialsForm } = useValues(CredentialsLogic); useEffect(() => { - initializeCredentialsData(); + fetchCredentials(); + }, [meta.page.current]); + + useEffect(() => { + fetchDetails(); return () => { resetCredentials(); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx index 8c52df30bfc67..09340d37fcf7b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx @@ -21,7 +21,7 @@ import { Key } from './key'; import { CredentialsList } from './'; -describe('Credentials', () => { +describe('CredentialsList', () => { const apiToken: ApiToken = { name: '', type: ApiTokenTypes.Private, @@ -42,10 +42,11 @@ describe('Credentials', () => { total_results: 1, }, }, + isCredentialsDataComplete: true, }; const actions = { deleteApiKey: jest.fn(), - fetchCredentials: jest.fn(), + onPaginate: jest.fn(), showCredentialsForm: jest.fn(), }; @@ -92,9 +93,22 @@ describe('Credentials', () => { apiTokens: [], }); + const wrapper = shallow() + .find(EuiBasicTable) + .dive(); + expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); + }); + }); + + describe('loading state', () => { + it('renders as loading when isCredentialsDataComplete is false', () => { + setMockValues({ + ...values, + isCredentialsDataComplete: false, + }); + const wrapper = shallow(); - expect(wrapper.exists(EuiEmptyPrompt)).toBe(true); - expect(wrapper.exists(EuiBasicTable)).toBe(false); + expect(wrapper.find(EuiBasicTable).prop('loading')).toBe(true); }); }); @@ -120,24 +134,13 @@ describe('Credentials', () => { hidePerPageOptions: true, }); }); - - it('will default pagination values if `page` is not available', () => { - setMockValues({ ...values, meta: {} }); - const wrapper = shallow(); - const { pagination } = wrapper.find(EuiBasicTable).props(); - expect(pagination).toEqual({ - pageIndex: 0, - pageSize: 0, - totalItemCount: 0, - hidePerPageOptions: true, - }); - }); }); describe('columns', () => { let columns: any[]; beforeAll(() => { + setMockValues(values); const wrapper = shallow(); columns = wrapper.find(EuiBasicTable).props().columns; }); @@ -269,18 +272,16 @@ describe('Credentials', () => { }); describe('onChange', () => { - it('will handle pagination by calling `fetchCredentials`', () => { + it('will handle pagination by calling `onPaginate`', () => { const wrapper = shallow(); - const { onChange } = wrapper.find(EuiBasicTable).props(); - - onChange({ + wrapper.find(EuiBasicTable).simulate('change', { page: { size: 10, index: 2, }, }); - expect(actions.fetchCredentials).toHaveBeenCalledWith(3); + expect(actions.onPaginate).toHaveBeenCalledWith(3); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx index f23479017a680..21a428bfdf836 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx @@ -10,10 +10,10 @@ import React, { useMemo } from 'react'; import { useActions, useValues } from 'kea'; import { EuiBasicTable, EuiBasicTableColumn, EuiCopy, EuiEmptyPrompt } from '@elastic/eui'; -import { CriteriaWithPagination } from '@elastic/eui/src/components/basic_table/basic_table'; import { i18n } from '@kbn/i18n'; import { HiddenText } from '../../../../shared/hidden_text'; +import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination'; import { TOKEN_TYPE_DISPLAY_NAMES } from '../constants'; import { CredentialsLogic } from '../credentials_logic'; import { ApiToken } from '../types'; @@ -23,9 +23,9 @@ import { apiTokenSort } from '../utils/api_token_sort'; import { Key } from './key'; export const CredentialsList: React.FC = () => { - const { deleteApiKey, fetchCredentials, showCredentialsForm } = useActions(CredentialsLogic); + const { deleteApiKey, onPaginate, showCredentialsForm } = useActions(CredentialsLogic); - const { apiTokens, meta } = useValues(CredentialsLogic); + const { apiTokens, meta, isCredentialsDataComplete } = useValues(CredentialsLogic); const items = useMemo(() => apiTokens.slice().sort(apiTokenSort), [apiTokens]); @@ -109,38 +109,31 @@ export const CredentialsList: React.FC = () => { }, ]; - const pagination = { - pageIndex: meta.page ? meta.page.current - 1 : 0, - pageSize: meta.page ? meta.page.size : 0, - totalItemCount: meta.page ? meta.page.total_results : 0, - hidePerPageOptions: true, - }; - - const onTableChange = ({ page }: CriteriaWithPagination) => { - const { index: current } = page; - fetchCredentials(current + 1); - }; - - return items.length < 1 ? ( - - {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.empty.title', { - defaultMessage: 'No API Keys have been created yet.', - })} - - } - body={i18n.translate('xpack.enterpriseSearch.appSearch.credentials.empty.body', { - defaultMessage: 'Click the "Create a key" button to make your first one.', - })} - /> - ) : ( + return ( + {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.empty.title', { + defaultMessage: 'No API Keys have been created yet.', + })} + + } + body={i18n.translate('xpack.enterpriseSearch.appSearch.credentials.empty.body', { + defaultMessage: 'Click the "Create a key" button to make your first one.', + })} + /> + } + loading={!isCredentialsDataComplete} + pagination={{ + ...convertMetaToPagination(meta), + hidePerPageOptions: true, + }} + onChange={handlePageChange(onPaginate)} /> ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts index c9d6a43ebbbae..9ff540de13fe1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts @@ -7,15 +7,15 @@ import { LogicMounter, mockFlashMessageHelpers, mockHttpValues } from '../../../__mocks__'; +import { nextTick } from '@kbn/test/jest'; + +import { DEFAULT_META } from '../../../shared/constants'; + jest.mock('../../app_logic', () => ({ AppLogic: { selectors: { myRole: jest.fn(() => ({})) }, - values: { myRole: jest.fn(() => ({})) }, }, })); - -import { nextTick } from '@kbn/test/jest'; - import { AppLogic } from '../../app_logic'; import { ApiTokenTypes } from './constants'; @@ -43,7 +43,7 @@ describe('CredentialsLogic', () => { formErrors: [], isCredentialsDataComplete: false, isCredentialsDetailsComplete: false, - meta: {}, + meta: DEFAULT_META, nameInputBlurred: false, shouldShowCredentialsForm: false, fullEngineAccessChecked: false, @@ -213,39 +213,6 @@ describe('CredentialsLogic', () => { }); }); - describe('onApiKeyDelete', () => { - const values = { - ...DEFAULT_VALUES, - apiTokens: expect.any(Array), - }; - - describe('apiTokens', () => { - it('should remove specified token from apiTokens if name matches', () => { - mount({ - apiTokens: [newToken], - }); - - CredentialsLogic.actions.onApiKeyDelete(newToken.name); - expect(CredentialsLogic.values).toEqual({ - ...values, - apiTokens: [], - }); - }); - - it('should not remove specified token from apiTokens if name does not match', () => { - mount({ - apiTokens: [newToken], - }); - - CredentialsLogic.actions.onApiKeyDelete('foo'); - expect(CredentialsLogic.values).toEqual({ - ...values, - apiTokens: [newToken], - }); - }); - }); - }); - describe('onApiTokenCreateSuccess', () => { const values = { ...DEFAULT_VALUES, @@ -467,6 +434,7 @@ describe('CredentialsLogic', () => { const values = { ...DEFAULT_VALUES, + dataLoading: false, apiTokens: expect.any(Array), meta: expect.any(Object), isCredentialsDataComplete: expect.any(Boolean), @@ -514,6 +482,7 @@ describe('CredentialsLogic', () => { describe('setCredentialsDetails', () => { const values = { ...DEFAULT_VALUES, + dataLoading: false, engines: expect.any(Array), isCredentialsDetailsComplete: expect.any(Boolean), }; @@ -1038,15 +1007,20 @@ describe('CredentialsLogic', () => { }); }); - describe('initializeCredentialsData', () => { - it('should call fetchCredentials and fetchDetails', () => { - mount(); - jest.spyOn(CredentialsLogic.actions, 'fetchCredentials').mockImplementationOnce(() => {}); - jest.spyOn(CredentialsLogic.actions, 'fetchDetails').mockImplementationOnce(() => {}); + describe('onPaginate', () => { + it('should set meta.page.current', () => { + mount({ meta: DEFAULT_META }); - CredentialsLogic.actions.initializeCredentialsData(); - expect(CredentialsLogic.actions.fetchCredentials).toHaveBeenCalled(); - expect(CredentialsLogic.actions.fetchDetails).toHaveBeenCalled(); + CredentialsLogic.actions.onPaginate(5); + expect(CredentialsLogic.values).toEqual({ + ...DEFAULT_VALUES, + meta: { + page: { + ...DEFAULT_META.page, + current: 5, + }, + }, + }); }); }); @@ -1066,10 +1040,11 @@ describe('CredentialsLogic', () => { jest.spyOn(CredentialsLogic.actions, 'setCredentialsData').mockImplementationOnce(() => {}); http.get.mockReturnValue(Promise.resolve({ meta, results })); - CredentialsLogic.actions.fetchCredentials(2); + CredentialsLogic.actions.fetchCredentials(); expect(http.get).toHaveBeenCalledWith('/api/app_search/credentials', { query: { - 'page[current]': 2, + 'page[current]': 1, + 'page[size]': 10, }, }); await nextTick(); @@ -1117,15 +1092,16 @@ describe('CredentialsLogic', () => { describe('deleteApiKey', () => { const tokenName = 'abc123'; - it('will call an API endpoint and set the results with the `onApiKeyDelete` action', async () => { + it('will call an API endpoint and re-fetch the credentials list', async () => { mount(); - jest.spyOn(CredentialsLogic.actions, 'onApiKeyDelete').mockImplementationOnce(() => {}); + jest.spyOn(CredentialsLogic.actions, 'fetchCredentials').mockImplementationOnce(() => {}); http.delete.mockReturnValue(Promise.resolve()); CredentialsLogic.actions.deleteApiKey(tokenName); expect(http.delete).toHaveBeenCalledWith(`/api/app_search/credentials/${tokenName}`); await nextTick(); - expect(CredentialsLogic.actions.onApiKeyDelete).toHaveBeenCalledWith(tokenName); + + expect(CredentialsLogic.actions.fetchCredentials).toHaveBeenCalled(); expect(setSuccessMessage).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts index ff4600872c589..2841282704189 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts @@ -8,12 +8,15 @@ import { kea, MakeLogicType } from 'kea'; import { Meta } from '../../../../../common/types'; +import { DEFAULT_META } from '../../../shared/constants'; import { clearFlashMessages, setSuccessMessage, flashAPIErrors, } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; +import { updateMetaPageIndex } from '../../../shared/table_pagination'; + import { AppLogic } from '../../app_logic'; import { Engine } from '../../types'; import { formatApiName } from '../../utils/format_api_name'; @@ -32,7 +35,6 @@ export const defaultApiToken: ApiToken = { interface CredentialsLogicActions { addEngineName(engineName: string): string; - onApiKeyDelete(tokenName: string): string; onApiTokenCreateSuccess(apiToken: ApiToken): ApiToken; onApiTokenError(formErrors: string[]): string[]; onApiTokenUpdateSuccess(apiToken: ApiToken): ApiToken; @@ -47,8 +49,8 @@ interface CredentialsLogicActions { showCredentialsForm(apiToken?: ApiToken): ApiToken; hideCredentialsForm(): { value: boolean }; resetCredentials(): { value: boolean }; - initializeCredentialsData(): { value: boolean }; - fetchCredentials(page?: number): number; + onPaginate(newPageIndex: number): { newPageIndex: number }; + fetchCredentials(): void; fetchDetails(): { value: boolean }; deleteApiKey(tokenName: string): string; onApiTokenChange(): void; @@ -66,7 +68,7 @@ interface CredentialsLogicValues { isCredentialsDataComplete: boolean; isCredentialsDetailsComplete: boolean; fullEngineAccessChecked: boolean; - meta: Partial; + meta: Meta; nameInputBlurred: boolean; shouldShowCredentialsForm: boolean; } @@ -77,7 +79,6 @@ export const CredentialsLogic = kea({ path: ['enterprise_search', 'app_search', 'credentials_logic'], actions: () => ({ addEngineName: (engineName) => engineName, - onApiKeyDelete: (tokenName) => tokenName, onApiTokenCreateSuccess: (apiToken) => apiToken, onApiTokenError: (formErrors) => formErrors, onApiTokenUpdateSuccess: (apiToken) => apiToken, @@ -92,8 +93,8 @@ export const CredentialsLogic = kea({ showCredentialsForm: (apiToken = { ...defaultApiToken }) => apiToken, hideCredentialsForm: false, resetCredentials: false, - initializeCredentialsData: true, - fetchCredentials: (page) => page, + onPaginate: (newPageIndex) => ({ newPageIndex }), + fetchCredentials: true, fetchDetails: true, deleteApiKey: (tokenName) => tokenName, onApiTokenChange: () => null, @@ -109,14 +110,13 @@ export const CredentialsLogic = kea({ ...apiTokens.filter((token) => token.name !== apiToken.name), apiToken, ], - onApiKeyDelete: (apiTokens, tokenName) => - apiTokens.filter((token) => token.name !== tokenName), }, ], meta: [ - {}, + DEFAULT_META, { setCredentialsData: (_, { meta }) => meta, + onPaginate: (state, { newPageIndex }) => updateMetaPageIndex(state, newPageIndex), }, ], isCredentialsDetailsComplete: [ @@ -130,6 +130,7 @@ export const CredentialsLogic = kea({ false, { setCredentialsData: () => true, + fetchCredentials: () => false, resetCredentials: () => false, }, ], @@ -218,7 +219,7 @@ export const CredentialsLogic = kea({ dataLoading: [ () => [selectors.isCredentialsDetailsComplete, selectors.isCredentialsDataComplete], (isCredentialsDetailsComplete, isCredentialsDataComplete) => { - return isCredentialsDetailsComplete === false || isCredentialsDataComplete === false; + return isCredentialsDetailsComplete === false && isCredentialsDataComplete === false; }, ], activeApiTokenExists: [ @@ -230,14 +231,14 @@ export const CredentialsLogic = kea({ showCredentialsForm: () => { clearFlashMessages(); }, - initializeCredentialsData: () => { - actions.fetchCredentials(); - actions.fetchDetails(); - }, - fetchCredentials: async (page = 1) => { + fetchCredentials: async () => { try { const { http } = HttpLogic.values; - const query = { 'page[current]': page }; + const { meta } = values; + const query = { + 'page[current]': meta.page.current, + 'page[size]': meta.page.size, + }; const response = await http.get('/api/app_search/credentials', { query }); actions.setCredentialsData(response.meta, response.results); } catch (e) { @@ -259,7 +260,7 @@ export const CredentialsLogic = kea({ const { http } = HttpLogic.values; await http.delete(`/api/app_search/credentials/${tokenName}`); - actions.onApiKeyDelete(tokenName); + actions.fetchCredentials(); setSuccessMessage(DELETE_MESSAGE); } catch (e) { flashAPIErrors(e); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts index 1b2d3b8cdb953..434aff9c3cc4b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts @@ -9,13 +9,13 @@ import { kea, MakeLogicType } from 'kea'; import { Meta } from '../../../../../common/types'; import { DEFAULT_META } from '../../../shared/constants'; - import { clearFlashMessages, setSuccessMessage, flashAPIErrors, } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; +import { updateMetaPageIndex } from '../../../shared/table_pagination'; import { EngineLogic } from '../engine'; import { DELETE_MESSAGE, SUCCESS_MESSAGE } from './constants'; @@ -60,11 +60,7 @@ export const CurationsLogic = kea meta, - onPaginate: (state, { newPageIndex }) => { - const newState = { page: { ...state.page } }; - newState.page.current = newPageIndex; - return newState; - }, + onPaginate: (state, { newPageIndex }) => updateMetaPageIndex(state, newPageIndex), }, ], }), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 863920d209d5b..6affef53d71ee 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -24,6 +24,8 @@ import { FlashMessages } from '../../../../shared/flash_messages'; import { KibanaLogic } from '../../../../shared/kibana'; import { Loading } from '../../../../shared/loading'; import { EuiButtonTo, EuiLinkTo } from '../../../../shared/react_router_helpers'; +import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination'; + import { ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH } from '../../../routes'; import { FormattedDateTime } from '../../../utils/formatted_date_time'; import { generateEnginePath } from '../../engine'; @@ -167,15 +169,10 @@ export const CurationsTable: React.FC = () => { /> } pagination={{ - pageIndex: meta.page.current - 1, - pageSize: meta.page.size, - totalItemCount: meta.page.total_results, + ...convertMetaToPagination(meta), hidePerPageOptions: true, }} - onChange={({ page }: { page: { index: number } }) => { - const { index } = page; - onPaginate(index + 1); // Note on paging - App Search's API pages start at 1, EuiBasicTables' pages start at 0 - }} + onChange={handlePageChange(onPaginate)} /> ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts index 9e9bfc4973124..8be4f471b79a6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts @@ -9,6 +9,8 @@ import { LogicMounter, mockHttpValues } from '../../../__mocks__'; import { nextTick } from '@kbn/test/jest'; +import { DEFAULT_META } from '../../../shared/constants'; + import { EngineDetails } from '../engine/types'; import { EnginesLogic } from './'; @@ -20,11 +22,11 @@ describe('EnginesLogic', () => { const DEFAULT_VALUES = { dataLoading: true, engines: [], - enginesTotal: 0, - enginesPage: 1, + enginesMeta: DEFAULT_META, + enginesLoading: true, metaEngines: [], - metaEnginesTotal: 0, - metaEnginesPage: 1, + metaEnginesMeta: DEFAULT_META, + metaEnginesLoading: true, }; const MOCK_ENGINE = { @@ -33,16 +35,17 @@ describe('EnginesLogic', () => { document_count: 50, field_count: 10, } as EngineDetails; + const MOCK_META = { + page: { + current: 1, + size: 10, + total_results: 100, + total_pages: 10, + }, + }; const MOCK_ENGINES_API_RESPONSE = { results: [MOCK_ENGINE], - meta: { - page: { - current: 1, - total_pages: 10, - total_results: 100, - size: 10, - }, - }, + meta: MOCK_META, }; beforeEach(() => { @@ -56,112 +59,143 @@ describe('EnginesLogic', () => { describe('actions', () => { describe('onEnginesLoad', () => { - describe('dataLoading', () => { - it('should be set to false', () => { - mount(); - EnginesLogic.actions.onEnginesLoad({ engines: [], total: 0 }); - - expect(EnginesLogic.values).toEqual({ - ...DEFAULT_VALUES, - dataLoading: false, - }); - }); - }); + it('should set engines & enginesMeta and set enginesLoading to false', () => { + mount(); + EnginesLogic.actions.onEnginesLoad(MOCK_ENGINES_API_RESPONSE); - describe('engines & enginesTotal', () => { - it('should be set to the provided value', () => { - mount(); - EnginesLogic.actions.onEnginesLoad({ engines: [MOCK_ENGINE], total: 100 }); - - expect(EnginesLogic.values).toEqual({ - ...DEFAULT_VALUES, - dataLoading: false, - engines: [MOCK_ENGINE], - enginesTotal: 100, - }); + expect(EnginesLogic.values).toEqual({ + ...DEFAULT_VALUES, + engines: [MOCK_ENGINE], + enginesMeta: MOCK_META, + enginesLoading: false, + dataLoading: false, }); }); }); describe('onMetaEnginesLoad', () => { - describe('engines & enginesTotal', () => { - it('should be set to the provided value', () => { - mount(); - EnginesLogic.actions.onMetaEnginesLoad({ engines: [MOCK_ENGINE], total: 1 }); - - expect(EnginesLogic.values).toEqual({ - ...DEFAULT_VALUES, - metaEngines: [MOCK_ENGINE], - metaEnginesTotal: 1, - }); + it('should set engines & enginesMeta and set enginesLoading to false', () => { + mount(); + EnginesLogic.actions.onMetaEnginesLoad(MOCK_ENGINES_API_RESPONSE); + + expect(EnginesLogic.values).toEqual({ + ...DEFAULT_VALUES, + metaEngines: [MOCK_ENGINE], + metaEnginesMeta: MOCK_META, + metaEnginesLoading: false, }); }); }); describe('onEnginesPagination', () => { - describe('enginesPage', () => { - it('should be set to the provided value', () => { - mount(); - EnginesLogic.actions.onEnginesPagination(2); - - expect(EnginesLogic.values).toEqual({ - ...DEFAULT_VALUES, - enginesPage: 2, - }); + it('should set enginesMeta.page.current', () => { + mount(); + EnginesLogic.actions.onEnginesPagination(2); + + expect(EnginesLogic.values).toEqual({ + ...DEFAULT_VALUES, + enginesMeta: { + page: { + ...DEFAULT_VALUES.enginesMeta.page, + current: 2, + }, + }, }); }); }); describe('onMetaEnginesPagination', () => { - describe('metaEnginesPage', () => { - it('should be set to the provided value', () => { - mount(); - EnginesLogic.actions.onMetaEnginesPagination(99); - - expect(EnginesLogic.values).toEqual({ - ...DEFAULT_VALUES, - metaEnginesPage: 99, - }); + it('should set metaEnginesMeta.page.current', () => { + mount(); + EnginesLogic.actions.onMetaEnginesPagination(99); + + expect(EnginesLogic.values).toEqual({ + ...DEFAULT_VALUES, + metaEnginesMeta: { + page: { + ...DEFAULT_VALUES.metaEnginesMeta.page, + current: 99, + }, + }, }); }); }); + }); + describe('listeners', () => { describe('loadEngines', () => { it('should call the engines API endpoint and set state based on the results', async () => { http.get.mockReturnValueOnce(Promise.resolve(MOCK_ENGINES_API_RESPONSE)); - mount({ enginesPage: 10 }); + mount(); jest.spyOn(EnginesLogic.actions, 'onEnginesLoad'); EnginesLogic.actions.loadEngines(); await nextTick(); expect(http.get).toHaveBeenCalledWith('/api/app_search/engines', { - query: { type: 'indexed', pageIndex: 10 }, - }); - expect(EnginesLogic.actions.onEnginesLoad).toHaveBeenCalledWith({ - engines: [MOCK_ENGINE], - total: 100, + query: { + type: 'indexed', + 'page[current]': 1, + 'page[size]': 10, + }, }); + expect(EnginesLogic.actions.onEnginesLoad).toHaveBeenCalledWith(MOCK_ENGINES_API_RESPONSE); }); }); describe('loadMetaEngines', () => { it('should call the engines API endpoint and set state based on the results', async () => { http.get.mockReturnValueOnce(Promise.resolve(MOCK_ENGINES_API_RESPONSE)); - mount({ metaEnginesPage: 99 }); + mount(); jest.spyOn(EnginesLogic.actions, 'onMetaEnginesLoad'); EnginesLogic.actions.loadMetaEngines(); await nextTick(); expect(http.get).toHaveBeenCalledWith('/api/app_search/engines', { - query: { type: 'meta', pageIndex: 99 }, + query: { + type: 'meta', + 'page[current]': 1, + 'page[size]': 10, + }, }); - expect(EnginesLogic.actions.onMetaEnginesLoad).toHaveBeenCalledWith({ + expect(EnginesLogic.actions.onMetaEnginesLoad).toHaveBeenCalledWith( + MOCK_ENGINES_API_RESPONSE + ); + }); + }); + }); + + describe('selectors', () => { + describe('dataLoading', () => { + it('returns true if enginesLoading is true and engines is empty', () => { + mount({ + enginesLoading: true, + engines: [], + }); + expect(EnginesLogic.values.dataLoading).toEqual(true); + }); + + it('returns false if enginesLoading is true but engines exist', () => { + // = the engines table is paginating, which has its own separate table loading indicator + mount({ + enginesLoading: true, engines: [MOCK_ENGINE], - total: 100, }); + expect(EnginesLogic.values.dataLoading).toEqual(false); }); + + it('returns false if engineLoading is false and engines is empty', () => { + // = empty prompt state + mount({ + enginesLoading: false, + engines: [], + }); + expect(EnginesLogic.values.dataLoading).toEqual(false); + }); + + // NOTE: dataLoading ignores metaEnginesLoading to prevent a race condition where + // meta engines finish fetching before engines and flash an empty prompt state. }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts index 4382722aeffd3..558bf666a51b1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts @@ -7,27 +7,30 @@ import { kea, MakeLogicType } from 'kea'; +import { Meta } from '../../../../../common/types'; +import { DEFAULT_META } from '../../../shared/constants'; import { HttpLogic } from '../../../shared/http'; +import { updateMetaPageIndex } from '../../../shared/table_pagination'; import { EngineDetails } from '../engine/types'; interface EnginesValues { dataLoading: boolean; engines: EngineDetails[]; - enginesTotal: number; - enginesPage: number; + enginesMeta: Meta; + enginesLoading: boolean; metaEngines: EngineDetails[]; - metaEnginesTotal: number; - metaEnginesPage: number; + metaEnginesMeta: Meta; // keanu_whoa.jpg + metaEnginesLoading: boolean; } -interface OnEnginesLoad { - engines: EngineDetails[]; - total: number; +interface EnginesAPIResponse { + results: EngineDetails[]; + meta: Meta; } interface EnginesActions { - onEnginesLoad({ engines, total }: OnEnginesLoad): OnEnginesLoad; - onMetaEnginesLoad({ engines, total }: OnEnginesLoad): OnEnginesLoad; + onEnginesLoad({ results, meta }: EnginesAPIResponse): EnginesAPIResponse; + onMetaEnginesLoad({ results, meta }: EnginesAPIResponse): EnginesAPIResponse; onEnginesPagination(page: number): { page: number }; onMetaEnginesPagination(page: number): { page: number }; loadEngines(): void; @@ -37,81 +40,87 @@ interface EnginesActions { export const EnginesLogic = kea>({ path: ['enterprise_search', 'app_search', 'engines_logic'], actions: { - onEnginesLoad: ({ engines, total }) => ({ engines, total }), - onMetaEnginesLoad: ({ engines, total }) => ({ engines, total }), + onEnginesLoad: ({ results, meta }) => ({ results, meta }), + onMetaEnginesLoad: ({ results, meta }) => ({ results, meta }), onEnginesPagination: (page) => ({ page }), onMetaEnginesPagination: (page) => ({ page }), loadEngines: true, loadMetaEngines: true, }, reducers: { - dataLoading: [ - true, - { - onEnginesLoad: () => false, - }, - ], engines: [ [], { - onEnginesLoad: (_, { engines }) => engines, + onEnginesLoad: (_, { results }) => results, }, ], - enginesTotal: [ - 0, + enginesMeta: [ + DEFAULT_META, { - onEnginesLoad: (_, { total }) => total, + onEnginesLoad: (_, { meta }) => meta, + onEnginesPagination: (state, { page }) => updateMetaPageIndex(state, page), }, ], - enginesPage: [ - 1, + enginesLoading: [ + true, { - onEnginesPagination: (_, { page }) => page, + loadEngines: () => true, + onEnginesLoad: () => false, }, ], metaEngines: [ [], { - onMetaEnginesLoad: (_, { engines }) => engines, + onMetaEnginesLoad: (_, { results }) => results, }, ], - metaEnginesTotal: [ - 0, + metaEnginesMeta: [ + DEFAULT_META, { - onMetaEnginesLoad: (_, { total }) => total, + onMetaEnginesLoad: (_, { meta }) => meta, + onMetaEnginesPagination: (state, { page }) => updateMetaPageIndex(state, page), }, ], - metaEnginesPage: [ - 1, + metaEnginesLoading: [ + true, { - onMetaEnginesPagination: (_, { page }) => page, + loadMetaEngines: () => true, + onMetaEnginesLoad: () => false, }, ], }, + selectors: { + dataLoading: [ + (selectors) => [selectors.enginesLoading, selectors.engines], + (enginesLoading, engines) => enginesLoading && !engines.length, + ], + }, listeners: ({ actions, values }) => ({ loadEngines: async () => { const { http } = HttpLogic.values; - const { enginesPage } = values; + const { enginesMeta } = values; const response = await http.get('/api/app_search/engines', { - query: { type: 'indexed', pageIndex: enginesPage }, - }); - actions.onEnginesLoad({ - engines: response.results, - total: response.meta.page.total_results, + query: { + type: 'indexed', + 'page[current]': enginesMeta.page.current, + 'page[size]': enginesMeta.page.size, + }, }); + actions.onEnginesLoad(response); }, loadMetaEngines: async () => { const { http } = HttpLogic.values; - const { metaEnginesPage } = values; + const { metaEnginesMeta } = values; const response = await http.get('/api/app_search/engines', { - query: { type: 'meta', pageIndex: metaEnginesPage }, - }); - actions.onMetaEnginesLoad({ - engines: response.results, - total: response.meta.page.total_results, + query: { + type: 'meta', + 'page[current]': metaEnginesMeta.page.current, + 'page[size]': metaEnginesMeta.page.size, + }, }); + actions.onMetaEnginesLoad(response); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx index 978538d26e5d6..c25f27c81ff48 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx @@ -22,11 +22,23 @@ describe('EnginesOverview', () => { hasPlatinumLicense: false, dataLoading: false, engines: [], - enginesTotal: 0, - enginesPage: 1, + enginesMeta: { + page: { + current: 1, + size: 10, + total_results: 0, + }, + }, + enginesLoading: false, metaEngines: [], - metaEnginesTotal: 0, - metaEnginesPage: 1, + metaEnginesMeta: { + page: { + current: 1, + size: 10, + total_results: 0, + }, + }, + metaEnginesLoading: false, }; const actions = { loadEngines: jest.fn(), @@ -62,8 +74,13 @@ describe('EnginesOverview', () => { ...values, dataLoading: false, engines: ['dummy-engine'], - enginesTotal: 100, - enginesPage: 1, + enginesMeta: { + page: { + current: 1, + size: 10, + total_results: 100, + }, + }, }; beforeEach(() => { @@ -107,19 +124,43 @@ describe('EnginesOverview', () => { const wrapper = shallow(); const pagination = getTablePagination(wrapper); - expect(pagination.totalEngines).toEqual(100); + expect(pagination.totalItemCount).toEqual(100); expect(pagination.pageIndex).toEqual(0); }); it('re-polls the API on page change', async () => { const wrapper = shallow(); - setMockValues({ ...valuesWithEngines, enginesPage: 51 }); + setMockValues({ + ...valuesWithEngines, + enginesMeta: { + page: { + ...valuesWithEngines.enginesMeta.page, + current: 51, + }, + }, + }); rerender(wrapper); expect(actions.loadEngines).toHaveBeenCalledTimes(2); expect(getTablePagination(wrapper).pageIndex).toEqual(50); }); + + it('calls onPagination handlers', async () => { + setMockValues({ + ...valuesWithEngines, + hasPlatinumLicense: true, + metaEngines: ['dummy-meta-engine'], + }); + const wrapper = shallow(); + const pageEvent = { page: { index: 0 } }; + + wrapper.find(EnginesTable).first().simulate('change', pageEvent); + expect(actions.onEnginesPagination).toHaveBeenCalledWith(1); + + wrapper.find(EnginesTable).last().simulate('change', pageEvent); + expect(actions.onMetaEnginesPagination).toHaveBeenCalledWith(1); + }); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx index 1a81c1918ad4d..a26fe87365536 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx @@ -22,6 +22,7 @@ import { FlashMessages } from '../../../shared/flash_messages'; import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { LicensingLogic } from '../../../shared/licensing'; import { EuiButtonTo } from '../../../shared/react_router_helpers'; +import { convertMetaToPagination, handlePageChange } from '../../../shared/table_pagination'; import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; import { ENGINE_CREATION_PATH } from '../../routes'; @@ -39,11 +40,11 @@ export const EnginesOverview: React.FC = () => { const { dataLoading, engines, - enginesTotal, - enginesPage, + enginesMeta, + enginesLoading, metaEngines, - metaEnginesTotal, - metaEnginesPage, + metaEnginesMeta, + metaEnginesLoading, } = useValues(EnginesLogic); const { loadEngines, loadMetaEngines, onEnginesPagination, onMetaEnginesPagination } = useActions( EnginesLogic @@ -51,11 +52,11 @@ export const EnginesOverview: React.FC = () => { useEffect(() => { loadEngines(); - }, [enginesPage]); + }, [enginesMeta.page.current]); useEffect(() => { if (hasPlatinumLicense) loadMetaEngines(); - }, [hasPlatinumLicense, metaEnginesPage]); + }, [hasPlatinumLicense, metaEnginesMeta.page.current]); if (dataLoading) return ; if (!engines.length) return ; @@ -89,12 +90,13 @@ export const EnginesOverview: React.FC = () => { @@ -110,12 +112,13 @@ export const EnginesOverview: React.FC = () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx index d6f0946164ea4..f8ad9bc6b2bc3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx @@ -19,8 +19,7 @@ import { EngineDetails } from '../engine/types'; import { EnginesTable } from './engines_table'; describe('EnginesTable', () => { - const onPaginate = jest.fn(); // onPaginate updates the engines API call upstream - + const onChange = jest.fn(); const data = [ { name: 'test-engine', @@ -32,113 +31,112 @@ describe('EnginesTable', () => { } as EngineDetails, ]; const pagination = { - totalEngines: 50, pageIndex: 0, - onPaginate, + pageSize: 10, + totalItemCount: 50, + hidePerPageOptions: true, }; const props = { - data, + items: data, + loading: false, pagination, + onChange, }; - const wrapper = mountWithIntl(); - const table = wrapper.find(EuiBasicTable); + describe('basic table', () => { + const wrapper = mountWithIntl(); + const table = wrapper.find(EuiBasicTable); - it('renders', () => { - expect(table).toHaveLength(1); - expect(table.prop('pagination').totalItemCount).toEqual(50); + it('renders', () => { + expect(table).toHaveLength(1); + expect(table.prop('pagination').totalItemCount).toEqual(50); - const tableContent = table.text(); - expect(tableContent).toContain('test-engine'); - expect(tableContent).toContain('Jan 1, 1970'); - expect(tableContent).toContain('English'); - expect(tableContent).toContain('99,999'); - expect(tableContent).toContain('10'); + const tableContent = table.text(); + expect(tableContent).toContain('test-engine'); + expect(tableContent).toContain('Jan 1, 1970'); + expect(tableContent).toContain('English'); + expect(tableContent).toContain('99,999'); + expect(tableContent).toContain('10'); - expect(table.find(EuiPagination).find(EuiButtonEmpty)).toHaveLength(5); // Should display 5 pages at 10 engines per page - }); + expect(table.find(EuiPagination).find(EuiButtonEmpty)).toHaveLength(5); // Should display 5 pages at 10 engines per page + }); - it('contains engine links which send telemetry', () => { - const engineLinks = wrapper.find(EuiLinkTo); + it('contains engine links which send telemetry', () => { + const engineLinks = wrapper.find(EuiLinkTo); - engineLinks.forEach((link) => { - expect(link.prop('to')).toEqual('/engines/test-engine'); - link.simulate('click'); + engineLinks.forEach((link) => { + expect(link.prop('to')).toEqual('/engines/test-engine'); + link.simulate('click'); - expect(mockTelemetryActions.sendAppSearchTelemetry).toHaveBeenCalledWith({ - action: 'clicked', - metric: 'engine_table_link', + expect(mockTelemetryActions.sendAppSearchTelemetry).toHaveBeenCalledWith({ + action: 'clicked', + metric: 'engine_table_link', + }); }); }); - }); - it('triggers onPaginate', () => { - table.prop('onChange')({ page: { index: 4 } }); - - expect(onPaginate).toHaveBeenCalledWith(5); + it('triggers onPaginate', () => { + table.prop('onChange')({ page: { index: 4 } }); + expect(onChange).toHaveBeenCalledWith({ page: { index: 4 } }); + }); }); - it('handles empty data', () => { - const emptyWrapper = mountWithIntl( - {} }} - /> - ); - const emptyTable = emptyWrapper.find(EuiBasicTable); - - expect(emptyTable.prop('pagination').pageIndex).toEqual(0); + describe('loading', () => { + it('passes the loading prop', () => { + const wrapper = mountWithIntl(); + expect(wrapper.find(EuiBasicTable).prop('loading')).toEqual(true); + }); }); describe('language field', () => { it('renders language when available', () => { - const wrapperWithLanguage = mountWithIntl( + const wrapper = mountWithIntl( ); - const tableContent = wrapperWithLanguage.find(EuiBasicTable).text(); + const tableContent = wrapper.find(EuiBasicTable).text(); expect(tableContent).toContain('German'); }); it('renders the language as Universal if no language is set', () => { - const wrapperWithLanguage = mountWithIntl( + const wrapper = mountWithIntl( ); - const tableContent = wrapperWithLanguage.find(EuiBasicTable).text(); + const tableContent = wrapper.find(EuiBasicTable).text(); expect(tableContent).toContain('Universal'); }); it('renders no language text if the engine is a Meta Engine', () => { - const wrapperWithLanguage = mountWithIntl( + const wrapper = mountWithIntl( ); - const tableContent = wrapperWithLanguage.find(EuiBasicTable).text(); + const tableContent = wrapper.find(EuiBasicTable).text(); expect(tableContent).not.toContain('Universal'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index 85a9bb338b7f0..fe61ba8cbcc43 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -9,11 +9,10 @@ import React from 'react'; import { useActions } from 'kea'; -import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; +import { EuiBasicTable, EuiBasicTableColumn, CriteriaWithPagination } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedNumber } from '@kbn/i18n/react'; -import { ENGINES_PAGE_SIZE } from '../../../../../common/constants'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { TelemetryLogic } from '../../../shared/telemetry'; import { UNIVERSAL_LANGUAGE } from '../../constants'; @@ -22,24 +21,23 @@ import { generateEncodedPath } from '../../utils/encode_path_params'; import { FormattedDateTime } from '../../utils/formatted_date_time'; import { EngineDetails } from '../engine/types'; -interface EnginesTablePagination { - totalEngines: number; - pageIndex: number; - onPaginate(pageIndex: number): void; -} interface EnginesTableProps { - data: EngineDetails[]; - pagination: EnginesTablePagination; -} -interface OnChange { - page: { - index: number; + items: EngineDetails[]; + loading: boolean; + pagination: { + pageIndex: number; + pageSize: number; + totalItemCount: number; + hidePerPageOptions: boolean; }; + onChange(criteria: CriteriaWithPagination): void; } export const EnginesTable: React.FC = ({ - data, - pagination: { totalEngines, pageIndex, onPaginate }, + items, + loading, + pagination, + onChange, }) => { const { sendAppSearchTelemetry } = useActions(TelemetryLogic); @@ -145,18 +143,11 @@ export const EnginesTable: React.FC = ({ return ( { - const { index } = page; - onPaginate(index + 1); // Note on paging - App Search's API pages start at 1, EuiBasicTables' pages start at 0 - }} + loading={loading} + pagination={pagination} + onChange={onChange} /> ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.test.ts new file mode 100644 index 0000000000000..32059f4dc7ae5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.test.ts @@ -0,0 +1,53 @@ +/* + * 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 { convertMetaToPagination, handlePageChange, updateMetaPageIndex } from './'; + +describe('convertMetaToPagination', () => { + expect( + convertMetaToPagination({ + page: { + current: 1, + size: 10, + total_results: 25, + total_pages: 3, // Not used, EuiBasicTable calculates pages on its own + }, + }) + ).toEqual({ + pageIndex: 0, + pageSize: 10, + totalItemCount: 25, + }); +}); + +describe('handlePageChange', () => { + it('creates an onChange handler that calls a passed callback with the new page index', () => { + const mockCallback = jest.fn(); + const handler = handlePageChange(mockCallback); + + handler({ page: { index: 0 } }); + expect(mockCallback).toHaveBeenCalledWith(1); + }); +}); + +describe('updateMetaPageIndex', () => { + it('updates meta.page.current without mutating meta.page', () => { + const oldMeta = { + page: { + current: 5, + size: 10, + total_results: 100, + total_pages: 10, + }, + some_other_meta: true, + }; + const newMeta = updateMetaPageIndex(oldMeta, 6); + + expect(newMeta.page.current).toEqual(6); + expect(oldMeta.page === newMeta.page).toEqual(false); // Would be true if we had simply done oldMeta.page.current = 6 + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.ts new file mode 100644 index 0000000000000..fb94a88fd0ce7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/table_pagination/index.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Meta } from '../../../../common/types'; + +/** + * Note: App Search's API pages start at 1 & EuiBasicTables' pages start at 0 + * These helpers both automatically handle off-by-1 conversion in addition to + * automatically converting our snake_cased API meta to camelCased EUI props + */ + +export const convertMetaToPagination = (meta: Meta) => ({ + pageIndex: meta.page.current - 1, + pageSize: meta.page.size, + totalItemCount: meta.page.total_results, +}); + +interface EuiBasicTableOnChange { + page: { index: number }; +} +export const handlePageChange = (paginationCallback: Function) => ({ + page: { index }, +}: EuiBasicTableOnChange) => { + paginationCallback(index + 1); +}; + +/** + * Helper for updating Kea `meta` state without mutating nested objs + */ + +export const updateMetaPageIndex = (oldState: Meta, newPageIndex: number) => { + const newMetaState = { ...oldState, page: { ...oldState.page } }; + newMetaState.page.current = newPageIndex; + return newMetaState; +}; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts index d9e84d3e62f28..18fce922e470d 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts @@ -34,11 +34,16 @@ describe('credentials routes', () => { describe('validates', () => { it('correctly', () => { - const request = { query: { 'page[current]': 1 } }; + const request = { + query: { + 'page[current]': 1, + 'page[size]': 10, + }, + }; mockRouter.shouldValidate(request); }); - it('missing page[current]', () => { + it('missing page query params', () => { const request = { query: {} }; mockRouter.shouldThrow(request); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts index 3b691f196c135..22edb16f0aca6 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts @@ -41,6 +41,7 @@ export function registerCredentialsRoutes({ validate: { query: schema.object({ 'page[current]': schema.number(), + 'page[size]': schema.number(), }), }, }, diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts index 6fbc9f5bd2fc4..779c51131b472 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts @@ -11,14 +11,11 @@ import { registerEnginesRoutes } from './engines'; describe('engine routes', () => { describe('GET /api/app_search/engines', () => { - const AUTH_HEADER = 'Basic 123'; const mockRequest = { - headers: { - authorization: AUTH_HEADER, - }, query: { type: 'indexed', - pageIndex: 1, + 'page[current]': 1, + 'page[size]': 10, }, }; @@ -42,17 +39,6 @@ describe('engine routes', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/as/engines/collection', - params: { type: 'indexed', 'page[current]': 1, 'page[size]': 10 }, - hasValidData: expect.any(Function), - }); - }); - - it('passes custom parameters to enterpriseSearchRequestHandler', () => { - mockRouter.callRoute({ query: { type: 'meta', pageIndex: 99 } }); - - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/collection', - params: { type: 'meta', 'page[current]': 99, 'page[size]': 10 }, hasValidData: expect.any(Function), }); }); @@ -84,27 +70,29 @@ describe('engine routes', () => { describe('validates', () => { it('correctly', () => { - const request = { query: { type: 'meta', pageIndex: 5 } }; + const request = { + query: { + type: 'meta', + 'page[current]': 5, + 'page[size]': 10, + }, + }; mockRouter.shouldValidate(request); }); - it('wrong pageIndex type', () => { - const request = { query: { type: 'indexed', pageIndex: 'indexed' } }; - mockRouter.shouldThrow(request); - }); - it('wrong type string', () => { - const request = { query: { type: 'invalid', pageIndex: 1 } }; - mockRouter.shouldThrow(request); - }); - - it('missing pageIndex', () => { - const request = { query: { type: 'indexed' } }; + const request = { + query: { + type: 'invalid', + 'page[current]': 5, + 'page[size]': 10, + }, + }; mockRouter.shouldThrow(request); }); - it('missing type', () => { - const request = { query: { pageIndex: 1 } }; + it('missing query params', () => { + const request = { query: {} }; mockRouter.shouldThrow(request); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts index 7d537e5dc0df3..9bff6cf127dd3 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts @@ -7,7 +7,6 @@ import { schema } from '@kbn/config-schema'; -import { ENGINES_PAGE_SIZE } from '../../../common/constants'; import { RouteDependencies } from '../../plugin'; interface EnginesResponse { @@ -25,20 +24,14 @@ export function registerEnginesRoutes({ validate: { query: schema.object({ type: schema.oneOf([schema.literal('indexed'), schema.literal('meta')]), - pageIndex: schema.number(), + 'page[current]': schema.number(), + 'page[size]': schema.number(), }), }, }, async (context, request, response) => { - const { type, pageIndex } = request.query; - return enterpriseSearchRequestHandler.createRequest({ path: '/as/engines/collection', - params: { - type, - 'page[current]': pageIndex, - 'page[size]': ENGINES_PAGE_SIZE, - }, hasValidData: (body?: EnginesResponse) => Array.isArray(body?.results) && typeof body?.meta?.page?.total_results === 'number', })(context, request, response); From 41a02a7a9e75716fe894585b6c4c91a50bb59459 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Tue, 23 Feb 2021 19:45:15 +0100 Subject: [PATCH 46/70] Bump Node.js from version 14.15.4 to 14.16.0 (#92421) --- .node-version | 2 +- .nvmrc | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.node-version b/.node-version index c91434ab584a7..2a0dc9a810cf3 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -14.15.4 +14.16.0 diff --git a/.nvmrc b/.nvmrc index c91434ab584a7..2a0dc9a810cf3 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14.15.4 +14.16.0 diff --git a/package.json b/package.json index 72fc72ea5f055..70918f02dcd41 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "**/typescript": "4.1.3" }, "engines": { - "node": "14.15.4", + "node": "14.16.0", "yarn": "^1.21.1" }, "dependencies": { From f4b16f3d872763108a8b6aab8187a04deaee8ea6 Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Tue, 23 Feb 2021 14:06:36 -0500 Subject: [PATCH 47/70] [Maps] Fix geo shape agg telemetry not collecting due to missing index pattern IDs (#90886) --- .../plugins/maps/server/maps_telemetry/maps_telemetry.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts index 0387d96046cb1..8f9b529ae30f5 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts @@ -24,6 +24,8 @@ import { import { MapSavedObject, MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import { getIndexPatternsService, getInternalRepository } from '../kibana_server_services'; import { MapsConfigType } from '../../config'; +// @ts-expect-error +import { injectReferences } from '././../../common/migrations/references'; interface Settings { showMapVisualizationTypes: boolean; @@ -310,7 +312,12 @@ export async function getMapsTelemetry(config: MapsConfigType): Promise( MAP_SAVED_OBJECT_TYPE, - (savedObjects) => layerLists.push(...getLayerLists(savedObjects)) + (savedObjects) => { + const savedObjectsWithIndexPatternIds = savedObjects.map((savedObject) => { + return injectReferences(savedObject); + }); + return layerLists.push(...getLayerLists(savedObjectsWithIndexPatternIds)); + } ); const savedObjectsTelemetry = buildMapsSavedObjectsTelemetry(layerLists); From a88106cc8b0f0b4bbb56ce51edd87367671d236d Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 23 Feb 2021 12:26:36 -0700 Subject: [PATCH 48/70] [esArchiver] pass x-elastic-product-origin header to ES (#91566) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../src/actions/empty_kibana_index.ts | 2 +- packages/kbn-es-archiver/src/actions/load.ts | 17 ++++--- .../kbn-es-archiver/src/client_headers.ts | 11 +++++ .../docs/generate_doc_records_stream.test.ts | 10 ++++ .../lib/docs/generate_doc_records_stream.ts | 24 ++++++---- .../lib/docs/index_doc_records_stream.test.ts | 10 ++++ .../src/lib/docs/index_doc_records_stream.ts | 46 +++++++++++-------- .../lib/indices/create_index_stream.test.ts | 8 ++++ .../src/lib/indices/create_index_stream.ts | 22 +++++---- .../src/lib/indices/delete_index.ts | 38 +++++++++++---- .../indices/generate_index_records_stream.ts | 45 +++++++++++------- .../src/lib/indices/kibana_index.ts | 31 ++++++++----- 12 files changed, 181 insertions(+), 83 deletions(-) create mode 100644 packages/kbn-es-archiver/src/client_headers.ts diff --git a/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts b/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts index f86865ffa6670..300c9f4dd66b0 100644 --- a/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts +++ b/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts @@ -24,7 +24,7 @@ export async function emptyKibanaIndexAction({ const kibanaPluginIds = await kbnClient.plugins.getEnabledIds(); await cleanKibanaIndices({ client, stats, log, kibanaPluginIds }); - await migrateKibanaIndex({ client, kbnClient }); + await migrateKibanaIndex(kbnClient); stats.createdIndex('.kibana'); return stats.toJSON(); } diff --git a/packages/kbn-es-archiver/src/actions/load.ts b/packages/kbn-es-archiver/src/actions/load.ts index e1552107eec7c..60af6b3aa747b 100644 --- a/packages/kbn-es-archiver/src/actions/load.ts +++ b/packages/kbn-es-archiver/src/actions/load.ts @@ -11,8 +11,8 @@ import { createReadStream } from 'fs'; import { Readable } from 'stream'; import { ToolingLog, KbnClient } from '@kbn/dev-utils'; import { Client } from '@elastic/elasticsearch'; - import { createPromiseFromStreams, concatStreamProviders } from '@kbn/utils'; +import { ES_CLIENT_HEADERS } from '../client_headers'; import { isGzip, @@ -90,14 +90,19 @@ export async function loadAction({ } } - await client.indices.refresh({ - index: '_all', - allow_no_indices: true, - }); + await client.indices.refresh( + { + index: '_all', + allow_no_indices: true, + }, + { + headers: ES_CLIENT_HEADERS, + } + ); // If we affected the Kibana index, we need to ensure it's migrated... if (Object.keys(result).some((k) => k.startsWith('.kibana'))) { - await migrateKibanaIndex({ client, kbnClient }); + await migrateKibanaIndex(kbnClient); log.debug('[%s] Migrated Kibana index after loading Kibana data', name); if (kibanaPluginIds.includes('spaces')) { diff --git a/packages/kbn-es-archiver/src/client_headers.ts b/packages/kbn-es-archiver/src/client_headers.ts new file mode 100644 index 0000000000000..da240c3ad8318 --- /dev/null +++ b/packages/kbn-es-archiver/src/client_headers.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +export const ES_CLIENT_HEADERS = { + 'x-elastic-product-origin': 'kibana', +}; diff --git a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts index f62fae2d197ee..da7ed4c81b666 100644 --- a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts +++ b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts @@ -107,6 +107,11 @@ it('transforms each input index to a stream of docs using scrollSearch helper', "scroll": "1m", "size": 1000, }, + Object { + "headers": Object { + "x-elastic-product-origin": "kibana", + }, + }, ], Array [ Object { @@ -119,6 +124,11 @@ it('transforms each input index to a stream of docs using scrollSearch helper', "scroll": "1m", "size": 1000, }, + Object { + "headers": Object { + "x-elastic-product-origin": "kibana", + }, + }, ], ], "results": Array [ diff --git a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts index 4afc29fad6e22..cacd224e71421 100644 --- a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts @@ -10,6 +10,7 @@ import { Transform } from 'stream'; import { Client } from '@elastic/elasticsearch'; import { Stats } from '../stats'; import { Progress } from '../progress'; +import { ES_CLIENT_HEADERS } from '../../client_headers'; const SCROLL_SIZE = 1000; const SCROLL_TIMEOUT = '1m'; @@ -30,16 +31,21 @@ export function createGenerateDocRecordsStream({ readableObjectMode: true, async transform(index, enc, callback) { try { - const interator = client.helpers.scrollSearch({ - index, - scroll: SCROLL_TIMEOUT, - size: SCROLL_SIZE, - _source: 'true', - body: { - query, + const interator = client.helpers.scrollSearch( + { + index, + scroll: SCROLL_TIMEOUT, + size: SCROLL_SIZE, + _source: 'true', + body: { + query, + }, + rest_total_hits_as_int: true, }, - rest_total_hits_as_int: true, - }); + { + headers: ES_CLIENT_HEADERS, + } + ); let remainingHits: number | null = null; diff --git a/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.test.ts b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.test.ts index 623410ae6a354..2aa71fd23a711 100644 --- a/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.test.ts +++ b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.test.ts @@ -152,6 +152,11 @@ it('indexes documents using the bulk client helper', async () => { "onDrop": [Function], "retries": 5, }, + Object { + "headers": Object { + "x-elastic-product-origin": "kibana", + }, + }, ], Array [ Object { @@ -170,6 +175,11 @@ it('indexes documents using the bulk client helper', async () => { "onDrop": [Function], "retries": 5, }, + Object { + "headers": Object { + "x-elastic-product-origin": "kibana", + }, + }, ], ], "results": Array [ diff --git a/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts index 98a9cdbe38ffd..e105a243cae76 100644 --- a/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts @@ -11,6 +11,7 @@ import AggregateError from 'aggregate-error'; import { Writable } from 'stream'; import { Stats } from '../stats'; import { Progress } from '../progress'; +import { ES_CLIENT_HEADERS } from '../../client_headers'; export function createIndexDocRecordsStream( client: Client, @@ -23,27 +24,32 @@ export function createIndexDocRecordsStream( const ops = new WeakMap(); const errors: string[] = []; - await client.helpers.bulk({ - retries: 5, - datasource: docs.map((doc) => { - const body = doc.source; - ops.set(body, { - [operation]: { - _index: doc.index, - _id: doc.id, - }, - }); - return body; - }), - onDocument(doc) { - return ops.get(doc); + await client.helpers.bulk( + { + retries: 5, + datasource: docs.map((doc) => { + const body = doc.source; + ops.set(body, { + [operation]: { + _index: doc.index, + _id: doc.id, + }, + }); + return body; + }), + onDocument(doc) { + return ops.get(doc); + }, + onDrop(dropped) { + const dj = JSON.stringify(dropped.document); + const ej = JSON.stringify(dropped.error); + errors.push(`Bulk doc failure [operation=${operation}]:\n doc: ${dj}\n error: ${ej}`); + }, }, - onDrop(dropped) { - const dj = JSON.stringify(dropped.document); - const ej = JSON.stringify(dropped.error); - errors.push(`Bulk doc failure [operation=${operation}]:\n doc: ${dj}\n error: ${ej}`); - }, - }); + { + headers: ES_CLIENT_HEADERS, + } + ); if (errors.length) { throw new AggregateError(errors); diff --git a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts index cc7ae7971424f..39e00ff0c72c0 100644 --- a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts +++ b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts @@ -65,6 +65,9 @@ describe('esArchiver: createCreateIndexStream()', () => { ], }, Object { + "headers": Object { + "x-elastic-product-origin": "kibana", + }, "ignore": Array [ 404, ], @@ -81,6 +84,11 @@ describe('esArchiver: createCreateIndexStream()', () => { "actual-index", ], }, + Object { + "headers": Object { + "x-elastic-product-origin": "kibana", + }, + }, ], ] `); diff --git a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts index 11220bb7562f9..ca89278305813 100644 --- a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts @@ -15,6 +15,7 @@ import { ToolingLog } from '@kbn/dev-utils'; import { Stats } from '../stats'; import { deleteKibanaIndices } from './kibana_index'; import { deleteIndex } from './delete_index'; +import { ES_CLIENT_HEADERS } from '../../client_headers'; interface DocRecord { value: { @@ -63,15 +64,20 @@ export function createCreateIndexStream({ kibanaIndexAlreadyDeleted = true; } - await client.indices.create({ - method: 'PUT', - index, - body: { - settings, - mappings, - aliases, + await client.indices.create( + { + method: 'PUT', + index, + body: { + settings, + mappings, + aliases, + }, }, - }); + { + headers: ES_CLIENT_HEADERS, + } + ); stats.createdIndex(index, { settings }); } catch (err) { diff --git a/packages/kbn-es-archiver/src/lib/indices/delete_index.ts b/packages/kbn-es-archiver/src/lib/indices/delete_index.ts index 1e411a33f62b6..b5641eec4b9da 100644 --- a/packages/kbn-es-archiver/src/lib/indices/delete_index.ts +++ b/packages/kbn-es-archiver/src/lib/indices/delete_index.ts @@ -9,6 +9,7 @@ import { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/dev-utils'; import { Stats } from '../stats'; +import { ES_CLIENT_HEADERS } from '../../client_headers'; // see https://github.com/elastic/elasticsearch/blob/99f88f15c5febbca2d13b5b5fda27b844153bf1a/server/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java#L313-L319 const PENDING_SNAPSHOT_STATUSES = ['INIT', 'STARTED', 'WAITING']; @@ -30,6 +31,7 @@ export async function deleteIndex(options: { }, { ignore: [404], + headers: ES_CLIENT_HEADERS, } ); @@ -38,7 +40,12 @@ export async function deleteIndex(options: { try { const indicesToDelete = await getIndicesToDelete(); - await client.indices.delete({ index: indicesToDelete }); + await client.indices.delete( + { index: indicesToDelete }, + { + headers: ES_CLIENT_HEADERS, + } + ); for (const index of indices) { stats.deletedIndex(index); } @@ -86,10 +93,15 @@ export async function waitForSnapshotCompletion( body: { snapshots: [status], }, - } = await client.snapshot.status({ - repository, - snapshot, - }); + } = await client.snapshot.status( + { + repository, + snapshot, + }, + { + headers: ES_CLIENT_HEADERS, + } + ); log.debug(`Snapshot ${repository}/${snapshot} is ${status.state}`); return PENDING_SNAPSHOT_STATUSES.includes(status.state); @@ -98,15 +110,21 @@ export async function waitForSnapshotCompletion( const getInProgressSnapshots = async (repository: string) => { const { body: { snapshots: inProgressSnapshots }, - } = await client.snapshot.get({ - repository, - snapshot: '_current', - }); + } = await client.snapshot.get( + { + repository, + snapshot: '_current', + }, + { + headers: ES_CLIENT_HEADERS, + } + ); return inProgressSnapshots; }; - for (const repository of Object.keys(await client.snapshot.getRepository({} as any))) { + const { body: repositoryMap } = await client.snapshot.getRepository({} as any); + for (const repository of Object.keys(repositoryMap)) { const allInProgress = await getInProgressSnapshots(repository); const found = allInProgress.find((s: any) => s.indices.includes(index)); diff --git a/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts b/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts index d9c94551e49bf..4e0319c52264f 100644 --- a/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts @@ -9,6 +9,7 @@ import { Transform } from 'stream'; import { Client } from '@elastic/elasticsearch'; import { Stats } from '../stats'; +import { ES_CLIENT_HEADERS } from '../../client_headers'; export function createGenerateIndexRecordsStream(client: Client, stats: Stats) { return new Transform({ @@ -17,22 +18,27 @@ export function createGenerateIndexRecordsStream(client: Client, stats: Stats) { async transform(indexOrAlias, enc, callback) { try { const resp = ( - await client.indices.get({ - index: indexOrAlias, - filter_path: [ - '*.settings', - '*.mappings', - // remove settings that aren't really settings - '-*.settings.index.creation_date', - '-*.settings.index.uuid', - '-*.settings.index.version', - '-*.settings.index.provided_name', - '-*.settings.index.frozen', - '-*.settings.index.search.throttled', - '-*.settings.index.query', - '-*.settings.index.routing', - ], - }) + await client.indices.get( + { + index: indexOrAlias, + filter_path: [ + '*.settings', + '*.mappings', + // remove settings that aren't really settings + '-*.settings.index.creation_date', + '-*.settings.index.uuid', + '-*.settings.index.version', + '-*.settings.index.provided_name', + '-*.settings.index.frozen', + '-*.settings.index.search.throttled', + '-*.settings.index.query', + '-*.settings.index.routing', + ], + }, + { + headers: ES_CLIENT_HEADERS, + } + ) ).body as Record; for (const [index, { settings, mappings }] of Object.entries(resp)) { @@ -40,7 +46,12 @@ export function createGenerateIndexRecordsStream(client: Client, stats: Stats) { body: { [index]: { aliases }, }, - } = await client.indices.getAlias({ index }); + } = await client.indices.getAlias( + { index }, + { + headers: ES_CLIENT_HEADERS, + } + ); stats.archivedIndex(index, { settings, mappings }); this.push({ diff --git a/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts b/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts index ae750023a8e39..7f0080783ee0a 100644 --- a/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts +++ b/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts @@ -12,6 +12,7 @@ import { Client } from '@elastic/elasticsearch'; import { ToolingLog, KbnClient } from '@kbn/dev-utils'; import { Stats } from '../stats'; import { deleteIndex } from './delete_index'; +import { ES_CLIENT_HEADERS } from '../../client_headers'; /** * Deletes all indices that start with `.kibana` @@ -30,10 +31,15 @@ export async function deleteKibanaIndices({ return; } - await client.indices.putSettings({ - index: indexNames, - body: { index: { blocks: { read_only: false } } }, - }); + await client.indices.putSettings( + { + index: indexNames, + body: { index: { blocks: { read_only: false } } }, + }, + { + headers: ES_CLIENT_HEADERS, + } + ); await deleteIndex({ client, @@ -50,13 +56,7 @@ export async function deleteKibanaIndices({ * builds up an object that implements just enough of the kbnMigrations interface * as is required by migrations. */ -export async function migrateKibanaIndex({ - client, - kbnClient, -}: { - client: Client; - kbnClient: KbnClient; -}) { +export async function migrateKibanaIndex(kbnClient: KbnClient) { await kbnClient.savedObjects.migrate(); } @@ -67,7 +67,12 @@ export async function migrateKibanaIndex({ * index (e.g. we don't want to remove .kibana_task_manager or the like). */ async function fetchKibanaIndices(client: Client) { - const resp = await client.cat.indices({ index: '.kibana*', format: 'json' }); + const resp = await client.cat.indices( + { index: '.kibana*', format: 'json' }, + { + headers: ES_CLIENT_HEADERS, + } + ); const isKibanaIndex = (index: string) => /^\.kibana(:?_\d*)?$/.test(index) || /^\.kibana(_task_manager)?_(pre)?\d+\.\d+\.\d+/.test(index); @@ -118,6 +123,7 @@ export async function cleanKibanaIndices({ }, { ignore: [404, 409], + headers: ES_CLIENT_HEADERS, } ); @@ -160,6 +166,7 @@ export async function createDefaultSpace({ index, client }: { index: string; cli }, { ignore: [409], + headers: ES_CLIENT_HEADERS, } ); } From 08fcf443fc1e7ed7be6ef7bd5ad83865e391920b Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Tue, 23 Feb 2021 13:44:46 -0600 Subject: [PATCH 49/70] Add labelAppend support for text form fields (#92106) The forms schema accepts this field for arbitrary field types, but text forms were not using it. For security solution, since we were already using labelAppend in multiple instances of text fields, this has the effect of causing an "Optional" label modifier appear where it didn't before. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../es_ui_shared/static/forms/components/fields/text_field.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/es_ui_shared/static/forms/components/fields/text_field.tsx b/src/plugins/es_ui_shared/static/forms/components/fields/text_field.tsx index fdb86aa1ca91f..ba0dee7bf2ce3 100644 --- a/src/plugins/es_ui_shared/static/forms/components/fields/text_field.tsx +++ b/src/plugins/es_ui_shared/static/forms/components/fields/text_field.tsx @@ -24,6 +24,7 @@ export const TextField = ({ field, euiFieldProps = {}, idAria, ...rest }: Props) return ( Date: Tue, 23 Feb 2021 13:46:41 -0600 Subject: [PATCH 50/70] [build] Add task skips intended for partial builds (#92076) --- src/dev/build/args.test.ts | 21 ++++++ src/dev/build/args.ts | 6 ++ src/dev/build/build_distributables.ts | 69 +++++++++++-------- .../tasks/os_packages/docker_generator/run.ts | 27 ++++---- 4 files changed, 82 insertions(+), 41 deletions(-) diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index 2397c18c04d07..e749af73241cf 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -33,8 +33,11 @@ it('build default and oss dist for current platform, without packages, by defaul "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": false, + "createGenericFolders": true, + "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, + "initialize": true, "isRelease": false, "targetAllPlatforms": false, "versionQualifier": "", @@ -57,8 +60,11 @@ it('builds packages if --all-platforms is passed', () => { "createDockerCentOS": true, "createDockerContexts": true, "createDockerUBI": true, + "createGenericFolders": true, + "createPlatformFolders": true, "createRpmPackage": true, "downloadFreshNode": true, + "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -81,8 +87,11 @@ it('limits packages if --rpm passed with --all-platforms', () => { "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": false, + "createGenericFolders": true, + "createPlatformFolders": true, "createRpmPackage": true, "downloadFreshNode": true, + "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -105,8 +114,11 @@ it('limits packages if --deb passed with --all-platforms', () => { "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": false, + "createGenericFolders": true, + "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, + "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -130,8 +142,11 @@ it('limits packages if --docker passed with --all-platforms', () => { "createDockerCentOS": true, "createDockerContexts": true, "createDockerUBI": true, + "createGenericFolders": true, + "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, + "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -162,8 +177,11 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform "createDockerCentOS": true, "createDockerContexts": true, "createDockerUBI": false, + "createGenericFolders": true, + "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, + "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -187,8 +205,11 @@ it('limits packages if --all-platforms passed with --skip-docker-centos', () => "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": true, + "createGenericFolders": true, + "createPlatformFolders": true, "createRpmPackage": true, "downloadFreshNode": true, + "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index c594eacd08c01..bbfbd3e6f8813 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -18,6 +18,9 @@ export function readCliArgs(argv: string[]) { 'oss', 'no-oss', 'skip-archives', + 'skip-initialize', + 'skip-generic-folders', + 'skip-platform-folders', 'skip-os-packages', 'rpm', 'deb', @@ -93,7 +96,10 @@ export function readCliArgs(argv: string[]) { versionQualifier: flags['version-qualifier'], buildOssDist: flags.oss !== false, buildDefaultDist: !flags.oss, + initialize: !Boolean(flags['skip-initialize']), downloadFreshNode: !Boolean(flags['skip-node-download']), + createGenericFolders: !Boolean(flags['skip-generic-folders']), + createPlatformFolders: !Boolean(flags['skip-platform-folders']), createArchives: !Boolean(flags['skip-archives']), createRpmPackage: isOsPackageDesired('rpm'), createDebPackage: isOsPackageDesired('deb'), diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index 237fc71811a41..f0403fac1e26b 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -16,6 +16,9 @@ export interface BuildOptions { buildOssDist: boolean; buildDefaultDist: boolean; downloadFreshNode: boolean; + initialize: boolean; + createGenericFolders: boolean; + createPlatformFolders: boolean; createArchives: boolean; createRpmPackage: boolean; createDebPackage: boolean; @@ -41,45 +44,53 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions /** * verify, reset, and initialize the build environment */ - await run(Tasks.VerifyEnv); - await run(Tasks.Clean); - await run(options.downloadFreshNode ? Tasks.DownloadNodeBuilds : Tasks.VerifyExistingNodeBuilds); - await run(Tasks.ExtractNodeBuilds); + if (options.initialize) { + await run(Tasks.VerifyEnv); + await run(Tasks.Clean); + await run( + options.downloadFreshNode ? Tasks.DownloadNodeBuilds : Tasks.VerifyExistingNodeBuilds + ); + await run(Tasks.ExtractNodeBuilds); + } /** * run platform-generic build tasks */ - await run(Tasks.CopySource); - await run(Tasks.CopyBinScripts); - await run(Tasks.ReplaceFavicon); - await run(Tasks.CreateEmptyDirsAndFiles); - await run(Tasks.CreateReadme); - await run(Tasks.BuildBazelPackages); - await run(Tasks.BuildPackages); - await run(Tasks.BuildKibanaPlatformPlugins); - await run(Tasks.TranspileBabel); - await run(Tasks.CreatePackageJson); - await run(Tasks.InstallDependencies); - await run(Tasks.CleanPackages); - await run(Tasks.CreateNoticeFile); - await run(Tasks.UpdateLicenseFile); - await run(Tasks.RemovePackageJsonDeps); - await run(Tasks.CleanTypescript); - await run(Tasks.CleanExtraFilesFromModules); - await run(Tasks.CleanEmptyFolders); + if (options.createGenericFolders) { + await run(Tasks.CopySource); + await run(Tasks.CopyBinScripts); + await run(Tasks.ReplaceFavicon); + await run(Tasks.CreateEmptyDirsAndFiles); + await run(Tasks.CreateReadme); + await run(Tasks.BuildBazelPackages); + await run(Tasks.BuildPackages); + await run(Tasks.BuildKibanaPlatformPlugins); + await run(Tasks.TranspileBabel); + await run(Tasks.CreatePackageJson); + await run(Tasks.InstallDependencies); + await run(Tasks.CleanPackages); + await run(Tasks.CreateNoticeFile); + await run(Tasks.UpdateLicenseFile); + await run(Tasks.RemovePackageJsonDeps); + await run(Tasks.CleanTypescript); + await run(Tasks.CleanExtraFilesFromModules); + await run(Tasks.CleanEmptyFolders); + } /** * copy generic build outputs into platform-specific build * directories and perform platform/architecture-specific steps */ - await run(Tasks.CreateArchivesSources); - await run(Tasks.PatchNativeModules); - await run(Tasks.InstallChromium); - await run(Tasks.CleanExtraBinScripts); - await run(Tasks.CleanNodeBuilds); + if (options.createPlatformFolders) { + await run(Tasks.CreateArchivesSources); + await run(Tasks.PatchNativeModules); + await run(Tasks.InstallChromium); + await run(Tasks.CleanExtraBinScripts); + await run(Tasks.CleanNodeBuilds); - await run(Tasks.PathLength); - await run(Tasks.UuidVerification); + await run(Tasks.PathLength); + await run(Tasks.UuidVerification); + } /** * package platform-specific builds into archives diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index 21d2582f205f3..b8029328ac94a 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -83,6 +83,16 @@ export async function runDockerGenerator( revision: config.getBuildSha(), }; + type HostArchitectureToDocker = Record; + const hostTarget: HostArchitectureToDocker = { + x64: 'x64', + arm64: 'aarch64', + }; + const buildArchitectureSupported = hostTarget[process.arch] === flags.architecture && flags.image; + if (!buildArchitectureSupported) { + return; + } + // Verify if we have the needed kibana target in order // to build the kibana docker image. // Also create the docker build target folder @@ -132,18 +142,11 @@ export async function runDockerGenerator( await chmodAsync(`${resolve(dockerBuildDir, 'build_docker.sh')}`, '755'); // Only build images on native targets - type HostArchitectureToDocker = Record; - const hostTarget: HostArchitectureToDocker = { - x64: 'x64', - arm64: 'aarch64', - }; - const buildImage = hostTarget[process.arch] === flags.architecture && flags.image; - if (buildImage) { - await exec(log, `./build_docker.sh`, [], { - cwd: dockerBuildDir, - level: 'info', - }); - } + + await exec(log, `./build_docker.sh`, [], { + cwd: dockerBuildDir, + level: 'info', + }); // Pack Dockerfiles and create a target for them if (flags.context) { From 4f8cd8399b51fd046d6e65eb34f43921042d3d10 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 23 Feb 2021 14:48:49 -0500 Subject: [PATCH 51/70] fix escapeDoubleQuotes regex to ensure quotes escaped correctly (#92471) --- x-pack/plugins/ml/public/application/explorer/explorer_utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js index eb94f00b6bf8d..2f19cbc80f055 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js @@ -631,7 +631,7 @@ export function escapeParens(string) { } export function escapeDoubleQuotes(string) { - return string.replace(/\"/g, '\\$&'); + return string.replace(/[\\"]/g, '\\$&'); } export function getQueryPattern(fieldName, fieldValue) { From 57dfb811d6c8315ce712965e51a3d537c8f53c8e Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 23 Feb 2021 14:04:12 -0600 Subject: [PATCH 52/70] Remove kuery from uiFilters (#91932) * Make kuery a standalone query parameter instead of part of uiFilters * Move getEsFilters helper to RUM * Move query utils from "common" to "server" (it uses esKuery from the data plugin, which is exported from server, not common.) * Move uiFiltersRt to RUM routes References #84526. --- .../app/ErrorGroupDetails/index.tsx | 9 +-- .../app/correlations/error_correlations.tsx | 7 +- .../app/correlations/latency_correlations.tsx | 7 +- .../app/error_group_overview/index.tsx | 9 ++- .../app/service_inventory/index.tsx | 10 ++- .../app/service_node_metrics/index.tsx | 9 ++- .../app/service_node_overview/index.tsx | 9 ++- .../service_overview_errors_table/index.tsx | 10 +-- ...ice_overview_instances_chart_and_table.tsx | 7 +- .../service_overview_throughput_chart.tsx | 9 ++- .../index.tsx | 9 +-- .../app/service_profiling/index.tsx | 9 +-- .../service_profiling_flamegraph.tsx | 9 +-- .../components/app/trace_overview/index.tsx | 9 ++- .../use_transaction_list.ts | 9 ++- .../shared/DatePicker/date_picker.test.tsx | 7 +- .../use_transaction_breakdown.ts | 9 ++- .../transaction_error_rate_chart/index.tsx | 9 ++- .../annotations/annotations_context.tsx | 9 ++- .../mock_url_params_context_provider.tsx | 4 +- .../url_params_context/url_params_context.tsx | 8 +- .../use_error_group_distribution_fetcher.tsx | 9 ++- .../use_service_metric_charts_fetcher.ts | 18 ++--- .../use_transaction_distribution_fetcher.ts | 24 +++--- .../use_transaction_latency_chart_fetcher.ts | 6 +- ...se_transaction_throughput_chart_fetcher.ts | 7 +- .../plugins/apm/public/utils/testHelpers.tsx | 3 - .../chart_preview/get_transaction_duration.ts | 2 +- .../get_transaction_error_count.ts | 2 +- .../get_transaction_error_rate.ts | 2 +- .../alerts/register_error_count_alert_type.ts | 2 +- ...egister_transaction_duration_alert_type.ts | 2 +- ...ister_transaction_error_rate_alert_type.ts | 2 +- .../create_anomaly_detection_jobs.ts | 2 +- .../index.ts | 12 ++- .../index.ts | 12 ++- .../lib/environments/get_environments.ts | 2 +- .../errors/__snapshots__/queries.test.ts.snap | 15 ---- .../__snapshots__/queries.test.ts.snap | 10 --- .../errors/distribution/get_buckets.test.ts | 1 - .../lib/errors/distribution/get_buckets.ts | 12 ++- .../errors/distribution/get_distribution.ts | 3 + .../lib/errors/get_error_group_sample.ts | 12 ++- .../apm/server/lib/errors/get_error_groups.ts | 3 + .../helpers/aggregated_transactions/index.ts | 2 +- .../apm/server/lib/helpers/setup_request.ts | 4 - .../__snapshots__/queries.test.ts.snap | 75 ------------------- .../server/lib/metrics/by_agent/default.ts | 6 +- .../java/gc/fetch_and_transform_gc_metrics.ts | 3 + .../by_agent/java/gc/get_gc_rate_chart.ts | 3 + .../by_agent/java/gc/get_gc_time_chart.ts | 3 + .../by_agent/java/heap_memory/index.ts | 3 + .../server/lib/metrics/by_agent/java/index.ts | 29 ++++--- .../by_agent/java/non_heap_memory/index.ts | 3 + .../by_agent/java/thread_count/index.ts | 3 + .../lib/metrics/by_agent/shared/cpu/index.ts | 3 + .../metrics/by_agent/shared/memory/index.ts | 4 + .../metrics/fetch_and_transform_metrics.ts | 3 + .../get_metrics_chart_data_by_agent.ts | 10 ++- .../get_service_count.ts | 2 +- .../get_transaction_coordinates.ts | 2 +- .../__snapshots__/queries.test.ts.snap | 35 --------- .../apm/server/lib/rum_client/has_rum_data.ts | 2 +- .../ui_filters}/get_es_filter.ts | 21 +----- .../__snapshots__/index.test.ts.snap | 5 -- .../get_local_filter_query.ts | 2 +- .../ui_filters/local_ui_filters/index.test.ts | 2 +- .../fetch_service_paths_from_trace_ids.ts | 2 +- .../lib/service_map/get_service_anomalies.ts | 2 +- .../server/lib/service_map/get_service_map.ts | 4 +- .../get_service_map_service_node_info.test.ts | 1 - .../get_service_map_service_node_info.ts | 2 +- .../lib/service_map/get_trace_sample_ids.ts | 2 +- .../__snapshots__/queries.test.ts.snap | 15 ---- .../apm/server/lib/service_nodes/index.ts | 4 +- .../__snapshots__/queries.test.ts.snap | 5 -- .../get_derived_service_annotations.ts | 2 +- .../annotations/get_stored_annotations.ts | 2 +- .../lib/services/get_service_agent_name.ts | 2 +- .../get_destination_map.ts | 2 +- .../get_service_dependencies/get_metrics.ts | 2 +- ...rvice_error_group_comparison_statistics.ts | 12 ++- ..._service_error_group_primary_statistics.ts | 12 ++- .../get_service_error_groups/index.ts | 14 +++- ...et_service_instance_system_metric_stats.ts | 11 ++- .../get_service_instance_transaction_stats.ts | 11 ++- .../services/get_service_instances/index.ts | 1 + .../services/get_service_metadata_details.ts | 2 +- .../services/get_service_metadata_icons.ts | 2 +- .../lib/services/get_service_node_metadata.ts | 3 + ...transaction_group_comparison_statistics.ts | 12 ++- .../get_service_transaction_groups.ts | 12 ++- .../services/get_service_transaction_types.ts | 2 +- .../get_services/get_legacy_data_status.ts | 2 +- .../get_service_transaction_stats.ts | 12 ++- .../get_services/get_services_items.ts | 4 + .../server/lib/services/get_services/index.ts | 3 + .../apm/server/lib/services/get_throughput.ts | 12 ++- .../get_service_profiling_statistics.ts | 14 +++- .../get_service_profiling_timeline.ts | 9 ++- .../apm/server/lib/traces/get_trace_items.ts | 2 +- .../__snapshots__/queries.test.ts.snap | 35 --------- .../server/lib/transaction_groups/fetcher.ts | 2 + .../lib/transaction_groups/get_error_rate.ts | 12 ++- .../__snapshots__/queries.test.ts.snap | 15 ---- .../lib/transactions/breakdown/index.test.ts | 1 - .../lib/transactions/breakdown/index.ts | 12 ++- .../distribution/get_buckets/index.ts | 9 ++- .../distribution/get_distribution_max.ts | 12 ++- .../lib/transactions/distribution/index.ts | 4 + .../transactions/get_anomaly_data/fetcher.ts | 2 +- .../transactions/get_latency_charts/index.ts | 15 +++- .../get_throughput_charts/index.ts | 15 +++- .../lib/transactions/get_transaction/index.ts | 2 +- .../plugins/apm/server/projections/errors.ts | 12 ++- .../plugins/apm/server/projections/metrics.ts | 12 ++- .../projections/rum_page_load_transactions.ts | 11 +-- .../apm/server/projections/service_nodes.ts | 3 + .../apm/server/projections/services.ts | 8 +- .../apm/server/projections/transactions.ts | 12 ++- .../plugins/apm/server/routes/correlations.ts | 10 ++- .../apm/server/routes/default_api_types.ts | 2 +- x-pack/plugins/apm/server/routes/errors.ts | 31 +++++--- x-pack/plugins/apm/server/routes/metrics.ts | 9 ++- .../plugins/apm/server/routes/rum_client.ts | 11 +-- .../plugins/apm/server/routes/service_map.ts | 2 +- .../apm/server/routes/service_nodes.ts | 6 +- x-pack/plugins/apm/server/routes/services.ts | 69 ++++++++++------- x-pack/plugins/apm/server/routes/traces.ts | 8 +- .../plugins/apm/server/routes/transactions.ts | 44 +++++++---- .../{common => server}/utils/queries.test.ts | 4 +- .../apm/{common => server}/utils/queries.ts | 16 +++- .../plugins/apm/server/utils/test_helpers.tsx | 3 - .../tests/feature_controls.ts | 26 +++---- .../tests/metrics_charts/metrics_charts.ts | 8 +- .../tests/service_overview/instances.ts | 3 - .../error_groups_comparison_statistics.ts | 3 - .../error_groups_primary_statistics.ts | 2 - .../tests/services/throughput.ts | 3 - .../tests/services/top_services.ts | 20 ++--- .../tests/traces/top_traces.ts | 9 +-- .../tests/transactions/breakdown.ts | 9 +-- .../tests/transactions/distribution.ts | 1 - .../tests/transactions/error_rate.ts | 3 +- .../tests/transactions/latency.ts | 41 +++------- .../tests/transactions/throughput.ts | 3 - .../transactions/top_transaction_groups.ts | 5 +- ...ansactions_groups_comparison_statistics.ts | 4 - .../transactions_groups_primary_statistics.ts | 3 - 149 files changed, 644 insertions(+), 672 deletions(-) rename x-pack/plugins/apm/server/lib/{helpers/convert_ui_filters => rum_client/ui_filters}/get_es_filter.ts (58%) rename x-pack/plugins/apm/{common => server}/utils/queries.test.ts (85%) rename x-pack/plugins/apm/{common => server}/utils/queries.ts (73%) diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx index 4cd2db43621a8..4b8d8ddc6f746 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx @@ -67,9 +67,8 @@ type ErrorGroupDetailsProps = RouteComponentProps<{ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) { const { serviceName, groupId } = match.params; - const { urlParams, uiFilters } = useUrlParams(); - const { environment, start, end } = urlParams; - + const { urlParams } = useUrlParams(); + const { environment, kuery, start, end } = urlParams; const { data: errorGroupData } = useFetcher( (callApmApi) => { if (start && end) { @@ -82,15 +81,15 @@ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) { }, query: { environment, + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [environment, serviceName, start, end, groupId, uiFilters] + [environment, kuery, serviceName, start, end, groupId] ); const { errorDistributionData } = useErrorGroupDistributionFetcher({ diff --git a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx index 69f03f56b4c72..6a5582ca24fc7 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx @@ -49,9 +49,10 @@ export function ErrorCorrelations({ onClose }: Props) { ] = useState(null); const { serviceName } = useParams<{ serviceName?: string }>(); - const { urlParams, uiFilters } = useUrlParams(); + const { urlParams } = useUrlParams(); const { environment, + kuery, transactionName, transactionType, start, @@ -71,12 +72,12 @@ export function ErrorCorrelations({ onClose }: Props) { params: { query: { environment, + kuery, serviceName, transactionName, transactionType, start, end, - uiFilters: JSON.stringify(uiFilters), fieldNames: fieldNames.join(','), }, }, @@ -85,12 +86,12 @@ export function ErrorCorrelations({ onClose }: Props) { }, [ environment, + kuery, serviceName, start, end, transactionName, transactionType, - uiFilters, fieldNames, ] ); diff --git a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx index ce88da64dabc4..22dd89918e25d 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx @@ -47,9 +47,10 @@ export function LatencyCorrelations({ onClose }: Props) { ] = useState(null); const { serviceName } = useParams<{ serviceName?: string }>(); - const { urlParams, uiFilters } = useUrlParams(); + const { urlParams } = useUrlParams(); const { environment, + kuery, transactionName, transactionType, start, @@ -76,12 +77,12 @@ export function LatencyCorrelations({ onClose }: Props) { params: { query: { environment, + kuery, serviceName, transactionName, transactionType, start, end, - uiFilters: JSON.stringify(uiFilters), durationPercentile: durationPercentile.toString(10), fieldNames: fieldNames.join(','), }, @@ -91,12 +92,12 @@ export function LatencyCorrelations({ onClose }: Props) { }, [ environment, + kuery, serviceName, start, end, transactionName, transactionType, - uiFilters, durationPercentile, fieldNames, ] diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx index bde23eddaa44f..49e1cc44f9255 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx @@ -27,8 +27,9 @@ interface ErrorGroupOverviewProps { } export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) { - const { urlParams, uiFilters } = useUrlParams(); - const { environment, start, end, sortField, sortDirection } = urlParams; + const { + urlParams: { environment, kuery, start, end, sortField, sortDirection }, + } = useUrlParams(); const { errorDistributionData } = useErrorGroupDistributionFetcher({ serviceName, groupId: undefined, @@ -47,17 +48,17 @@ export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) { }, query: { environment, + kuery, start, end, sortField, sortDirection: normalizedSortDirection, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [environment, serviceName, start, end, sortField, sortDirection, uiFilters] + [environment, kuery, serviceName, start, end, sortField, sortDirection] ); useTrackPageview({ diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index cd17ca0ce023d..1d67ff03b675d 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -36,10 +36,12 @@ const initialData = { let hasDisplayedToast = false; function useServicesFetcher() { - const { urlParams, uiFilters } = useUrlParams(); + const { + urlParams: { environment, kuery, start, end }, + } = useUrlParams(); const { core } = useApmPluginContext(); const upgradeAssistantHref = useUpgradeAssistantHref(); - const { environment, start, end } = urlParams; + const { data = initialData, status } = useFetcher( (callApmApi) => { if (start && end) { @@ -48,15 +50,15 @@ function useServicesFetcher() { params: { query: { environment, + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [environment, start, end, uiFilters] + [environment, kuery, start, end] ); useEffect(() => { diff --git a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx index 21871a17f4b04..186c148fa6918 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx @@ -57,11 +57,12 @@ type ServiceNodeMetricsProps = RouteComponentProps<{ }>; export function ServiceNodeMetrics({ match }: ServiceNodeMetricsProps) { - const { urlParams, uiFilters } = useUrlParams(); + const { + urlParams: { kuery, start, end }, + } = useUrlParams(); const { serviceName, serviceNodeName } = match.params; const { agentName } = useApmServiceContext(); const { data } = useServiceMetricChartsFetcher({ serviceNodeName }); - const { start, end } = urlParams; const { data: { host, containerId } = INITIAL_DATA, status } = useFetcher( (callApmApi) => { @@ -72,15 +73,15 @@ export function ServiceNodeMetrics({ match }: ServiceNodeMetricsProps) { params: { path: { serviceName, serviceNodeName }, query: { + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [serviceName, serviceNodeName, start, end, uiFilters] + [kuery, serviceName, serviceNodeName, start, end] ); const isLoading = status === FETCH_STATUS.LOADING; diff --git a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx index c64bbcb569dde..f4870439fe478 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx @@ -35,8 +35,9 @@ interface ServiceNodeOverviewProps { } function ServiceNodeOverview({ serviceName }: ServiceNodeOverviewProps) { - const { uiFilters, urlParams } = useUrlParams(); - const { start, end } = urlParams; + const { + urlParams: { kuery, start, end }, + } = useUrlParams(); const { data: items = [] } = useFetcher( (callApmApi) => { @@ -50,14 +51,14 @@ function ServiceNodeOverview({ serviceName }: ServiceNodeOverviewProps) { serviceName, }, query: { + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), }, }, }); }, - [serviceName, start, end, uiFilters] + [kuery, serviceName, start, end] ); const columns: Array> = [ diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index 109bf0483f2b0..bbd36a4c8df93 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -43,8 +43,7 @@ const INITIAL_STATE = { export function ServiceOverviewErrorsTable({ serviceName }: Props) { const { - urlParams: { environment, start, end }, - uiFilters, + urlParams: { environment, kuery, start, end }, } = useUrlParams(); const { transactionType } = useApmServiceContext(); const [tableOptions, setTableOptions] = useState<{ @@ -72,9 +71,9 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { path: { serviceName }, query: { environment, + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), transactionType, }, }, @@ -85,7 +84,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { }; }); }, - [environment, start, end, serviceName, uiFilters, transactionType] + [environment, kuery, start, end, serviceName, transactionType] ); const { requestId, items } = data; @@ -116,9 +115,10 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { params: { path: { serviceName }, query: { + environment, + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), numBuckets: 20, transactionType, groupIds, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx index 2f2aaf3156b93..b01529a86e88f 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx @@ -25,8 +25,7 @@ export function ServiceOverviewInstancesChartAndTable({ const { transactionType } = useApmServiceContext(); const { - urlParams: { environment, latencyAggregationType, start, end }, - uiFilters, + urlParams: { environment, kuery, latencyAggregationType, start, end }, } = useUrlParams(); const { data = [], status } = useFetcher( @@ -44,11 +43,11 @@ export function ServiceOverviewInstancesChartAndTable({ }, query: { environment, + kuery, latencyAggregationType, start, end, transactionType, - uiFilters: JSON.stringify(uiFilters), numBuckets: 20, }, }, @@ -56,12 +55,12 @@ export function ServiceOverviewInstancesChartAndTable({ }, [ environment, + kuery, latencyAggregationType, start, end, serviceName, transactionType, - uiFilters, ] ); diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx index 2d38ce2c23ca7..ecb57ae5d5715 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx @@ -28,9 +28,10 @@ export function ServiceOverviewThroughputChart({ }) { const theme = useTheme(); const { serviceName } = useParams<{ serviceName?: string }>(); - const { urlParams, uiFilters } = useUrlParams(); + const { + urlParams: { environment, kuery, start, end }, + } = useUrlParams(); const { transactionType } = useApmServiceContext(); - const { environment, start, end } = urlParams; const { data = INITIAL_STATE, status } = useFetcher( (callApmApi) => { @@ -43,16 +44,16 @@ export function ServiceOverviewThroughputChart({ }, query: { environment, + kuery, start, end, transactionType, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [environment, serviceName, start, end, uiFilters, transactionType] + [environment, kuery, serviceName, start, end, transactionType] ); return ( diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx index 5529f9028b9dd..e4c42b4de90fe 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx @@ -57,8 +57,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) { const { transactionType } = useApmServiceContext(); const { - uiFilters, - urlParams: { environment, start, end, latencyAggregationType }, + urlParams: { environment, kuery, start, end, latencyAggregationType }, } = useUrlParams(); const { data = INITIAL_STATE, status } = useFetcher( @@ -73,9 +72,9 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) { path: { serviceName }, query: { environment, + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), transactionType, latencyAggregationType, }, @@ -89,10 +88,10 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) { }, [ environment, + kuery, serviceName, start, end, - uiFilters, transactionType, latencyAggregationType, ] @@ -128,9 +127,9 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) { path: { serviceName }, query: { environment, + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), numBuckets: 20, transactionType, latencyAggregationType, diff --git a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx index 09a42f9b2df90..23adbb23b2322 100644 --- a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx @@ -33,8 +33,7 @@ export function ServiceProfiling({ environment, }: ServiceProfilingProps) { const { - urlParams: { start, end }, - uiFilters, + urlParams: { kuery, start, end }, } = useUrlParams(); const { data = [] } = useFetcher( @@ -48,15 +47,15 @@ export function ServiceProfiling({ params: { path: { serviceName }, query: { + kuery, start, end, environment, - uiFilters: JSON.stringify(uiFilters), }, }, }); }, - [start, end, serviceName, environment, uiFilters] + [kuery, start, end, serviceName, environment] ); const [valueType, setValueType] = useState(); @@ -125,7 +124,7 @@ export function ServiceProfiling({ valueType={valueType} start={start} end={end} - uiFilters={uiFilters} + kuery={kuery} /> diff --git a/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx b/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx index 03248d2836674..fa1da99dbf072 100644 --- a/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx +++ b/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx @@ -42,7 +42,6 @@ import { asDynamicBytes, asInteger, } from '../../../../common/utils/formatters'; -import { UIFilters } from '../../../../typings/ui_filters'; import { useFetcher } from '../../../hooks/use_fetcher'; import { useTheme } from '../../../hooks/use_theme'; import { px, unit } from '../../../style/variables'; @@ -124,17 +123,17 @@ function CustomTooltip({ export function ServiceProfilingFlamegraph({ serviceName, environment, + kuery, valueType, start, end, - uiFilters, }: { serviceName: string; environment?: string; + kuery?: string; valueType?: ProfilingValueType; start?: string; end?: string; - uiFilters: UIFilters; }) { const theme = useTheme(); @@ -154,16 +153,16 @@ export function ServiceProfilingFlamegraph({ serviceName, }, query: { + kuery, start, end, environment, valueType, - uiFilters: JSON.stringify(uiFilters), }, }, }); }, - [start, end, environment, serviceName, valueType, uiFilters] + [kuery, start, end, environment, serviceName, valueType] ); const points = useMemo(() => { diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx index 8fc9ac12824ba..6d7edcd0a1e35 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx @@ -22,8 +22,9 @@ const DEFAULT_RESPONSE: TracesAPIResponse = { }; export function TraceOverview() { - const { urlParams, uiFilters } = useUrlParams(); - const { environment, start, end } = urlParams; + const { + urlParams: { environment, kuery, start, end }, + } = useUrlParams(); const { status, data = DEFAULT_RESPONSE } = useFetcher( (callApmApi) => { if (start && end) { @@ -32,15 +33,15 @@ export function TraceOverview() { params: { query: { environment, + kuery, start, end, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [environment, start, end, uiFilters] + [environment, kuery, start, end] ); useTrackPageview({ app: 'apm', path: 'traces_overview' }); diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts b/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts index a63788457b8b5..062fd5470e60c 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts @@ -19,9 +19,10 @@ const DEFAULT_RESPONSE: Partial = { }; export function useTransactionListFetcher() { - const { urlParams, uiFilters } = useUrlParams(); + const { + urlParams: { environment, kuery, transactionType, start, end }, + } = useUrlParams(); const { serviceName } = useParams<{ serviceName?: string }>(); - const { environment, transactionType, start, end } = urlParams; const { data = DEFAULT_RESPONSE, error, status } = useFetcher( (callApmApi) => { if (serviceName && start && end && transactionType) { @@ -31,16 +32,16 @@ export function useTransactionListFetcher() { path: { serviceName }, query: { environment, + kuery, start, end, transactionType, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [environment, serviceName, start, end, transactionType, uiFilters] + [environment, kuery, serviceName, start, end, transactionType] ); return { diff --git a/x-pack/plugins/apm/public/components/shared/DatePicker/date_picker.test.tsx b/x-pack/plugins/apm/public/components/shared/DatePicker/date_picker.test.tsx index f21e87da23979..28a45e492169b 100644 --- a/x-pack/plugins/apm/public/components/shared/DatePicker/date_picker.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/DatePicker/date_picker.test.tsx @@ -12,10 +12,7 @@ import { createMemoryHistory } from 'history'; import React, { ReactNode } from 'react'; import { Router } from 'react-router-dom'; import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; -import { - UrlParamsContext, - useUiFilters, -} from '../../../context/url_params_context/url_params_context'; +import { UrlParamsContext } from '../../../context/url_params_context/url_params_context'; import { IUrlParams } from '../../../context/url_params_context/types'; import { DatePicker } from './'; @@ -35,7 +32,7 @@ function MockUrlParamsProvider({ rangeId: 0, refreshTimeRange: mockRefreshTimeRange, urlParams, - uiFilters: useUiFilters(urlParams), + uiFilters: {}, }} children={children} /> diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts index 293a1929ca606..a80c859459557 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts @@ -12,8 +12,9 @@ import { useApmServiceContext } from '../../../../context/apm_service/use_apm_se export function useTransactionBreakdown() { const { serviceName } = useParams<{ serviceName?: string }>(); - const { urlParams, uiFilters } = useUrlParams(); - const { environment, start, end, transactionName } = urlParams; + const { + urlParams: { environment, kuery, start, end, transactionName }, + } = useUrlParams(); const { transactionType } = useApmServiceContext(); const { data = { timeseries: undefined }, error, status } = useFetcher( @@ -26,11 +27,11 @@ export function useTransactionBreakdown() { path: { serviceName }, query: { environment, + kuery, start, end, transactionName, transactionType, - uiFilters: JSON.stringify(uiFilters), }, }, }); @@ -38,12 +39,12 @@ export function useTransactionBreakdown() { }, [ environment, + kuery, serviceName, start, end, transactionType, transactionName, - uiFilters, ] ); diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx index a3da8812966f1..b3c38651ea178 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx @@ -31,9 +31,10 @@ export function TransactionErrorRateChart({ }: Props) { const theme = useTheme(); const { serviceName } = useParams<{ serviceName?: string }>(); - const { urlParams, uiFilters } = useUrlParams(); + const { + urlParams: { environment, kuery, start, end, transactionName }, + } = useUrlParams(); const { transactionType } = useApmServiceContext(); - const { environment, start, end, transactionName } = urlParams; const { data, status } = useFetcher( (callApmApi) => { @@ -47,11 +48,11 @@ export function TransactionErrorRateChart({ }, query: { environment, + kuery, start, end, transactionType, transactionName, - uiFilters: JSON.stringify(uiFilters), }, }, }); @@ -59,10 +60,10 @@ export function TransactionErrorRateChart({ }, [ environment, + kuery, serviceName, start, end, - uiFilters, transactionType, transactionName, ] diff --git a/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx b/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx index 54a1d3a59eb20..ea2feb3d2a4ad 100644 --- a/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx +++ b/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx @@ -23,8 +23,9 @@ export function AnnotationsContextProvider({ children: React.ReactNode; }) { const { serviceName } = useParams<{ serviceName?: string }>(); - const { urlParams } = useUrlParams(); - const { environment, start, end } = urlParams; + const { + urlParams: { environment, start, end }, + } = useUrlParams(); const { data = INITIAL_STATE } = useFetcher( (callApmApi) => { @@ -36,15 +37,15 @@ export function AnnotationsContextProvider({ serviceName, }, query: { + environment, start, end, - environment, }, }, }); } }, - [start, end, environment, serviceName] + [environment, start, end, serviceName] ); return ; diff --git a/x-pack/plugins/apm/public/context/url_params_context/mock_url_params_context_provider.tsx b/x-pack/plugins/apm/public/context/url_params_context/mock_url_params_context_provider.tsx index 7a561072d4bb2..6f34e9dec59e5 100644 --- a/x-pack/plugins/apm/public/context/url_params_context/mock_url_params_context_provider.tsx +++ b/x-pack/plugins/apm/public/context/url_params_context/mock_url_params_context_provider.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { IUrlParams } from './types'; -import { UrlParamsContext, useUiFilters } from './url_params_context'; +import { UrlParamsContext } from './url_params_context'; const defaultUrlParams = { page: 0, @@ -35,7 +35,7 @@ export function MockUrlParamsContextProvider({ rangeId: 0, refreshTimeRange, urlParams, - uiFilters: useUiFilters(urlParams), + uiFilters: {}, }} children={children} /> diff --git a/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx b/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx index 90245b9843b01..bb29d1c40b5e2 100644 --- a/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx +++ b/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx @@ -30,16 +30,12 @@ export interface TimeRange { } function useUiFilters(params: IUrlParams): UIFilters { - const { kuery, environment, ...urlParams } = params; const localUiFilters = mapValues( - pickKeys(urlParams, ...localUIFilterNames), + pickKeys(params, ...localUIFilterNames), (val) => (val ? val.split(',') : []) ) as Partial>; - return useDeepObjectIdentity({ - kuery, - ...localUiFilters, - }); + return useDeepObjectIdentity(localUiFilters); } const defaultRefresh = (_time: TimeRange) => {}; diff --git a/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx index 9ff179e6af6a0..3ef685abe0847 100644 --- a/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx @@ -15,8 +15,9 @@ export function useErrorGroupDistributionFetcher({ serviceName: string; groupId: string | undefined; }) { - const { urlParams, uiFilters } = useUrlParams(); - const { environment, start, end } = urlParams; + const { + urlParams: { environment, kuery, start, end }, + } = useUrlParams(); const { data } = useFetcher( (callApmApi) => { if (start && end) { @@ -26,16 +27,16 @@ export function useErrorGroupDistributionFetcher({ path: { serviceName }, query: { environment, + kuery, start, end, groupId, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [environment, serviceName, start, end, groupId, uiFilters] + [environment, kuery, serviceName, start, end, groupId] ); return { errorDistributionData: data }; diff --git a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts index 87e10f1e8937b..baf3eb51ae033 100644 --- a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts @@ -21,10 +21,12 @@ export function useServiceMetricChartsFetcher({ }: { serviceNodeName: string | undefined; }) { - const { urlParams, uiFilters } = useUrlParams(); + const { + urlParams: { environment, kuery, start, end }, + } = useUrlParams(); const { agentName } = useApmServiceContext(); const { serviceName } = useParams<{ serviceName?: string }>(); - const { environment, start, end } = urlParams; + const { data = INITIAL_DATA, error, status } = useFetcher( (callApmApi) => { if (serviceName && start && end && agentName) { @@ -34,25 +36,17 @@ export function useServiceMetricChartsFetcher({ path: { serviceName }, query: { environment, + kuery, serviceNodeName, start, end, agentName, - uiFilters: JSON.stringify(uiFilters), }, }, }); } }, - [ - environment, - serviceName, - start, - end, - agentName, - serviceNodeName, - uiFilters, - ] + [environment, kuery, serviceName, start, end, agentName, serviceNodeName] ); return { diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts index c493a30716aa5..25632d4b19cf4 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts @@ -23,16 +23,18 @@ const INITIAL_DATA = { export function useTransactionDistributionFetcher() { const { serviceName } = useParams<{ serviceName?: string }>(); - const { urlParams, uiFilters } = useUrlParams(); const { - environment, - start, - end, - transactionType, - transactionId, - traceId, - transactionName, - } = urlParams; + urlParams: { + environment, + kuery, + start, + end, + transactionType, + transactionId, + traceId, + transactionName, + }, + } = useUrlParams(); const history = useHistory(); const { data = INITIAL_DATA, status, error } = useFetcher( @@ -47,13 +49,13 @@ export function useTransactionDistributionFetcher() { }, query: { environment, + kuery, start, end, transactionType, transactionName, transactionId, traceId, - uiFilters: JSON.stringify(uiFilters), }, }, }); @@ -96,12 +98,12 @@ export function useTransactionDistributionFetcher() { // eslint-disable-next-line react-hooks/exhaustive-deps [ environment, + kuery, serviceName, start, end, transactionType, transactionName, - uiFilters, ] ); diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts index cca2e99d84dfd..b92b812bdd430 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts @@ -20,12 +20,12 @@ export function useTransactionLatencyChartsFetcher() { const { urlParams: { environment, + kuery, start, end, transactionName, latencyAggregationType, }, - uiFilters, } = useUrlParams(); const { data, error, status } = useFetcher( @@ -44,11 +44,11 @@ export function useTransactionLatencyChartsFetcher() { path: { serviceName }, query: { environment, + kuery, start, end, transactionType, transactionName, - uiFilters: JSON.stringify(uiFilters), latencyAggregationType, }, }, @@ -57,12 +57,12 @@ export function useTransactionLatencyChartsFetcher() { }, [ environment, + kuery, serviceName, start, end, transactionName, transactionType, - uiFilters, latencyAggregationType, ] ); diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts index 55765cd40c04e..c8ae4fa5823a4 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts @@ -18,8 +18,7 @@ export function useTransactionThroughputChartsFetcher() { const { transactionType } = useApmServiceContext(); const theme = useTheme(); const { - urlParams: { environment, start, end, transactionName }, - uiFilters, + urlParams: { environment, kuery, start, end, transactionName }, } = useUrlParams(); const { data, error, status } = useFetcher( @@ -32,11 +31,11 @@ export function useTransactionThroughputChartsFetcher() { path: { serviceName }, query: { environment, + kuery, start, end, transactionType, transactionName, - uiFilters: JSON.stringify(uiFilters), }, }, }); @@ -44,12 +43,12 @@ export function useTransactionThroughputChartsFetcher() { }, [ environment, + kuery, serviceName, start, end, transactionName, transactionType, - uiFilters, ] ); diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx index f7f6f7486091b..80df113e18190 100644 --- a/x-pack/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -17,7 +17,6 @@ import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; import { - ESFilter, ESSearchRequest, ESSearchResponse, } from '../../../../typings/elasticsearch'; @@ -121,7 +120,6 @@ interface MockSetup { internalClient: any; config: APMConfig; uiFilters: UIFilters; - esFilter: ESFilter[]; indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': string; @@ -183,7 +181,6 @@ export async function inspectSearchParams( } ) as APMConfig, uiFilters: {}, - esFilter: [], indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': 'myIndex', diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts index 3457207eeee3c..86456114698cb 100644 --- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts +++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts @@ -13,7 +13,7 @@ import { TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; import { AlertParams } from '../../../routes/alerts/chart_preview'; import { withApmSpan } from '../../../utils/with_apm_span'; import { getBucketSize } from '../../helpers/get_bucket_size'; diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts index aa85c44284d9d..2cf1317dc44b0 100644 --- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts +++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts @@ -8,7 +8,7 @@ import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; import { AlertParams } from '../../../routes/alerts/chart_preview'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; import { withApmSpan } from '../../../utils/with_apm_span'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts index 88e249a71a81f..f0c8d23e0e8fa 100644 --- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts @@ -12,7 +12,7 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; import { AlertParams } from '../../../routes/alerts/chart_preview'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts index c7861eaa819ae..cf31ff69bad27 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts @@ -27,7 +27,7 @@ import { SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; -import { environmentQuery } from '../../../common/utils/queries'; +import { environmentQuery } from '../../../server/utils/queries'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts index 704aee932a604..6de9ca6db8c12 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts @@ -20,7 +20,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; import { getDurationFormatter } from '../../../common/utils/formatters'; -import { environmentQuery } from '../../../common/utils/queries'; +import { environmentQuery } from '../../../server/utils/queries'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts index 6f58b7714d832..b9923cc339ac2 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts @@ -22,7 +22,7 @@ import { import { EventOutcome } from '../../../common/event_outcome'; import { ProcessorEvent } from '../../../common/processor_event'; import { asDecimalOrInteger } from '../../../common/utils/formatters'; -import { environmentQuery } from '../../../common/utils/queries'; +import { environmentQuery } from '../../../server/utils/queries'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; diff --git a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts index d70e19bf4a5f5..dd36e7cbeb24e 100644 --- a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts +++ b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts @@ -11,7 +11,7 @@ import { snakeCase } from 'lodash'; import Boom from '@hapi/boom'; import { ML_ERRORS } from '../../../common/anomaly_detection'; import { ProcessorEvent } from '../../../common/processor_event'; -import { environmentQuery } from '../../../common/utils/queries'; +import { environmentQuery } from '../../../server/utils/queries'; import { Setup } from '../helpers/setup_request'; import { TRANSACTION_DURATION, diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts index ecefdfc2b3d9b..e2411d1d17adc 100644 --- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts +++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts @@ -13,7 +13,11 @@ import { } from '../process_significant_term_aggs'; import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations'; import { ESFilter } from '../../../../../../typings/elasticsearch'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { EVENT_OUTCOME, SERVICE_NAME, @@ -32,6 +36,7 @@ import { withApmSpan } from '../../../utils/with_apm_span'; export async function getCorrelationsForFailedTransactions({ environment, + kuery, serviceName, transactionType, transactionName, @@ -39,6 +44,7 @@ export async function getCorrelationsForFailedTransactions({ setup, }: { environment?: string; + kuery?: string; serviceName: string | undefined; transactionType: string | undefined; transactionName: string | undefined; @@ -46,13 +52,13 @@ export async function getCorrelationsForFailedTransactions({ setup: Setup & SetupTimeRange; }) { return withApmSpan('get_correlations_for_failed_transactions', async () => { - const { start, end, esFilter, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const backgroundFilters: ESFilter[] = [ { term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; if (serviceName) { diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts index 832b89a18d102..824b290a6ba60 100644 --- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts +++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts @@ -7,7 +7,11 @@ import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations'; import { ESFilter } from '../../../../../../typings/elasticsearch'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { SERVICE_NAME, TRANSACTION_DURATION, @@ -24,6 +28,7 @@ import { withApmSpan } from '../../../utils/with_apm_span'; export async function getCorrelationsForSlowTransactions({ environment, + kuery, serviceName, transactionType, transactionName, @@ -32,6 +37,7 @@ export async function getCorrelationsForSlowTransactions({ setup, }: { environment?: string; + kuery?: string; serviceName: string | undefined; transactionType: string | undefined; transactionName: string | undefined; @@ -40,13 +46,13 @@ export async function getCorrelationsForSlowTransactions({ setup: Setup & SetupTimeRange; }) { return withApmSpan('get_correlations_for_slow_transactions', async () => { - const { start, end, esFilter, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const backgroundFilters: ESFilter[] = [ { term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; if (serviceName) { diff --git a/x-pack/plugins/apm/server/lib/environments/get_environments.ts b/x-pack/plugins/apm/server/lib/environments/get_environments.ts index af88493c148ce..509e4cdcd67ac 100644 --- a/x-pack/plugins/apm/server/lib/environments/get_environments.ts +++ b/x-pack/plugins/apm/server/lib/environments/get_environments.ts @@ -11,7 +11,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ENVIRONMENT_NOT_DEFINED } from '../../../common/environment_filter_values'; import { ProcessorEvent } from '../../../common/processor_event'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { withApmSpan } from '../../utils/with_apm_span'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap index 632232ffb075d..c0d928ebd70f6 100644 --- a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap @@ -30,11 +30,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], "should": Array [ Object { @@ -117,11 +112,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -192,11 +182,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap index b329499c8b045..121cbe226d387 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap @@ -38,11 +38,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -89,11 +84,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "error.grouping_key": "foo", diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts index 1712699162b73..b1260d653f3de 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts @@ -44,7 +44,6 @@ describe('get buckets', () => { } ) as APMConfig, uiFilters: {}, - esFilter: [], indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': 'apm-*', diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts index fbe406d8d1a9d..1e161b0383f0b 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts @@ -11,30 +11,36 @@ import { SERVICE_NAME, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { withApmSpan } from '../../../utils/with_apm_span'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; export async function getBuckets({ environment, + kuery, serviceName, groupId, bucketSize, setup, }: { environment?: string; + kuery?: string; serviceName: string; groupId?: string; bucketSize: number; setup: Setup & SetupTimeRange; }) { return withApmSpan('get_error_distribution_buckets', async () => { - const { start, end, esFilter, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const filter: ESFilter[] = [ { term: { [SERVICE_NAME]: serviceName } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; if (groupId) { diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts index 1fb0cbad4a5f0..6bb43a395f235 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts @@ -15,11 +15,13 @@ function getBucketSize({ start, end }: SetupTimeRange) { export async function getErrorDistribution({ environment, + kuery, serviceName, groupId, setup, }: { environment?: string; + kuery?: string; serviceName: string; groupId?: string; setup: Setup & SetupTimeRange; @@ -27,6 +29,7 @@ export async function getErrorDistribution({ const bucketSize = getBucketSize({ start: setup.start, end: setup.end }); const { buckets, noHits } = await getBuckets({ environment, + kuery, serviceName, groupId, bucketSize, diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts b/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts index 0ab26f3c6e969..d4ad2c8a9b2cb 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts @@ -11,24 +11,30 @@ import { TRANSACTION_SAMPLED, } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../server/utils/queries'; import { withApmSpan } from '../../utils/with_apm_span'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getTransaction } from '../transactions/get_transaction'; export function getErrorGroupSample({ environment, + kuery, serviceName, groupId, setup, }: { environment?: string; + kuery?: string; serviceName: string; groupId: string; setup: Setup & SetupTimeRange; }) { return withApmSpan('get_error_group_sample', async () => { - const { start, end, esFilter, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const params = { apm: { @@ -43,7 +49,7 @@ export function getErrorGroupSample({ { term: { [ERROR_GROUP_ID]: groupId } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], should: [{ term: { [TRANSACTION_SAMPLED]: true } }], }, diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts index 28d89eb057470..5371d69caaa99 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts @@ -22,12 +22,14 @@ import { Setup, SetupTimeRange } from '../helpers/setup_request'; export function getErrorGroups({ environment, + kuery, serviceName, sortField, sortDirection = 'desc', setup, }: { environment?: string; + kuery?: string; serviceName: string; sortField?: string; sortDirection?: 'asc' | 'desc'; @@ -41,6 +43,7 @@ export function getErrorGroups({ const projection = getErrorGroupsProjection({ environment, + kuery, setup, serviceName, }); diff --git a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts index 71744c3e59092..394cf6b988f12 100644 --- a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts @@ -6,7 +6,7 @@ */ import { SearchAggregatedTransactionSetting } from '../../../../common/aggregated_transactions'; -import { rangeQuery } from '../../../../common/utils/queries'; +import { rangeQuery } from '../../../../server/utils/queries'; import { ProcessorEvent } from '../../../../common/processor_event'; import { TRANSACTION_DURATION, diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts index b12a396befe8c..60fb9a8bfa85a 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts @@ -10,14 +10,12 @@ import { isActivePlatinumLicense } from '../../../common/license_check'; import { APMConfig } from '../..'; import { KibanaRequest } from '../../../../../../src/core/server'; import { UI_SETTINGS } from '../../../../../../src/plugins/data/common'; -import { ESFilter } from '../../../../../typings/elasticsearch'; import { UIFilters } from '../../../typings/ui_filters'; import { APMRequestHandlerContext } from '../../routes/typings'; import { ApmIndicesConfig, getApmIndices, } from '../settings/apm_indices/get_apm_indices'; -import { getEsFilter } from './convert_ui_filters/get_es_filter'; import { APMEventClient, createApmEventClient, @@ -38,7 +36,6 @@ export interface Setup { config: APMConfig; indices: ApmIndicesConfig; uiFilters: UIFilters; - esFilter: ESFilter[]; } export interface SetupTimeRange { @@ -110,7 +107,6 @@ export async function setupRequest( : undefined, config, uiFilters, - esFilter: getEsFilter(uiFilters), }; return { diff --git a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap index 4eed09f3e5c28..ad91356ac6448 100644 --- a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap @@ -85,11 +85,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -173,11 +168,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -336,11 +326,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "exists": Object { "field": "system.process.cgroup.memory.mem.usage.bytes", @@ -429,11 +414,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -512,11 +492,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -621,11 +596,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -715,11 +685,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -884,11 +849,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "exists": Object { "field": "system.process.cgroup.memory.mem.usage.bytes", @@ -983,11 +943,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -1072,11 +1027,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -1170,11 +1120,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -1253,11 +1198,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -1411,11 +1351,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "exists": Object { "field": "system.process.cgroup.memory.mem.usage.bytes", @@ -1499,11 +1434,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", @@ -1577,11 +1507,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "term": Object { "agent.name": "java", diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts index c5e80600b69d4..e4d6d2e77f73c 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts @@ -11,16 +11,18 @@ import { getMemoryChartData } from './shared/memory'; export async function getDefaultMetricsCharts({ environment, + kuery, serviceName, setup, }: { environment?: string; + kuery?: string; serviceName: string; setup: Setup & SetupTimeRange; }) { const charts = await Promise.all([ - getCPUChartData({ environment, setup, serviceName }), - getMemoryChartData({ environment, setup, serviceName }), + getCPUChartData({ environment, kuery, setup, serviceName }), + getMemoryChartData({ environment, kuery, setup, serviceName }), ]); return { charts }; diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts index d7c9294c8ec7a..9f83af989fc57 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts @@ -23,6 +23,7 @@ import { getVizColorForIndex } from '../../../../../../common/viz_colors'; export async function fetchAndTransformGcMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, @@ -30,6 +31,7 @@ export async function fetchAndTransformGcMetrics({ fieldName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -42,6 +44,7 @@ export async function fetchAndTransformGcMetrics({ const projection = getMetricsProjection({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts index 8c5b9fb3db922..388331f3bbf17 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts @@ -34,11 +34,13 @@ const chartBase: ChartBase = { function getGcRateChart({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -46,6 +48,7 @@ function getGcRateChart({ return withApmSpan('get_gc_rate_charts', () => fetchAndTransformGcMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts index 98f31f06c1b64..e6f80190d1daa 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts @@ -34,11 +34,13 @@ const chartBase: ChartBase = { function getGcTimeChart({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -46,6 +48,7 @@ function getGcTimeChart({ return withApmSpan('get_gc_time_charts', () => fetchAndTransformGcMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts index d6cbc4a07e8f9..7630827a3cb38 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts @@ -54,11 +54,13 @@ const chartBase: ChartBase = { export function getHeapMemoryChart({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -66,6 +68,7 @@ export function getHeapMemoryChart({ return withApmSpan('get_heap_memory_charts', () => fetchAndTransformMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts index 970b4d3499b79..5a266b57bd598 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts @@ -17,29 +17,34 @@ import { getGcTimeChart } from './gc/get_gc_time_chart'; export function getJavaMetricsCharts({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; }) { return withApmSpan('get_java_system_metric_charts', async () => { + const options = { + environment, + kuery, + setup, + serviceName, + serviceNodeName, + }; + const charts = await Promise.all([ - getCPUChartData({ environment, setup, serviceName, serviceNodeName }), - getMemoryChartData({ environment, setup, serviceName, serviceNodeName }), - getHeapMemoryChart({ environment, setup, serviceName, serviceNodeName }), - getNonHeapMemoryChart({ - environment, - setup, - serviceName, - serviceNodeName, - }), - getThreadCountChart({ environment, setup, serviceName, serviceNodeName }), - getGcRateChart({ environment, setup, serviceName, serviceNodeName }), - getGcTimeChart({ environment, setup, serviceName, serviceNodeName }), + getCPUChartData(options), + getMemoryChartData(options), + getHeapMemoryChart(options), + getNonHeapMemoryChart(options), + getThreadCountChart(options), + getGcRateChart(options), + getGcTimeChart(options), ]); return { charts }; diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts index 25abd2c34c83a..cd11e5e5383b6 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts @@ -51,11 +51,13 @@ const chartBase: ChartBase = { export async function getNonHeapMemoryChart({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -63,6 +65,7 @@ export async function getNonHeapMemoryChart({ return withApmSpan('get_non_heap_memory_charts', () => fetchAndTransformMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts index c8a209fee701a..8d4c079197d19 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts @@ -43,11 +43,13 @@ const chartBase: ChartBase = { export async function getThreadCountChart({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -55,6 +57,7 @@ export async function getThreadCountChart({ return withApmSpan('get_thread_count_charts', () => fetchAndTransformMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts index ebfe504e5269b..37bef191ae876 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts @@ -55,11 +55,13 @@ const chartBase: ChartBase = { export function getCPUChartData({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -67,6 +69,7 @@ export function getCPUChartData({ return withApmSpan('get_cpu_metric_charts', () => fetchAndTransformMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts index 55b3328bcd2a9..c58fb170bd2b0 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts @@ -72,11 +72,13 @@ export const percentCgroupMemoryUsedScript = { export async function getMemoryChartData({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -87,6 +89,7 @@ export async function getMemoryChartData({ () => fetchAndTransformMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, @@ -105,6 +108,7 @@ export async function getMemoryChartData({ return await withApmSpan('get_system_memory_metrics_charts', () => fetchAndTransformMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts index 17e9aef29ba82..ef24b531d8046 100644 --- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts @@ -49,6 +49,7 @@ interface Filter { export async function fetchAndTransformMetrics({ environment, + kuery, setup, serviceName, serviceNodeName, @@ -57,6 +58,7 @@ export async function fetchAndTransformMetrics({ additionalFilters = [], }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -68,6 +70,7 @@ export async function fetchAndTransformMetrics({ const projection = getMetricsProjection({ environment, + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts index eda71ef380ee9..8e7008ef913fa 100644 --- a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts +++ b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts @@ -16,12 +16,14 @@ export interface MetricsChartsByAgentAPIResponse { export async function getMetricsChartDataByAgent({ environment, + kuery, setup, serviceName, serviceNodeName, agentName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; @@ -31,6 +33,7 @@ export async function getMetricsChartDataByAgent({ case 'java': { return getJavaMetricsCharts({ environment, + kuery, setup, serviceName, serviceNodeName, @@ -38,7 +41,12 @@ export async function getMetricsChartDataByAgent({ } default: { - return getDefaultMetricsCharts({ environment, setup, serviceName }); + return getDefaultMetricsCharts({ + environment, + kuery, + setup, + serviceName, + }); } } } diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts index c7ac678899b58..2ccbe318862f1 100644 --- a/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts +++ b/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts @@ -6,7 +6,7 @@ */ import { ProcessorEvent } from '../../../common/processor_event'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts index 2da4b0f8de363..aac18e2bdfe4c 100644 --- a/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts +++ b/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { Coordinates } from '../../../../observability/typings/common'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap index 6b4bc844f21c3..79eb0fbce5498 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap @@ -58,11 +58,6 @@ Object { "transaction.type": "page-load", }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -158,11 +153,6 @@ Object { "service.language.name": "javascript", }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -237,11 +227,6 @@ Object { "field": "transaction.marks.navigationTiming.fetchStart", }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -507,11 +492,6 @@ Object { "field": "transaction.marks.navigationTiming.fetchStart", }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -554,11 +534,6 @@ Object { "transaction.type": "page-load", }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -676,11 +651,6 @@ Object { "field": "transaction.marks.navigationTiming.fetchStart", }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -727,11 +697,6 @@ Object { "field": "transaction.marks.navigationTiming.fetchStart", }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts index 9626019347e5b..ec96b5225d617 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts @@ -11,7 +11,7 @@ import { TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { TRANSACTION_PAGE_LOAD } from '../../../common/transaction_types'; export async function hasRumData({ setup }: { setup: Setup & SetupTimeRange }) { diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts similarity index 58% rename from x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts rename to x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts index e91c9b52deecf..aed361f13bd7d 100644 --- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts @@ -7,14 +7,10 @@ import { ESFilter } from '../../../../../../typings/elasticsearch'; import { UIFilters } from '../../../../typings/ui_filters'; -import { - localUIFilters, - localUIFilterNames, -} from '../../rum_client/ui_filters/local_ui_filters/config'; -import { esKuery } from '../../../../../../../src/plugins/data/server'; +import { localUIFilters, localUIFilterNames } from './local_ui_filters/config'; export function getEsFilter(uiFilters: UIFilters) { - const { kuery, environment, ...localFilterValues } = uiFilters; + const localFilterValues = uiFilters; const mappedFilters = localUIFilterNames .filter((name) => name in localFilterValues) .map((filterName) => { @@ -27,16 +23,5 @@ export function getEsFilter(uiFilters: UIFilters) { }; }) as ESFilter[]; - const esFilters = [...getKueryUiFilterES(uiFilters.kuery), ...mappedFilters]; - - return esFilters; -} - -function getKueryUiFilterES(kuery?: string) { - if (!kuery) { - return []; - } - - const ast = esKuery.fromKueryExpression(kuery); - return [esKuery.toElasticsearchQuery(ast) as ESFilter]; + return mappedFilters; } diff --git a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/__snapshots__/index.test.ts.snap index 40504cec36a63..9742d89587841 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/__snapshots__/index.test.ts.snap @@ -44,11 +44,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/get_local_filter_query.ts b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/get_local_filter_query.ts index 8ea635467d0a1..a904bfe337a6b 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/get_local_filter_query.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/get_local_filter_query.ts @@ -9,7 +9,7 @@ import { omit } from 'lodash'; import { mergeProjection } from '../../../../projections/util/merge_projection'; import { Projection } from '../../../../projections/typings'; import { UIFilters } from '../../../../../typings/ui_filters'; -import { getEsFilter } from '../../../helpers/convert_ui_filters/get_es_filter'; +import { getEsFilter } from '../get_es_filter'; import { localUIFilters } from './config'; import { LocalUIFilterName } from '../../../../../common/ui_filter'; diff --git a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/index.test.ts b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/index.test.ts index 7254bb25cc5fe..e2ccc43374db7 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/index.test.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/local_ui_filters/index.test.ts @@ -16,7 +16,7 @@ describe('getLocalUIFilters', () => { let mock: SearchParamsMock; beforeEach(() => { - jest.mock('../../../helpers/convert_ui_filters/get_es_filter', () => { + jest.mock('../get_es_filter', () => { return []; }); }); diff --git a/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts index 259a0e6daea6f..64de74fb9023b 100644 --- a/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { ProcessorEvent } from '../../../common/processor_event'; import { TRACE_ID } from '../../../common/elasticsearch_fieldnames'; import { diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts index ab221e30ea489..f08cc27b2e59c 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts @@ -17,7 +17,7 @@ import { TRANSACTION_PAGE_LOAD, TRANSACTION_REQUEST, } from '../../../common/transaction_types'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { withApmSpan } from '../../utils/with_apm_span'; import { getMlJobsWithAPMGroup } from '../anomaly_detection/get_ml_jobs_with_apm_group'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index 1aee1bb5b242a..e5b0b72b8784a 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -15,7 +15,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { getServicesProjection } from '../../projections/services'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { environmentQuery } from '../../../common/utils/queries'; +import { environmentQuery } from '../../../server/utils/queries'; import { withApmSpan } from '../../utils/with_apm_span'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { @@ -91,7 +91,7 @@ async function getServicesData(options: IEnvOptions) { const { environment, setup, searchAggregatedTransactions } = options; const projection = getServicesProjection({ - setup: { ...setup, esFilter: [] }, + setup, searchAggregatedTransactions, }); diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts index b161345e729d3..d812275d7103b 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts @@ -19,7 +19,6 @@ describe('getServiceMapServiceNodeInfo', () => { hits: { total: { value: 0 } }, }), }, - esFilter: [], indices: {}, uiFilters: {}, } as unknown) as Setup & SetupTimeRange; diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts index 367fbc6810a7f..a6e7832bf697d 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts @@ -19,7 +19,7 @@ import { TRANSACTION_PAGE_LOAD, TRANSACTION_REQUEST, } from '../../../common/transaction_types'; -import { environmentQuery, rangeQuery } from '../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../server/utils/queries'; import { withApmSpan } from '../../utils/with_apm_span'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts index e8dcb28baa9a3..2b949863bcb30 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts @@ -16,7 +16,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; import { SERVICE_MAP_TIMEOUT_ERROR } from '../../../common/service_map'; -import { environmentQuery, rangeQuery } from '../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../server/utils/queries'; import { withApmSpan } from '../../utils/with_apm_span'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap index e6d702cc03c0b..8e47b7298cc33 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap @@ -49,11 +49,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -117,11 +112,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -186,11 +176,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts index a22c732a5e8ce..07b7e532d8055 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts +++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts @@ -18,16 +18,18 @@ import { withApmSpan } from '../../utils/with_apm_span'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; const getServiceNodes = ({ + kuery, setup, serviceName, }: { + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; }) => { return withApmSpan('get_service_nodes', async () => { const { apmEventClient } = setup; - const projection = getServiceNodesProjection({ setup, serviceName }); + const projection = getServiceNodesProjection({ kuery, setup, serviceName }); const params = mergeProjection(projection, { body: { diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index 0521ff7d9554d..dec5be8da32f4 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -191,11 +191,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts index 25c42f403da2e..efe9608edb95d 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts @@ -12,7 +12,7 @@ import { SERVICE_NAME, SERVICE_VERSION, } from '../../../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; import { withApmSpan } from '../../../utils/with_apm_span'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts index 6c7cbc26ea653..87ee0e9830fce 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts @@ -7,7 +7,7 @@ import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { ElasticsearchClient, Logger } from 'kibana/server'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; import { unwrapEsResponse, WrappedElasticsearchClientError, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts index 3683a069342a9..a81c0b2fc2c44 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts @@ -10,7 +10,7 @@ import { AGENT_NAME, SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { withApmSpan } from '../../utils/with_apm_span'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts index 558d6ae22f00f..cb9d37d56b867 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts @@ -20,7 +20,7 @@ import { SPAN_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; import { joinByKey } from '../../../../common/utils/join_by_key'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { withApmSpan } from '../../../utils/with_apm_span'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts index dfbdfb3f504e8..c8642c6272b5f 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts @@ -14,7 +14,7 @@ import { SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { EventOutcome } from '../../../../common/event_outcome'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts index 3655fa513dfb4..e33044bff8ffa 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts @@ -11,12 +11,17 @@ import { TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { withApmSpan } from '../../../utils/with_apm_span'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; export async function getServiceErrorGroupComparisonStatistics({ + kuery, serviceName, setup, numBuckets, @@ -24,6 +29,7 @@ export async function getServiceErrorGroupComparisonStatistics({ groupIds, environment, }: { + kuery?: string; serviceName: string; setup: Setup & SetupTimeRange; numBuckets: number; @@ -34,7 +40,7 @@ export async function getServiceErrorGroupComparisonStatistics({ return withApmSpan( 'get_service_error_group_comparison_statistics', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const { intervalString } = getBucketSize({ start, end, numBuckets }); @@ -52,7 +58,7 @@ export async function getServiceErrorGroupComparisonStatistics({ { term: { [TRANSACTION_TYPE]: transactionType } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts index e6c1c5db8f2ca..13a6069876369 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts @@ -14,24 +14,30 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { withApmSpan } from '../../../utils/with_apm_span'; import { getErrorName } from '../../helpers/get_error_name'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; export function getServiceErrorGroupPrimaryStatistics({ + kuery, serviceName, setup, transactionType, environment, }: { + kuery?: string; serviceName: string; setup: Setup & SetupTimeRange; transactionType: string; environment?: string; }) { return withApmSpan('get_service_error_group_primary_statistics', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const response = await apmEventClient.search({ apm: { @@ -46,7 +52,7 @@ export function getServiceErrorGroupPrimaryStatistics({ { term: { [TRANSACTION_TYPE]: transactionType } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts index a17fb6da2007f..676ba1625cc61 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts @@ -9,7 +9,11 @@ import { ValuesType } from 'utility-types'; import { orderBy } from 'lodash'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { ProcessorEvent } from '../../../../common/processor_event'; import { ERROR_EXC_MESSAGE, @@ -29,6 +33,7 @@ export type ServiceErrorGroupItem = ValuesType< export async function getServiceErrorGroups({ environment, + kuery, serviceName, setup, size, @@ -39,6 +44,7 @@ export async function getServiceErrorGroups({ transactionType, }: { environment?: string; + kuery?: string; serviceName: string; setup: Setup & SetupTimeRange; size: number; @@ -49,7 +55,7 @@ export async function getServiceErrorGroups({ transactionType: string; }) { return withApmSpan('get_service_error_groups', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const { intervalString } = getBucketSize({ start, end, numBuckets }); @@ -67,7 +73,7 @@ export async function getServiceErrorGroups({ { term: { [TRANSACTION_TYPE]: transactionType } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, @@ -150,7 +156,7 @@ export async function getServiceErrorGroups({ { term: { [TRANSACTION_TYPE]: transactionType } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts index ef90e5197229b..3e788ca8ddf83 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts @@ -6,7 +6,11 @@ */ import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { METRIC_CGROUP_MEMORY_USAGE_BYTES, @@ -27,13 +31,14 @@ import { withApmSpan } from '../../../utils/with_apm_span'; export async function getServiceInstanceSystemMetricStats({ environment, + kuery, setup, serviceName, size, numBuckets, }: ServiceInstanceParams) { return withApmSpan('get_service_instance_system_metric_stats', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const { intervalString } = getBucketSize({ start, end, numBuckets }); @@ -99,7 +104,7 @@ export async function getServiceInstanceSystemMetricStats({ { term: { [SERVICE_NAME]: serviceName } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], should: [cgroupMemoryFilter, systemMemoryFilter, cpuUsageFilter], minimum_should_match: 1, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts index 620fd9828bd37..94a5e54e9ace5 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts @@ -6,7 +6,11 @@ */ import { EventOutcome } from '../../../../common/event_outcome'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { EVENT_OUTCOME, @@ -29,6 +33,7 @@ import { export async function getServiceInstanceTransactionStats({ environment, + kuery, latencyAggregationType, setup, transactionType, @@ -38,7 +43,7 @@ export async function getServiceInstanceTransactionStats({ numBuckets, }: ServiceInstanceParams) { return withApmSpan('get_service_instance_transaction_stats', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const { intervalString, bucketSize } = getBucketSize({ start, @@ -78,7 +83,7 @@ export async function getServiceInstanceTransactionStats({ { term: { [TRANSACTION_TYPE]: transactionType } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts index 7c0124f4ce004..838753890a8cd 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts @@ -14,6 +14,7 @@ import { getServiceInstanceTransactionStats } from './get_service_instance_trans export interface ServiceInstanceParams { environment?: string; + kuery?: string; latencyAggregationType: LatencyAggregationType; setup: Setup & SetupTimeRange; serviceName: string; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts index 5c43191cf588c..a064d5b3008c2 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts @@ -21,7 +21,7 @@ import { SERVICE_VERSION, } from '../../../common/elasticsearch_fieldnames'; import { ContainerType } from '../../../common/service_metadata'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts b/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts index b342ffea02464..94da6545c5e90 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts @@ -16,7 +16,7 @@ import { HOST_OS_PLATFORM, } from '../../../common/elasticsearch_fieldnames'; import { ContainerType } from '../../../common/service_metadata'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts index 16753db416edd..8eaf9e96c7fd9 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts @@ -16,10 +16,12 @@ import { getServiceNodesProjection } from '../../projections/service_nodes'; import { withApmSpan } from '../../utils/with_apm_span'; export function getServiceNodeMetadata({ + kuery, serviceName, serviceNodeName, setup, }: { + kuery?: string; serviceName: string; serviceNodeName: string; setup: Setup & SetupTimeRange; @@ -29,6 +31,7 @@ export function getServiceNodeMetadata({ const query = mergeProjection( getServiceNodesProjection({ + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts index ce36db3e82bab..6875a41ad7d9f 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts @@ -14,7 +14,11 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { EventOutcome } from '../../../common/event_outcome'; import { LatencyAggregationType } from '../../../common/latency_aggregation_types'; -import { environmentQuery, rangeQuery } from '../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../server/utils/queries'; import { Coordinate } from '../../../typings/timeseries'; import { withApmSpan } from '../../utils/with_apm_span'; import { @@ -32,6 +36,7 @@ import { calculateTransactionErrorPercentage } from '../helpers/transaction_erro export async function getServiceTransactionGroupComparisonStatistics({ environment, + kuery, serviceName, transactionNames, setup, @@ -41,6 +46,7 @@ export async function getServiceTransactionGroupComparisonStatistics({ latencyAggregationType, }: { environment?: string; + kuery?: string; serviceName: string; transactionNames: string[]; setup: Setup & SetupTimeRange; @@ -62,7 +68,7 @@ export async function getServiceTransactionGroupComparisonStatistics({ return withApmSpan( 'get_service_transaction_group_comparison_statistics', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const { intervalString } = getBucketSize({ start, end, numBuckets }); const field = getTransactionDurationFieldForAggregatedTransactions( @@ -89,7 +95,7 @@ export async function getServiceTransactionGroupComparisonStatistics({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts index ddbfd617faf65..28574bab4df21 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts @@ -13,7 +13,11 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { EventOutcome } from '../../../common/event_outcome'; import { LatencyAggregationType } from '../../../common/latency_aggregation_types'; -import { environmentQuery, rangeQuery } from '../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../server/utils/queries'; import { withApmSpan } from '../../utils/with_apm_span'; import { getDocumentTypeFilterForAggregatedTransactions, @@ -37,6 +41,7 @@ export type ServiceOverviewTransactionGroupSortField = export async function getServiceTransactionGroups({ environment, + kuery, serviceName, setup, searchAggregatedTransactions, @@ -44,6 +49,7 @@ export async function getServiceTransactionGroups({ latencyAggregationType, }: { environment?: string; + kuery?: string; serviceName: string; setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; @@ -51,7 +57,7 @@ export async function getServiceTransactionGroups({ latencyAggregationType: LatencyAggregationType; }) { return withApmSpan('get_service_transaction_groups', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const field = getTransactionDurationFieldForAggregatedTransactions( searchAggregatedTransactions @@ -77,7 +83,7 @@ export async function getServiceTransactionGroups({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts index 3d77bf5bd6baf..e280ab6db1665 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts @@ -9,7 +9,7 @@ import { SERVICE_NAME, TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts index a3adca0d306aa..b42fd340bfb42 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { rangeQuery } from '../../../../common/utils/queries'; +import { rangeQuery } from '../../../../server/utils/queries'; import { ProcessorEvent } from '../../../../common/processor_event'; import { OBSERVER_VERSION_MAJOR } from '../../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts index e1f8bca83829c..5f0302035462c 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts @@ -15,7 +15,11 @@ import { TRANSACTION_PAGE_LOAD, TRANSACTION_REQUEST, } from '../../../../common/transaction_types'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; import { getDocumentTypeFilterForAggregatedTransactions, @@ -33,6 +37,7 @@ import { withApmSpan } from '../../../utils/with_apm_span'; interface AggregationParams { environment?: string; + kuery?: string; setup: ServicesItemsSetup; searchAggregatedTransactions: boolean; } @@ -41,11 +46,12 @@ const MAX_NUMBER_OF_SERVICES = 500; export async function getServiceTransactionStats({ environment, + kuery, setup, searchAggregatedTransactions, }: AggregationParams) { return withApmSpan('get_service_transaction_stats', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const outcomes = getOutcomeAggregation(); @@ -78,7 +84,7 @@ export async function getServiceTransactionStats({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index c2677af038486..1ddc7a6583c81 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -17,11 +17,13 @@ export type ServicesItemsSetup = Setup & SetupTimeRange; export async function getServicesItems({ environment, + kuery, setup, searchAggregatedTransactions, logger, }: { environment?: string; + kuery?: string; setup: ServicesItemsSetup; searchAggregatedTransactions: boolean; logger: Logger; @@ -29,7 +31,9 @@ export async function getServicesItems({ return withApmSpan('get_services_items', async () => { const params = { environment, + kuery, projection: getServicesProjection({ + kuery, setup, searchAggregatedTransactions, }), diff --git a/x-pack/plugins/apm/server/lib/services/get_services/index.ts b/x-pack/plugins/apm/server/lib/services/get_services/index.ts index 1a0ddeda11651..e76eb3c28fddb 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/index.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/index.ts @@ -15,11 +15,13 @@ import { hasHistoricalAgentData } from './has_historical_agent_data'; export async function getServices({ environment, + kuery, setup, searchAggregatedTransactions, logger, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; logger: Logger; @@ -28,6 +30,7 @@ export async function getServices({ const [items, hasLegacyData] = await Promise.all([ getServicesItems({ environment, + kuery, setup, searchAggregatedTransactions, logger, diff --git a/x-pack/plugins/apm/server/lib/services/get_throughput.ts b/x-pack/plugins/apm/server/lib/services/get_throughput.ts index f7cd23b0e37a7..490eec337840e 100644 --- a/x-pack/plugins/apm/server/lib/services/get_throughput.ts +++ b/x-pack/plugins/apm/server/lib/services/get_throughput.ts @@ -10,7 +10,11 @@ import { SERVICE_NAME, TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../server/utils/queries'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, @@ -21,6 +25,7 @@ import { withApmSpan } from '../../utils/with_apm_span'; interface Options { environment?: string; + kuery?: string; searchAggregatedTransactions: boolean; serviceName: string; setup: Setup; @@ -31,6 +36,7 @@ interface Options { function fetcher({ environment, + kuery, searchAggregatedTransactions, serviceName, setup, @@ -38,7 +44,7 @@ function fetcher({ start, end, }: Options) { - const { esFilter, apmEventClient } = setup; + const { apmEventClient } = setup; const { intervalString } = getBucketSize({ start, end }); const filter: ESFilter[] = [ { term: { [SERVICE_NAME]: serviceName } }, @@ -48,7 +54,7 @@ function fetcher({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; const params = { diff --git a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts index 0c9bbb35be631..8b60d39a8de5d 100644 --- a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts @@ -21,7 +21,11 @@ import { PROFILE_TOP_ID, SERVICE_NAME, } from '../../../../common/elasticsearch_fieldnames'; -import { rangeQuery, environmentQuery } from '../../../../common/utils/queries'; +import { + rangeQuery, + environmentQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { APMEventClient } from '../../helpers/create_es_client/create_apm_event_client'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { withApmSpan } from '../../../utils/with_apm_span'; @@ -184,12 +188,14 @@ function getProfilesWithStacks({ } export async function getServiceProfilingStatistics({ + kuery, serviceName, setup, environment, valueType, logger, }: { + kuery?: string; serviceName: string; setup: Setup & SetupTimeRange; environment?: string; @@ -202,11 +208,11 @@ export async function getServiceProfilingStatistics({ const valueTypeField = getValueTypeConfig(valueType).field; const filter: ESFilter[] = [ - ...rangeQuery(start, end), { term: { [SERVICE_NAME]: serviceName } }, - ...environmentQuery(environment), { exists: { field: valueTypeField } }, - ...setup.esFilter, + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), ]; const [profileStats, profileStacks] = await Promise.all([ diff --git a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts index dc29d6a43d82d..93fa029da8c72 100644 --- a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts +++ b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts @@ -5,7 +5,7 @@ * 2.0. */ import { mapKeys, mapValues } from 'lodash'; -import { rangeQuery, environmentQuery } from '../../../../common/utils/queries'; +import { rangeQuery, environmentQuery } from '../../../../server/utils/queries'; import { ProcessorEvent } from '../../../../common/processor_event'; import { PROFILE_ID, @@ -18,6 +18,7 @@ import { import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { withApmSpan } from '../../../utils/with_apm_span'; +import { kqlQuery } from '../../../utils/queries'; const configMap = mapValues( mapKeys(ProfilingValueType, (val, key) => val), @@ -27,16 +28,18 @@ const configMap = mapValues( const allFields = Object.values(configMap).map((config) => config.field); export async function getServiceProfilingTimeline({ + kuery, serviceName, environment, setup, }: { + kuery?: string; serviceName: string; setup: Setup & SetupTimeRange; environment?: string; }) { return withApmSpan('get_service_profiling_timeline', async () => { - const { apmEventClient, start, end, esFilter } = setup; + const { apmEventClient, start, end } = setup; const response = await apmEventClient.search({ apm: { @@ -50,7 +53,7 @@ export async function getServiceProfilingTimeline({ { term: { [SERVICE_NAME]: serviceName } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts index f631657f87276..0b158d9e57285 100644 --- a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts +++ b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts @@ -15,7 +15,7 @@ import { ERROR_LOG_LEVEL, } from '../../../common/elasticsearch_fieldnames'; import { APMError } from '../../../typings/es_schemas/ui/apm_error'; -import { rangeQuery } from '../../../common/utils/queries'; +import { rangeQuery } from '../../../server/utils/queries'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { PromiseValueType } from '../../../typings/common'; import { withApmSpan } from '../../utils/with_apm_span'; diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap index 7fb2bb2fcbeeb..34c2f39ca04c0 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap @@ -56,11 +56,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], "must_not": Array [ Object { @@ -123,11 +118,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], "must_not": Array [ Object { @@ -190,11 +180,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], "must_not": Array [ Object { @@ -262,11 +247,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -317,11 +297,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -372,11 +347,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, @@ -433,11 +403,6 @@ Array [ }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts index 09e5e358a1b7c..ce0b6cf2a64fe 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts @@ -28,6 +28,7 @@ import { interface TopTransactionOptions { environment?: string; + kuery?: string; type: 'top_transactions'; serviceName: string; transactionType: string; @@ -37,6 +38,7 @@ interface TopTransactionOptions { interface TopTraceOptions { environment?: string; + kuery?: string; type: 'top_traces'; transactionName?: string; searchAggregatedTransactions: boolean; diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts index d1a056002db07..627086df9d681 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts @@ -14,7 +14,11 @@ import { TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; import { EventOutcome } from '../../../common/event_outcome'; -import { environmentQuery, rangeQuery } from '../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../server/utils/queries'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, @@ -30,6 +34,7 @@ import { withApmSpan } from '../../utils/with_apm_span'; export async function getErrorRate({ environment, + kuery, serviceName, transactionType, transactionName, @@ -37,6 +42,7 @@ export async function getErrorRate({ searchAggregatedTransactions, }: { environment?: string; + kuery?: string; serviceName: string; transactionType?: string; transactionName?: string; @@ -48,7 +54,7 @@ export async function getErrorRate({ average: number | null; }> { return withApmSpan('get_transaction_group_error_rate', async () => { - const { start, end, esFilter, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const transactionNamefilter = transactionName ? [{ term: { [TRANSACTION_NAME]: transactionName } }] @@ -71,7 +77,7 @@ export async function getErrorRate({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; const outcomes = getOutcomeAggregation(); diff --git a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap index 62050563497e9..baa9b3ae230fe 100644 --- a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap @@ -159,11 +159,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "bool": Object { "minimum_should_match": 1, @@ -310,11 +305,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, Object { "bool": Object { "minimum_should_match": 1, @@ -387,11 +377,6 @@ Object { }, }, }, - Object { - "term": Object { - "service.environment": "test", - }, - }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts index 130029495af32..d8867c0dcc1e2 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts @@ -38,7 +38,6 @@ function getMockSetup(esResponse: any) { } ) as APMConfig, uiFilters: {}, - esFilter: [], indices: mockIndices, dynamicIndexPattern: null as any, }; diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts index f1e202df312c2..568769b52e2b4 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts @@ -18,7 +18,11 @@ import { TRANSACTION_BREAKDOWN_COUNT, } from '../../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { getMetricsDateHistogramParams } from '../../helpers/metrics'; import { MAX_KPIS } from './constants'; import { getVizColorForIndex } from '../../../../common/viz_colors'; @@ -26,19 +30,21 @@ import { withApmSpan } from '../../../utils/with_apm_span'; export function getTransactionBreakdown({ environment, + kuery, setup, serviceName, transactionName, transactionType, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; transactionName?: string; transactionType: string; }) { return withApmSpan('get_transaction_breakdown', async () => { - const { esFilter, apmEventClient, start, end, config } = setup; + const { apmEventClient, start, end, config } = setup; const subAggs = { sum_all_self_times: { @@ -86,7 +92,7 @@ export function getTransactionBreakdown({ { term: { [TRANSACTION_TYPE]: transactionType } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), { bool: { should: [ diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts index 7ed016cd4b4c6..fb7544e5fcb8d 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts @@ -20,7 +20,8 @@ import { joinByKey } from '../../../../../common/utils/join_by_key'; import { environmentQuery, rangeQuery, -} from '../../../../../common/utils/queries'; + kqlQuery, +} from '../../../../../server/utils/queries'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, @@ -50,6 +51,7 @@ function getHistogramAggOptions({ export async function getBuckets({ environment, + kuery, serviceName, transactionName, transactionType, @@ -61,6 +63,7 @@ export async function getBuckets({ searchAggregatedTransactions, }: { environment?: string; + kuery?: string; serviceName: string; transactionName: string; transactionType: string; @@ -74,7 +77,7 @@ export async function getBuckets({ return withApmSpan( 'get_latency_distribution_buckets_with_samples', async () => { - const { start, end, esFilter, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const commonFilters = [ { term: { [SERVICE_NAME]: serviceName } }, @@ -82,7 +85,7 @@ export async function getBuckets({ { term: { [TRANSACTION_NAME]: transactionName } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; async function getSamplesForDistributionBuckets() { diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts index f8061ea989469..2e86f6bb84c81 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts @@ -15,11 +15,16 @@ import { getProcessorEventForAggregatedTransactions, getTransactionDurationFieldForAggregatedTransactions, } from '../../helpers/aggregated_transactions'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { withApmSpan } from '../../../utils/with_apm_span'; export async function getDistributionMax({ environment, + kuery, serviceName, transactionName, transactionType, @@ -27,6 +32,7 @@ export async function getDistributionMax({ searchAggregatedTransactions, }: { environment?: string; + kuery?: string; serviceName: string; transactionName: string; transactionType: string; @@ -34,7 +40,7 @@ export async function getDistributionMax({ searchAggregatedTransactions: boolean; }) { return withApmSpan('get_latency_distribution_max', async () => { - const { start, end, esFilter, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const params = { apm: { @@ -54,7 +60,7 @@ export async function getDistributionMax({ { term: { [TRANSACTION_NAME]: transactionName } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts index 92d1d96b4a8e3..ef92ce6edcafe 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts @@ -20,6 +20,7 @@ function getBucketSize(max: number) { } export async function getTransactionDistribution({ + kuery, environment, serviceName, transactionName, @@ -30,6 +31,7 @@ export async function getTransactionDistribution({ searchAggregatedTransactions, }: { environment?: string; + kuery?: string; serviceName: string; transactionName: string; transactionType: string; @@ -41,6 +43,7 @@ export async function getTransactionDistribution({ return withApmSpan('get_transaction_latency_distribution', async () => { const distributionMax = await getDistributionMax({ environment, + kuery, serviceName, transactionName, transactionType, @@ -56,6 +59,7 @@ export async function getTransactionDistribution({ const { buckets, noHits } = await getBuckets({ environment, + kuery, serviceName, transactionName, transactionType, diff --git a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts index d566f3a169e78..cfd09f0207536 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts @@ -7,7 +7,7 @@ import { ESSearchResponse } from '../../../../../../typings/elasticsearch'; import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { rangeQuery } from '../../../../common/utils/queries'; +import { rangeQuery } from '../../../../server/utils/queries'; import { withApmSpan } from '../../../utils/with_apm_span'; import { Setup } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts index e1d3921d298c7..0be72c95b0a60 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts @@ -13,7 +13,11 @@ import { TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { LatencyAggregationType } from '../../../../common/latency_aggregation_types'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, @@ -32,6 +36,7 @@ export type LatencyChartsSearchResponse = PromiseReturnType< function searchLatency({ environment, + kuery, serviceName, transactionType, transactionName, @@ -40,6 +45,7 @@ function searchLatency({ latencyAggregationType, }: { environment?: string; + kuery?: string; serviceName: string; transactionType: string | undefined; transactionName: string | undefined; @@ -47,7 +53,7 @@ function searchLatency({ searchAggregatedTransactions: boolean; latencyAggregationType: LatencyAggregationType; }) { - const { esFilter, start, end, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const { intervalString } = getBucketSize({ start, end }); const filter: ESFilter[] = [ @@ -57,7 +63,7 @@ function searchLatency({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; if (transactionName) { @@ -106,6 +112,7 @@ function searchLatency({ export function getLatencyTimeseries({ environment, + kuery, serviceName, transactionType, transactionName, @@ -114,6 +121,7 @@ export function getLatencyTimeseries({ latencyAggregationType, }: { environment?: string; + kuery?: string; serviceName: string; transactionType: string | undefined; transactionName: string | undefined; @@ -124,6 +132,7 @@ export function getLatencyTimeseries({ return withApmSpan('get_latency_charts', async () => { const response = await searchLatency({ environment, + kuery, serviceName, transactionType, transactionName, diff --git a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts index ec5dbf0eab3e9..3b7ffafff0d2a 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts @@ -13,7 +13,11 @@ import { TRANSACTION_RESULT, TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../../../server/utils/queries'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, @@ -29,6 +33,7 @@ export type ThroughputChartsResponse = PromiseReturnType< function searchThroughput({ environment, + kuery, serviceName, transactionType, transactionName, @@ -37,6 +42,7 @@ function searchThroughput({ intervalString, }: { environment?: string; + kuery?: string; serviceName: string; transactionType: string; transactionName: string | undefined; @@ -44,7 +50,7 @@ function searchThroughput({ searchAggregatedTransactions: boolean; intervalString: string; }) { - const { esFilter, start, end, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const filter: ESFilter[] = [ { term: { [SERVICE_NAME]: serviceName } }, @@ -54,7 +60,7 @@ function searchThroughput({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; if (transactionName) { @@ -95,6 +101,7 @@ function searchThroughput({ export async function getThroughputCharts({ environment, + kuery, serviceName, transactionType, transactionName, @@ -102,6 +109,7 @@ export async function getThroughputCharts({ searchAggregatedTransactions, }: { environment?: string; + kuery?: string; serviceName: string; transactionType: string; transactionName: string | undefined; @@ -113,6 +121,7 @@ export async function getThroughputCharts({ const response = await searchThroughput({ environment, + kuery, serviceName, transactionType, transactionName, diff --git a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts index 38d6b593dc72d..a089850e427e6 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts @@ -9,7 +9,7 @@ import { TRACE_ID, TRANSACTION_ID, } from '../../../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../../../common/utils/queries'; +import { rangeQuery } from '../../../../server/utils/queries'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { ProcessorEvent } from '../../../../common/processor_event'; import { withApmSpan } from '../../../utils/with_apm_span'; diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts index 342d78608efbf..341c7d13936ba 100644 --- a/x-pack/plugins/apm/server/projections/errors.ts +++ b/x-pack/plugins/apm/server/projections/errors.ts @@ -10,19 +10,25 @@ import { SERVICE_NAME, ERROR_GROUP_ID, } from '../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../server/utils/queries'; import { ProcessorEvent } from '../../common/processor_event'; export function getErrorGroupsProjection({ environment, + kuery, setup, serviceName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; }) { - const { start, end, esFilter } = setup; + const { start, end } = setup; return { apm: { @@ -35,7 +41,7 @@ export function getErrorGroupsProjection({ { term: { [SERVICE_NAME]: serviceName } }, ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }, }, diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts index a32c2ae46c870..ca43d0a8fb3c8 100644 --- a/x-pack/plugins/apm/server/projections/metrics.ts +++ b/x-pack/plugins/apm/server/projections/metrics.ts @@ -10,7 +10,11 @@ import { SERVICE_NAME, SERVICE_NODE_NAME, } from '../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../server/utils/queries'; import { SERVICE_NODE_NAME_MISSING } from '../../common/service_nodes'; import { ProcessorEvent } from '../../common/processor_event'; @@ -28,23 +32,25 @@ function getServiceNodeNameFilters(serviceNodeName?: string) { export function getMetricsProjection({ environment, + kuery, setup, serviceName, serviceNodeName, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; }) { - const { start, end, esFilter } = setup; + const { start, end } = setup; const filter = [ { term: { [SERVICE_NAME]: serviceName } }, ...getServiceNodeNameFilters(serviceNodeName), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ]; return { diff --git a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts index 1d5f7316b69ad..9c6ea6bc83511 100644 --- a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts +++ b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts @@ -11,9 +11,10 @@ import { TRANSACTION_TYPE, SERVICE_LANGUAGE_NAME, } from '../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../common/utils/queries'; +import { rangeQuery } from '../../server/utils/queries'; import { ProcessorEvent } from '../../common/processor_event'; import { TRANSACTION_PAGE_LOAD } from '../../common/transaction_types'; +import { getEsFilter } from '../lib/rum_client/ui_filters/get_es_filter'; export function getRumPageLoadTransactionsProjection({ setup, @@ -24,7 +25,7 @@ export function getRumPageLoadTransactionsProjection({ urlQuery?: string; checkFetchStartFieldExists?: boolean; }) { - const { start, end, esFilter } = setup; + const { start, end, uiFilters } = setup; const bool = { filter: [ @@ -52,7 +53,7 @@ export function getRumPageLoadTransactionsProjection({ }, ] : []), - ...esFilter, + ...getEsFilter(uiFilters), ], }; @@ -75,7 +76,7 @@ export function getRumErrorsProjection({ setup: Setup & SetupTimeRange; urlQuery?: string; }) { - const { start, end, esFilter: esFilter } = setup; + const { start, end, uiFilters } = setup; const bool = { filter: [ @@ -86,7 +87,7 @@ export function getRumErrorsProjection({ [SERVICE_LANGUAGE_NAME]: 'javascript', }, }, - ...esFilter, + ...getEsFilter(uiFilters), ...(urlQuery ? [ { diff --git a/x-pack/plugins/apm/server/projections/service_nodes.ts b/x-pack/plugins/apm/server/projections/service_nodes.ts index 64e7406987fea..932309d9875c2 100644 --- a/x-pack/plugins/apm/server/projections/service_nodes.ts +++ b/x-pack/plugins/apm/server/projections/service_nodes.ts @@ -11,16 +11,19 @@ import { mergeProjection } from './util/merge_projection'; import { getMetricsProjection } from './metrics'; export function getServiceNodesProjection({ + kuery, setup, serviceName, serviceNodeName, }: { + kuery?: string; setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; }) { return mergeProjection( getMetricsProjection({ + kuery, setup, serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/projections/services.ts b/x-pack/plugins/apm/server/projections/services.ts index a9f5a7efd0e67..3509e4fa5b339 100644 --- a/x-pack/plugins/apm/server/projections/services.ts +++ b/x-pack/plugins/apm/server/projections/services.ts @@ -7,18 +7,20 @@ import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { SERVICE_NAME } from '../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../common/utils/queries'; +import { rangeQuery, kqlQuery } from '../../server/utils/queries'; import { ProcessorEvent } from '../../common/processor_event'; import { getProcessorEventForAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; export function getServicesProjection({ + kuery, setup, searchAggregatedTransactions, }: { + kuery?: string; setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { - const { start, end, esFilter } = setup; + const { start, end } = setup; return { apm: { @@ -34,7 +36,7 @@ export function getServicesProjection({ size: 0, query: { bool: { - filter: [...rangeQuery(start, end), ...esFilter], + filter: [...rangeQuery(start, end), ...kqlQuery(kuery)], }, }, aggs: { diff --git a/x-pack/plugins/apm/server/projections/transactions.ts b/x-pack/plugins/apm/server/projections/transactions.ts index 45ed5d2865a67..7955518d56f03 100644 --- a/x-pack/plugins/apm/server/projections/transactions.ts +++ b/x-pack/plugins/apm/server/projections/transactions.ts @@ -11,7 +11,11 @@ import { TRANSACTION_TYPE, TRANSACTION_NAME, } from '../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../common/utils/queries'; +import { + environmentQuery, + rangeQuery, + kqlQuery, +} from '../../server/utils/queries'; import { getProcessorEventForAggregatedTransactions, getDocumentTypeFilterForAggregatedTransactions, @@ -19,6 +23,7 @@ import { export function getTransactionsProjection({ environment, + kuery, setup, serviceName, transactionName, @@ -26,13 +31,14 @@ export function getTransactionsProjection({ searchAggregatedTransactions, }: { environment?: string; + kuery?: string; setup: Setup & SetupTimeRange; serviceName?: string; transactionName?: string; transactionType?: string; searchAggregatedTransactions: boolean; }) { - const { start, end, esFilter } = setup; + const { start, end } = setup; const transactionNameFilter = transactionName ? [{ term: { [TRANSACTION_NAME]: transactionName } }] @@ -54,7 +60,7 @@ export function getTransactionsProjection({ ), ...rangeQuery(start, end), ...environmentQuery(environment), - ...esFilter, + ...kqlQuery(kuery), ], }; diff --git a/x-pack/plugins/apm/server/routes/correlations.ts b/x-pack/plugins/apm/server/routes/correlations.ts index d4a0db3c0d6c7..48305d1a9df07 100644 --- a/x-pack/plugins/apm/server/routes/correlations.ts +++ b/x-pack/plugins/apm/server/routes/correlations.ts @@ -13,7 +13,7 @@ import { getCorrelationsForFailedTransactions } from '../lib/correlations/get_co import { getCorrelationsForSlowTransactions } from '../lib/correlations/get_correlations_for_slow_transactions'; import { setupRequest } from '../lib/helpers/setup_request'; import { createRoute } from './create_route'; -import { environmentRt, rangeRt } from './default_api_types'; +import { environmentRt, kueryRt, rangeRt } from './default_api_types'; const INVALID_LICENSE = i18n.translate( 'xpack.apm.significanTerms.license.text', @@ -36,8 +36,8 @@ export const correlationsForSlowTransactionsRoute = createRoute({ durationPercentile: t.string, fieldNames: t.string, }), - t.partial({ uiFilters: t.string }), environmentRt, + kueryRt, rangeRt, ]), }), @@ -49,6 +49,7 @@ export const correlationsForSlowTransactionsRoute = createRoute({ const setup = await setupRequest(context, request); const { environment, + kuery, serviceName, transactionType, transactionName, @@ -58,6 +59,7 @@ export const correlationsForSlowTransactionsRoute = createRoute({ return getCorrelationsForSlowTransactions({ environment, + kuery, serviceName, transactionType, transactionName, @@ -80,8 +82,8 @@ export const correlationsForFailedTransactionsRoute = createRoute({ t.type({ fieldNames: t.string, }), - t.partial({ uiFilters: t.string }), environmentRt, + kueryRt, rangeRt, ]), }), @@ -93,6 +95,7 @@ export const correlationsForFailedTransactionsRoute = createRoute({ const setup = await setupRequest(context, request); const { environment, + kuery, serviceName, transactionType, transactionName, @@ -101,6 +104,7 @@ export const correlationsForFailedTransactionsRoute = createRoute({ return getCorrelationsForFailedTransactions({ environment, + kuery, serviceName, transactionType, transactionName, diff --git a/x-pack/plugins/apm/server/routes/default_api_types.ts b/x-pack/plugins/apm/server/routes/default_api_types.ts index 990b462a520d2..10c50a384c2d7 100644 --- a/x-pack/plugins/apm/server/routes/default_api_types.ts +++ b/x-pack/plugins/apm/server/routes/default_api_types.ts @@ -20,4 +20,4 @@ export const comparisonRangeRt = t.partial({ export const environmentRt = t.partial({ environment: t.string }); -export const uiFiltersRt = t.type({ uiFilters: t.string }); +export const kueryRt = t.partial({ kuery: t.string }); diff --git a/x-pack/plugins/apm/server/routes/errors.ts b/x-pack/plugins/apm/server/routes/errors.ts index 073a91bfe1548..710e614165aa5 100644 --- a/x-pack/plugins/apm/server/routes/errors.ts +++ b/x-pack/plugins/apm/server/routes/errors.ts @@ -11,7 +11,7 @@ import { getErrorDistribution } from '../lib/errors/distribution/get_distributio import { getErrorGroupSample } from '../lib/errors/get_error_group_sample'; import { getErrorGroups } from '../lib/errors/get_error_groups'; import { setupRequest } from '../lib/helpers/setup_request'; -import { environmentRt, uiFiltersRt, rangeRt } from './default_api_types'; +import { environmentRt, kueryRt, rangeRt } from './default_api_types'; export const errorsRoute = createRoute({ endpoint: 'GET /api/apm/services/{serviceName}/errors', @@ -25,7 +25,7 @@ export const errorsRoute = createRoute({ sortDirection: t.union([t.literal('asc'), t.literal('desc')]), }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -34,10 +34,11 @@ export const errorsRoute = createRoute({ const setup = await setupRequest(context, request); const { params } = context; const { serviceName } = params.path; - const { environment, sortField, sortDirection } = params.query; + const { environment, kuery, sortField, sortDirection } = params.query; return getErrorGroups({ environment, + kuery, serviceName, sortField, sortDirection, @@ -53,15 +54,21 @@ export const errorGroupsRoute = createRoute({ serviceName: t.string, groupId: t.string, }), - query: t.intersection([environmentRt, uiFiltersRt, rangeRt]), + query: t.intersection([environmentRt, kueryRt, rangeRt]), }), options: { tags: ['access:apm'] }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { serviceName, groupId } = context.params.path; - const { environment } = context.params.query; + const { environment, kuery } = context.params.query; - return getErrorGroupSample({ environment, serviceName, groupId, setup }); + return getErrorGroupSample({ + environment, + groupId, + kuery, + serviceName, + setup, + }); }, }); @@ -76,7 +83,7 @@ export const errorDistributionRoute = createRoute({ groupId: t.string, }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -85,7 +92,13 @@ export const errorDistributionRoute = createRoute({ const setup = await setupRequest(context, request); const { params } = context; const { serviceName } = params.path; - const { environment, groupId } = params.query; - return getErrorDistribution({ environment, serviceName, groupId, setup }); + const { environment, kuery, groupId } = params.query; + return getErrorDistribution({ + environment, + kuery, + serviceName, + groupId, + setup, + }); }, }); diff --git a/x-pack/plugins/apm/server/routes/metrics.ts b/x-pack/plugins/apm/server/routes/metrics.ts index 08376ed0e37ff..c7e82e13d07b8 100644 --- a/x-pack/plugins/apm/server/routes/metrics.ts +++ b/x-pack/plugins/apm/server/routes/metrics.ts @@ -9,10 +9,10 @@ import * as t from 'io-ts'; import { setupRequest } from '../lib/helpers/setup_request'; import { getMetricsChartDataByAgent } from '../lib/metrics/get_metrics_chart_data_by_agent'; import { createRoute } from './create_route'; -import { environmentRt, uiFiltersRt, rangeRt } from './default_api_types'; +import { environmentRt, kueryRt, rangeRt } from './default_api_types'; export const metricsChartsRoute = createRoute({ - endpoint: `GET /api/apm/services/{serviceName}/metrics/charts`, + endpoint: 'GET /api/apm/services/{serviceName}/metrics/charts', params: t.type({ path: t.type({ serviceName: t.string, @@ -25,7 +25,7 @@ export const metricsChartsRoute = createRoute({ serviceNodeName: t.string, }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -34,9 +34,10 @@ export const metricsChartsRoute = createRoute({ const setup = await setupRequest(context, request); const { params } = context; const { serviceName } = params.path; - const { agentName, environment, serviceNodeName } = params.query; + const { agentName, environment, kuery, serviceNodeName } = params.query; return await getMetricsChartDataByAgent({ environment, + kuery, setup, serviceName, agentName, diff --git a/x-pack/plugins/apm/server/routes/rum_client.ts b/x-pack/plugins/apm/server/routes/rum_client.ts index c9fa4253bb58e..ecf56e2aec246 100644 --- a/x-pack/plugins/apm/server/routes/rum_client.ts +++ b/x-pack/plugins/apm/server/routes/rum_client.ts @@ -6,10 +6,8 @@ */ import * as t from 'io-ts'; -import { omit } from 'lodash'; import { jsonRt } from '../../common/runtime_types/json_rt'; import { LocalUIFilterName } from '../../common/ui_filter'; -import { getEsFilter } from '../lib/helpers/convert_ui_filters/get_es_filter'; import { Setup, setupRequest, @@ -31,7 +29,7 @@ import { localUIFilterNames } from '../lib/rum_client/ui_filters/local_ui_filter import { getRumPageLoadTransactionsProjection } from '../projections/rum_page_load_transactions'; import { Projection } from '../projections/typings'; import { createRoute } from './create_route'; -import { rangeRt, uiFiltersRt } from './default_api_types'; +import { rangeRt } from './default_api_types'; import { APMRequestHandlerContext } from './typings'; export const percentileRangeRt = t.partial({ @@ -39,6 +37,8 @@ export const percentileRangeRt = t.partial({ maxPercentile: t.string, }); +const uiFiltersRt = t.type({ uiFilters: t.string }); + const uxQueryRt = t.intersection([ uiFiltersRt, rangeRt, @@ -319,10 +319,7 @@ function createLocalFiltersRoute< const projection = await getProjection({ query, context, - setup: { - ...setup, - esFilter: getEsFilter(omit(uiFilters, filterNames)), - }, + setup, }); return getLocalUIFilters({ diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts index 6a05431c5677a..33943d6e05d01 100644 --- a/x-pack/plugins/apm/server/routes/service_map.ts +++ b/x-pack/plugins/apm/server/routes/service_map.ts @@ -62,7 +62,7 @@ export const serviceMapRoute = createRoute({ }); export const serviceMapServiceNodeRoute = createRoute({ - endpoint: `GET /api/apm/service-map/service/{serviceName}`, + endpoint: 'GET /api/apm/service-map/service/{serviceName}', params: t.type({ path: t.type({ serviceName: t.string, diff --git a/x-pack/plugins/apm/server/routes/service_nodes.ts b/x-pack/plugins/apm/server/routes/service_nodes.ts index 9523c89a639c4..e65b0b679da5a 100644 --- a/x-pack/plugins/apm/server/routes/service_nodes.ts +++ b/x-pack/plugins/apm/server/routes/service_nodes.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { createRoute } from './create_route'; import { setupRequest } from '../lib/helpers/setup_request'; import { getServiceNodes } from '../lib/service_nodes'; -import { rangeRt, uiFiltersRt } from './default_api_types'; +import { rangeRt, kueryRt } from './default_api_types'; export const serviceNodesRoute = createRoute({ endpoint: 'GET /api/apm/services/{serviceName}/serviceNodes', @@ -17,15 +17,17 @@ export const serviceNodesRoute = createRoute({ path: t.type({ serviceName: t.string, }), - query: t.intersection([rangeRt, uiFiltersRt]), + query: t.intersection([kueryRt, rangeRt]), }), options: { tags: ['access:apm'] }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { params } = context; const { serviceName } = params.path; + const { kuery } = params.query; return getServiceNodes({ + kuery, setup, serviceName, }); diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index 86f7853647894..58e6f6ccadc0a 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -30,8 +30,8 @@ import { jsonRt } from '../../common/runtime_types/json_rt'; import { comparisonRangeRt, environmentRt, + kueryRt, rangeRt, - uiFiltersRt, } from './default_api_types'; import { withApmSpan } from '../utils/with_apm_span'; import { getServiceProfilingStatistics } from '../lib/services/profiling/get_service_profiling_statistics'; @@ -45,18 +45,19 @@ import { export const servicesRoute = createRoute({ endpoint: 'GET /api/apm/services', params: t.type({ - query: t.intersection([environmentRt, uiFiltersRt, rangeRt]), + query: t.intersection([environmentRt, kueryRt, rangeRt]), }), options: { tags: ['access:apm'] }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const { environment } = context.params.query; + const { environment, kuery } = context.params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions( setup ); const services = await getServices({ environment, + kuery, setup, searchAggregatedTransactions, logger: context.logger, @@ -166,13 +167,20 @@ export const serviceNodeMetadataRoute = createRoute({ serviceName: t.string, serviceNodeName: t.string, }), - query: t.intersection([uiFiltersRt, rangeRt]), + query: t.intersection([kueryRt, rangeRt]), }), options: { tags: ['access:apm'] }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { serviceName, serviceNodeName } = context.params.path; - return getServiceNodeMetadata({ setup, serviceName, serviceNodeName }); + const { kuery } = context.params.query; + + return getServiceNodeMetadata({ + kuery, + setup, + serviceName, + serviceNodeName, + }); }, }); @@ -182,7 +190,7 @@ export const serviceAnnotationsRoute = createRoute({ path: t.type({ serviceName: t.string, }), - query: t.intersection([rangeRt, environmentRt]), + query: t.intersection([environmentRt, rangeRt]), }), options: { tags: ['access:apm'] }, handler: async ({ context, request }) => { @@ -205,10 +213,10 @@ export const serviceAnnotationsRoute = createRoute({ ]); return getServiceAnnotations({ + environment, setup, searchAggregatedTransactions, serviceName, - environment, annotationsClient, client: context.core.elasticsearch.client.asCurrentUser, logger: context.logger, @@ -285,8 +293,8 @@ export const serviceErrorGroupsPrimaryStatisticsRoute = createRoute({ }), query: t.intersection([ environmentRt, + kueryRt, rangeRt, - uiFiltersRt, t.type({ transactionType: t.string, }), @@ -298,9 +306,10 @@ export const serviceErrorGroupsPrimaryStatisticsRoute = createRoute({ const { path: { serviceName }, - query: { transactionType, environment }, + query: { kuery, transactionType, environment }, } = context.params; return getServiceErrorGroupPrimaryStatistics({ + kuery, serviceName, setup, transactionType, @@ -318,8 +327,8 @@ export const serviceErrorGroupsComparisonStatisticsRoute = createRoute({ }), query: t.intersection([ environmentRt, + kueryRt, rangeRt, - uiFiltersRt, t.type({ numBuckets: toNumberRt, transactionType: t.string, @@ -333,11 +342,12 @@ export const serviceErrorGroupsComparisonStatisticsRoute = createRoute({ const { path: { serviceName }, - query: { environment, numBuckets, transactionType, groupIds }, + query: { environment, kuery, numBuckets, transactionType, groupIds }, } = context.params; return getServiceErrorGroupComparisonStatistics({ environment, + kuery, serviceName, setup, numBuckets, @@ -356,7 +366,7 @@ export const serviceThroughputRoute = createRoute({ query: t.intersection([ t.type({ transactionType: t.string }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, comparisonRangeRt, ]), @@ -367,6 +377,7 @@ export const serviceThroughputRoute = createRoute({ const { serviceName } = context.params.path; const { environment, + kuery, transactionType, comparisonStart, comparisonEnd, @@ -378,6 +389,8 @@ export const serviceThroughputRoute = createRoute({ const { start, end } = setup; const commonProps = { + environment, + kuery, searchAggregatedTransactions, serviceName, setup, @@ -387,14 +400,12 @@ export const serviceThroughputRoute = createRoute({ const [currentPeriod, previousPeriod] = await Promise.all([ getThroughput({ ...commonProps, - environment, start, end, }), comparisonStart && comparisonEnd ? getThroughput({ ...commonProps, - environment, start: comparisonStart, end: comparisonEnd, }).then((coordinates) => @@ -427,7 +438,7 @@ export const serviceInstancesRoute = createRoute({ numBuckets: toNumberRt, }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -435,7 +446,12 @@ export const serviceInstancesRoute = createRoute({ handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { serviceName } = context.params.path; - const { environment, transactionType, numBuckets } = context.params.query; + const { + environment, + kuery, + transactionType, + numBuckets, + } = context.params.query; const latencyAggregationType = (context.params.query .latencyAggregationType as unknown) as LatencyAggregationType; @@ -445,6 +461,7 @@ export const serviceInstancesRoute = createRoute({ return getServiceInstances({ environment, + kuery, latencyAggregationType, serviceName, setup, @@ -493,13 +510,7 @@ export const serviceProfilingTimelineRoute = createRoute({ path: t.type({ serviceName: t.string, }), - query: t.intersection([ - rangeRt, - uiFiltersRt, - t.partial({ - environment: t.string, - }), - ]), + query: t.intersection([environmentRt, kueryRt, rangeRt]), }), options: { tags: ['access:apm'], @@ -509,10 +520,11 @@ export const serviceProfilingTimelineRoute = createRoute({ const { path: { serviceName }, - query: { environment }, + query: { environment, kuery }, } = context.params; return getServiceProfilingTimeline({ + kuery, setup, serviceName, environment, @@ -527,11 +539,9 @@ export const serviceProfilingStatisticsRoute = createRoute({ serviceName: t.string, }), query: t.intersection([ + environmentRt, + kueryRt, rangeRt, - uiFiltersRt, - t.partial({ - environment: t.string, - }), t.type({ valueType: t.union([ t.literal(ProfilingValueType.wallTime), @@ -553,10 +563,11 @@ export const serviceProfilingStatisticsRoute = createRoute({ const { path: { serviceName }, - query: { environment, valueType }, + query: { environment, kuery, valueType }, } = context.params; return getServiceProfilingStatistics({ + kuery, serviceName, environment, valueType, diff --git a/x-pack/plugins/apm/server/routes/traces.ts b/x-pack/plugins/apm/server/routes/traces.ts index 5d3f99be7af34..6287ffbf0c751 100644 --- a/x-pack/plugins/apm/server/routes/traces.ts +++ b/x-pack/plugins/apm/server/routes/traces.ts @@ -10,25 +10,25 @@ import { setupRequest } from '../lib/helpers/setup_request'; import { getTrace } from '../lib/traces/get_trace'; import { getTransactionGroupList } from '../lib/transaction_groups'; import { createRoute } from './create_route'; -import { environmentRt, rangeRt, uiFiltersRt } from './default_api_types'; +import { environmentRt, kueryRt, rangeRt } from './default_api_types'; import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; import { getRootTransactionByTraceId } from '../lib/transactions/get_transaction_by_trace'; export const tracesRoute = createRoute({ endpoint: 'GET /api/apm/traces', params: t.type({ - query: t.intersection([environmentRt, rangeRt, uiFiltersRt]), + query: t.intersection([environmentRt, kueryRt, rangeRt]), }), options: { tags: ['access:apm'] }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const { environment } = context.params.query; + const { environment, kuery } = context.params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions( setup ); return getTransactionGroupList( - { environment, type: 'top_traces', searchAggregatedTransactions }, + { environment, kuery, type: 'top_traces', searchAggregatedTransactions }, setup ); }, diff --git a/x-pack/plugins/apm/server/routes/transactions.ts b/x-pack/plugins/apm/server/routes/transactions.ts index 960cc7f526424..330b4b4bdd12a 100644 --- a/x-pack/plugins/apm/server/routes/transactions.ts +++ b/x-pack/plugins/apm/server/routes/transactions.ts @@ -24,7 +24,7 @@ import { getThroughputCharts } from '../lib/transactions/get_throughput_charts'; import { getTransactionGroupList } from '../lib/transaction_groups'; import { getErrorRate } from '../lib/transaction_groups/get_error_rate'; import { createRoute } from './create_route'; -import { environmentRt, rangeRt, uiFiltersRt } from './default_api_types'; +import { environmentRt, kueryRt, rangeRt } from './default_api_types'; /** * Returns a list of transactions grouped by name @@ -39,7 +39,7 @@ export const transactionGroupsRoute = createRoute({ query: t.intersection([ t.type({ transactionType: t.string }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -47,7 +47,7 @@ export const transactionGroupsRoute = createRoute({ handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { serviceName } = context.params.path; - const { environment, transactionType } = context.params.query; + const { environment, kuery, transactionType } = context.params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions( setup @@ -56,6 +56,7 @@ export const transactionGroupsRoute = createRoute({ return getTransactionGroupList( { environment, + kuery, type: 'top_transactions', serviceName, transactionType, @@ -73,8 +74,8 @@ export const transactionGroupsPrimaryStatisticsRoute = createRoute({ path: t.type({ serviceName: t.string }), query: t.intersection([ environmentRt, + kueryRt, rangeRt, - uiFiltersRt, t.type({ transactionType: t.string, latencyAggregationType: latencyAggregationTypeRt, @@ -93,11 +94,12 @@ export const transactionGroupsPrimaryStatisticsRoute = createRoute({ const { path: { serviceName }, - query: { environment, latencyAggregationType, transactionType }, + query: { environment, kuery, latencyAggregationType, transactionType }, } = context.params; return getServiceTransactionGroups({ environment, + kuery, setup, serviceName, searchAggregatedTransactions, @@ -114,8 +116,8 @@ export const transactionGroupsComparisonStatisticsRoute = createRoute({ path: t.type({ serviceName: t.string }), query: t.intersection([ environmentRt, + kueryRt, rangeRt, - uiFiltersRt, t.type({ transactionNames: jsonRt.pipe(t.array(t.string)), numBuckets: toNumberRt, @@ -138,6 +140,7 @@ export const transactionGroupsComparisonStatisticsRoute = createRoute({ path: { serviceName }, query: { environment, + kuery, transactionNames, latencyAggregationType, numBuckets, @@ -147,6 +150,7 @@ export const transactionGroupsComparisonStatisticsRoute = createRoute({ return getServiceTransactionGroupComparisonStatistics({ environment, + kuery, setup, serviceName, transactionNames, @@ -173,7 +177,7 @@ export const transactionLatencyChartsRoute = createRoute({ latencyAggregationType: latencyAggregationTypeRt, }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -184,6 +188,7 @@ export const transactionLatencyChartsRoute = createRoute({ const { serviceName } = context.params.path; const { environment, + kuery, transactionType, transactionName, latencyAggregationType, @@ -195,6 +200,7 @@ export const transactionLatencyChartsRoute = createRoute({ const options = { environment, + kuery, serviceName, transactionType, transactionName, @@ -235,9 +241,9 @@ export const transactionThroughputChartsRoute = createRoute({ query: t.intersection([ t.type({ transactionType: t.string }), t.partial({ transactionName: t.string }), - uiFiltersRt, - rangeRt, environmentRt, + kueryRt, + rangeRt, ]), }), options: { tags: ['access:apm'] }, @@ -246,6 +252,7 @@ export const transactionThroughputChartsRoute = createRoute({ const { serviceName } = context.params.path; const { environment, + kuery, transactionType, transactionName, } = context.params.query; @@ -256,6 +263,7 @@ export const transactionThroughputChartsRoute = createRoute({ return await getThroughputCharts({ environment, + kuery, serviceName, transactionType, transactionName, @@ -282,7 +290,7 @@ export const transactionChartsDistributionRoute = createRoute({ traceId: t.string, }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -292,6 +300,7 @@ export const transactionChartsDistributionRoute = createRoute({ const { serviceName } = context.params.path; const { environment, + kuery, transactionType, transactionName, transactionId = '', @@ -304,6 +313,7 @@ export const transactionChartsDistributionRoute = createRoute({ return getTransactionDistribution({ environment, + kuery, serviceName, transactionType, transactionName, @@ -325,7 +335,7 @@ export const transactionChartsBreakdownRoute = createRoute({ t.type({ transactionType: t.string }), t.partial({ transactionName: t.string }), environmentRt, - uiFiltersRt, + kueryRt, rangeRt, ]), }), @@ -335,12 +345,14 @@ export const transactionChartsBreakdownRoute = createRoute({ const { serviceName } = context.params.path; const { environment, + kuery, transactionName, transactionType, } = context.params.query; return getTransactionBreakdown({ environment, + kuery, serviceName, transactionName, transactionType, @@ -358,7 +370,7 @@ export const transactionChartsErrorRateRoute = createRoute({ }), query: t.intersection([ environmentRt, - uiFiltersRt, + kueryRt, rangeRt, t.type({ transactionType: t.string }), t.partial({ transactionName: t.string }), @@ -369,7 +381,12 @@ export const transactionChartsErrorRateRoute = createRoute({ const setup = await setupRequest(context, request); const { params } = context; const { serviceName } = params.path; - const { environment, transactionType, transactionName } = params.query; + const { + environment, + kuery, + transactionType, + transactionName, + } = params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions( setup @@ -377,6 +394,7 @@ export const transactionChartsErrorRateRoute = createRoute({ return getErrorRate({ environment, + kuery, serviceName, transactionType, transactionName, diff --git a/x-pack/plugins/apm/common/utils/queries.test.ts b/x-pack/plugins/apm/server/utils/queries.test.ts similarity index 85% rename from x-pack/plugins/apm/common/utils/queries.test.ts rename to x-pack/plugins/apm/server/utils/queries.test.ts index 546c8627def69..9fa97940db8f3 100644 --- a/x-pack/plugins/apm/common/utils/queries.test.ts +++ b/x-pack/plugins/apm/server/utils/queries.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames'; -import { ENVIRONMENT_NOT_DEFINED } from '../environment_filter_values'; +import { SERVICE_ENVIRONMENT } from '../../common/elasticsearch_fieldnames'; +import { ENVIRONMENT_NOT_DEFINED } from '../../common/environment_filter_values'; import { environmentQuery } from './queries'; describe('environmentQuery', () => { diff --git a/x-pack/plugins/apm/common/utils/queries.ts b/x-pack/plugins/apm/server/utils/queries.ts similarity index 73% rename from x-pack/plugins/apm/common/utils/queries.ts rename to x-pack/plugins/apm/server/utils/queries.ts index dbbbf324b964a..6eab50d089821 100644 --- a/x-pack/plugins/apm/common/utils/queries.ts +++ b/x-pack/plugins/apm/server/utils/queries.ts @@ -5,12 +5,13 @@ * 2.0. */ +import { esKuery } from '../../../../../src/plugins/data/server'; import { ESFilter } from '../../../../typings/elasticsearch'; +import { SERVICE_ENVIRONMENT } from '../../common/elasticsearch_fieldnames'; import { - ENVIRONMENT_NOT_DEFINED, ENVIRONMENT_ALL, -} from '../environment_filter_values'; -import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames'; + ENVIRONMENT_NOT_DEFINED, +} from '../../common/environment_filter_values'; type QueryContainer = ESFilter; @@ -43,3 +44,12 @@ export function rangeQuery( }, ]; } + +export function kqlQuery(kql?: string) { + if (!kql) { + return []; + } + + const ast = esKuery.fromKueryExpression(kql); + return [esKuery.toElasticsearchQuery(ast) as ESFilter]; +} diff --git a/x-pack/plugins/apm/server/utils/test_helpers.tsx b/x-pack/plugins/apm/server/utils/test_helpers.tsx index 4df638cc2c5df..e804183c78867 100644 --- a/x-pack/plugins/apm/server/utils/test_helpers.tsx +++ b/x-pack/plugins/apm/server/utils/test_helpers.tsx @@ -8,7 +8,6 @@ import { APMConfig } from '../'; import { PromiseReturnType } from '../../../observability/typings/common'; import { - ESFilter, ESSearchRequest, ESSearchResponse, } from '../../../../typings/elasticsearch'; @@ -27,7 +26,6 @@ interface MockSetup { internalClient: any; config: APMConfig; uiFilters: UIFilters; - esFilter: ESFilter[]; indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': string; @@ -89,7 +87,6 @@ export async function inspectSearchParams( } ) as APMConfig, uiFilters: {}, - esFilter: [{ term: { 'service.environment': 'test' } }], indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': 'myIndex', diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.ts b/x-pack/test/apm_api_integration/tests/feature_controls.ts index 45114dd506716..e82b14d6cb7e6 100644 --- a/x-pack/test/apm_api_integration/tests/feature_controls.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.ts @@ -44,39 +44,39 @@ export default function featureControlsTests({ getService }: FtrProviderContext) { // this doubles as a smoke test for the _debug query parameter req: { - url: `/api/apm/services/foo/errors?start=${start}&end=${end}&uiFilters=%7B%7D&_debug=true`, + url: `/api/apm/services/foo/errors?start=${start}&end=${end}&_debug=true`, }, expectForbidden: expect403, expectResponse: expect200, }, { - req: { url: `/api/apm/services/foo/errors/bar?start=${start}&end=${end}&uiFilters=%7B%7D` }, + req: { url: `/api/apm/services/foo/errors/bar?start=${start}&end=${end}` }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&groupId=bar&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&groupId=bar`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/metrics/charts?start=${start}&end=${end}&agentName=cool-agent&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/metrics/charts?start=${start}&end=${end}&agentName=cool-agent`, }, expectForbidden: expect403, expectResponse: expect200, }, { - req: { url: `/api/apm/services?start=${start}&end=${end}&uiFilters=%7B%7D` }, + req: { url: `/api/apm/services?start=${start}&end=${end}` }, expectForbidden: expect403, expectResponse: expect200, }, @@ -91,7 +91,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) expectResponse: expect200, }, { - req: { url: `/api/apm/traces?start=${start}&end=${end}&uiFilters=%7B%7D` }, + req: { url: `/api/apm/traces?start=${start}&end=${end}` }, expectForbidden: expect403, expectResponse: expect200, }, @@ -102,42 +102,42 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }, { req: { - url: `/api/apm/services/foo/transactions/groups?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transactions/groups?start=${start}&end=${end}&transactionType=bar`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&transactionName=baz&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&transactionName=baz`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar&transactionName=baz`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/distribution?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transactions/charts/distribution?start=${start}&end=${end}&transactionType=bar&transactionName=baz`, }, expectForbidden: expect403, expectResponse: expect200, diff --git a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts index 78dc0fa44c5de..b767eaae1c203 100644 --- a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts +++ b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts @@ -27,14 +27,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('for opbeans-node', () => { const start = encodeURIComponent('2020-09-08T14:50:00.000Z'); const end = encodeURIComponent('2020-09-08T14:55:00.000Z'); - const uiFilters = encodeURIComponent(JSON.stringify({})); const agentName = 'nodejs'; describe('returns metrics data', () => { let chartsResponse: ChartResponse; before(async () => { chartsResponse = await supertest.get( - `/api/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` + `/api/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&agentName=${agentName}` ); }); it('contains CPU usage and System memory usage chart data', async () => { @@ -113,7 +112,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('for opbeans-java', () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); const agentName = 'java'; describe('returns metrics data', () => { @@ -123,7 +121,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let chartsResponse: ChartResponse; before(async () => { chartsResponse = await supertest.get( - `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` + `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}` ); }); @@ -412,7 +410,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const end = encodeURIComponent('2020-09-08T15:05:00.000Z'); const chartsResponse: ChartResponse = await supertest.get( - `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` + `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}` ); const systemMemoryUsageChart = chartsResponse.body.charts.find( diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instances.ts b/x-pack/test/apm_api_integration/tests/service_overview/instances.ts index cca40a6950007..8ff493b5575b0 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instances.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instances.ts @@ -40,7 +40,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { end, numBuckets: 20, transactionType: 'request', - uiFilters: '{}', }, }) ); @@ -69,7 +68,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { end, numBuckets: 20, transactionType: 'request', - uiFilters: '{}', }, }) ); @@ -153,7 +151,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { end, numBuckets: 20, transactionType: 'request', - uiFilters: '{}', }, }) ); diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts index a13a76e2ddb46..4a19efac5a809 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts @@ -39,7 +39,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', numBuckets: 20, transactionType: 'request', groupIds: JSON.stringify(groupIds), @@ -63,7 +62,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', numBuckets: 20, transactionType: 'request', groupIds: JSON.stringify(groupIds), @@ -94,7 +92,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', numBuckets: 20, transactionType: 'request', groupIds: JSON.stringify(['foo']), diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts index 8a334ca567f0e..61a44619ea905 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts @@ -32,7 +32,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', transactionType: 'request', }, }) @@ -58,7 +57,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', transactionType: 'request', environment: 'production', }, diff --git a/x-pack/test/apm_api_integration/tests/services/throughput.ts b/x-pack/test/apm_api_integration/tests/services/throughput.ts index 787436ea37b05..4f568aafe33cc 100644 --- a/x-pack/test/apm_api_integration/tests/services/throughput.ts +++ b/x-pack/test/apm_api_integration/tests/services/throughput.ts @@ -29,7 +29,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { `/api/apm/services/opbeans-java/throughput?${qs.stringify({ start: metadata.start, end: metadata.end, - uiFilters: encodeURIComponent('{}'), transactionType: 'request', })}` ); @@ -49,7 +48,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { `/api/apm/services/opbeans-java/throughput?${qs.stringify({ start: metadata.start, end: metadata.end, - uiFilters: encodeURIComponent('{}'), transactionType: 'request', })}` ); @@ -96,7 +94,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await supertest.get( `/api/apm/services/opbeans-java/throughput?${qs.stringify({ - uiFilters: encodeURIComponent('{}'), transactionType: 'request', start: moment(metadata.end).subtract(15, 'minutes').toISOString(), end: metadata.end, diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.ts b/x-pack/test/apm_api_integration/tests/services/top_services.ts index 3afaec653fcb3..3896bc1c6fabc 100644 --- a/x-pack/test/apm_api_integration/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/tests/services/top_services.ts @@ -25,16 +25,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { const start = encodeURIComponent(range.start); const end = encodeURIComponent(range.end); - const uiFilters = encodeURIComponent(JSON.stringify({})); - registry.when( 'APM Services Overview with a basic license when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); + const response = await supertest.get(`/api/apm/services?start=${start}&end=${end}`); expect(response.status).to.be(200); expect(response.body.hasHistoricalData).to.be(false); @@ -56,9 +52,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let sortedItems: typeof response.body.items; before(async () => { - response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); + response = await supertest.get(`/api/apm/services?start=${start}&end=${end}`); sortedItems = sortBy(response.body.items, 'serviceName'); }); @@ -273,9 +267,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }; before(async () => { - response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); + response = await supertest.get(`/api/apm/services?start=${start}&end=${end}`); }); it('the response is successful', () => { @@ -321,7 +313,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let response: PromiseReturnType; before(async () => { response = await supertestAsApmReadUserWithoutMlAccess.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` + `/api/apm/services?start=${start}&end=${end}` ); }); @@ -346,8 +338,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { let response: PromiseReturnType; before(async () => { response = await supertest.get( - `/api/apm/services?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&uiFilters=${encodeURIComponent( - `{"kuery":"service.name:opbeans-java"}` + `/api/apm/services?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&kuery=${encodeURIComponent( + 'service.name:opbeans-java' )}` ); }); diff --git a/x-pack/test/apm_api_integration/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts index fb19c74e05cf4..40f87be3dc402 100644 --- a/x-pack/test/apm_api_integration/tests/traces/top_traces.ts +++ b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts @@ -20,13 +20,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { // url parameters const start = encodeURIComponent(metadata.start); const end = encodeURIComponent(metadata.end); - const uiFilters = encodeURIComponent(JSON.stringify({})); registry.when('Top traces when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles empty state', async () => { - const response = await supertest.get( - `/api/apm/traces?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); + const response = await supertest.get(`/api/apm/traces?start=${start}&end=${end}`); expect(response.status).to.be(200); expect(response.body.items.length).to.be(0); @@ -40,9 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let response: any; before(async () => { - response = await supertest.get( - `/api/apm/traces?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); + response = await supertest.get(`/api/apm/traces?start=${start}&end=${end}`); }); it('returns the correct status code', async () => { diff --git a/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts index 90ade5bb9c8a9..8e29004deb4f3 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts @@ -20,12 +20,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { const end = encodeURIComponent(metadata.end); const transactionType = 'request'; const transactionName = 'GET /api'; - const uiFilters = encodeURIComponent(JSON.stringify({})); registry.when('Breakdown when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}` ); expect(response.status).to.be(200); expect(response.body).to.eql({ timeseries: [] }); @@ -35,7 +34,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('when data is loaded', { config: 'basic', archives: [archiveName] }, () => { it('returns the transaction breakdown for a service', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}` ); expect(response.status).to.be(200); @@ -43,7 +42,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns the transaction breakdown for a transaction group', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}&transactionName=${transactionName}` + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&transactionName=${transactionName}` ); expect(response.status).to.be(200); @@ -102,7 +101,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns the transaction breakdown sorted by name', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}` ); expect(response.status).to.be(200); diff --git a/x-pack/test/apm_api_integration/tests/transactions/distribution.ts b/x-pack/test/apm_api_integration/tests/transactions/distribution.ts index 56d5e217068a4..770fc90267680 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/distribution.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/distribution.ts @@ -21,7 +21,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { const url = `/api/apm/services/opbeans-java/transactions/charts/distribution?${qs.stringify({ start: metadata.start, end: metadata.end, - uiFilters: encodeURIComponent('{}'), transactionName: 'APIRestController#stats', transactionType: 'request', })}`; diff --git a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts index 73b40648b5377..2b94816466aa7 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts @@ -19,12 +19,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { // url parameters const { start, end } = archives_metadata[archiveName]; - const uiFilters = '{}'; const transactionType = 'request'; const url = format({ pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate', - query: { start, end, uiFilters, transactionType }, + query: { start, end, transactionType }, }); registry.when('Error rate when data is not loaded', { config: 'basic', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/tests/transactions/latency.ts index 523139717b309..4b9409ce6f16b 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/latency.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/latency.ts @@ -26,10 +26,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { 'Latency with a basic license when data is not loaded ', { config: 'basic', archives: [] }, () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); it('returns 400 when latencyAggregationType is not informed', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request` + `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request` ); expect(response.status).to.be(400); @@ -37,7 +36,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns 400 when transactionType is not informed', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg` + `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&latencyAggregationType=avg` ); expect(response.status).to.be(400); @@ -45,7 +44,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg&transactionType=request` + `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&latencyAggregationType=avg&transactionType=request` ); expect(response.status).to.be(200); @@ -62,12 +61,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let response: PromiseReturnType; - const uiFilters = encodeURIComponent(JSON.stringify({})); - describe('average latency type', () => { before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=avg` + `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request&latencyAggregationType=avg` ); }); @@ -81,7 +78,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('95th percentile latency type', () => { before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p95` + `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request&latencyAggregationType=p95` ); }); @@ -95,7 +92,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('99th percentile latency type', () => { before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p99` + `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request&latencyAggregationType=p99` ); }); @@ -117,10 +114,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { const transactionType = 'request'; describe('without an environment', () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` ); }); @@ -129,22 +125,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - describe('without uiFilters', () => { - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` - ); - }); - it('should return an error response', () => { - expect(response.status).to.eql(400); - }); - }); - describe('with environment selected', () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + `/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` ); }); @@ -168,10 +152,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('when not defined environment is selected', () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-python/transactions/charts/latency?environment=ENVIRONMENT_NOT_DEFINED&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + `/api/apm/services/opbeans-python/transactions/charts/latency?environment=ENVIRONMENT_NOT_DEFINED&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` ); }); @@ -194,10 +177,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('with all environments selected', () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + `/api/apm/services/opbeans-java/transactions/charts/latency?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` ); }); @@ -211,10 +193,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('with environment selected and empty kuery filter', () => { - const uiFilters = encodeURIComponent(JSON.stringify({ kuery: '' })); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + `/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` ); }); diff --git a/x-pack/test/apm_api_integration/tests/transactions/throughput.ts b/x-pack/test/apm_api_integration/tests/transactions/throughput.ts index 430392a32bfb8..5c2de185fdf79 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/throughput.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/throughput.ts @@ -20,7 +20,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { // url parameters const { start, end } = metadata; - const uiFilters = JSON.stringify({}); registry.when('Throughput when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { @@ -31,7 +30,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { environment: 'testing', start, end, - uiFilters, transactionType: 'request', }, }) @@ -57,7 +55,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { environment: 'testing', start, end, - uiFilters, transactionType: 'request', }, }) diff --git a/x-pack/test/apm_api_integration/tests/transactions/top_transaction_groups.ts b/x-pack/test/apm_api_integration/tests/transactions/top_transaction_groups.ts index 165853203429f..32effa64e60db 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/top_transaction_groups.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/top_transaction_groups.ts @@ -24,7 +24,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { // url parameters const start = encodeURIComponent(metadata.start); const end = encodeURIComponent(metadata.end); - const uiFilters = encodeURIComponent(JSON.stringify({})); const transactionType = 'request'; registry.when( @@ -33,7 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('handles empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/groups?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + `/api/apm/services/opbeans-node/transactions/groups?start=${start}&end=${end}&transactionType=${transactionType}` ); expect(response.status).to.be(200); @@ -51,7 +50,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let response: any; before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/groups?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + `/api/apm/services/opbeans-node/transactions/groups?start=${start}&end=${end}&transactionType=${transactionType}` ); }); diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_comparison_statistics.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_comparison_statistics.ts index 414e2189a63fe..fdc499594aad0 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_comparison_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_comparison_statistics.ts @@ -33,7 +33,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', numBuckets: 20, latencyAggregationType: 'avg', transactionType: 'request', @@ -59,7 +58,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', numBuckets: 20, transactionType: 'request', latencyAggregationType: 'avg', @@ -103,7 +101,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', numBuckets: 20, transactionType: 'request', latencyAggregationType: 'p99', @@ -141,7 +138,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', numBuckets: 20, transactionType: 'request', latencyAggregationType: 'avg', diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_primary_statistics.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_primary_statistics.ts index 7d8417bc5bf63..b6fd4054a351c 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_primary_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_primary_statistics.ts @@ -32,7 +32,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', latencyAggregationType: 'avg', transactionType: 'request', }, @@ -58,7 +57,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', transactionType: 'request', latencyAggregationType: 'avg', }, @@ -131,7 +129,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { start, end, - uiFilters: '{}', transactionType: 'request', latencyAggregationType: 'p99', }, From d847958fb30bc29edc4301f7720c1178686c21ac Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 23 Feb 2021 12:06:46 -0800 Subject: [PATCH 53/70] [ci] disable firefox scripts from security cypress job (#92483) Temporarily disabling security solution Firefox tests. Seem to be race conditions present in the Firefox run specifically causing flake. --- .ci/Jenkinsfile_security_cypress | 3 ++- vars/tasks.groovy | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/Jenkinsfile_security_cypress b/.ci/Jenkinsfile_security_cypress index 9202af60c1571..811af44d1ca56 100644 --- a/.ci/Jenkinsfile_security_cypress +++ b/.ci/Jenkinsfile_security_cypress @@ -18,7 +18,8 @@ kibanaPipeline(timeoutMinutes: 180) { workers.ci(name: job, size: 'l', ramDisk: true) { kibanaPipeline.bash('test/scripts/jenkins_xpack_build_kibana.sh', 'Build Default Distributable') kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress_chrome.sh')() - kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress_firefox.sh')() + // Temporarily disabled to figure out test flake + // kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress_firefox.sh')() } } } diff --git a/vars/tasks.groovy b/vars/tasks.groovy index f782ffdd8c5e6..a61035403dabd 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -127,7 +127,8 @@ def functionalXpack(Map params = [:]) { ]) { if (githubPr.isPr()) { task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypressChrome', './test/scripts/jenkins_security_solution_cypress_chrome.sh')) - task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypressFirefox', './test/scripts/jenkins_security_solution_cypress_firefox.sh')) + // Temporarily disabled to figure out test flake + // task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypressFirefox', './test/scripts/jenkins_security_solution_cypress_firefox.sh')) } } } From 535686607bc132ce697af731478b5dbc8079e569 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 23 Feb 2021 12:25:47 -0800 Subject: [PATCH 54/70] [build] Clean tsbuildinfo from distribution (#92115) Signed-off-by: Tyler Smalley --- src/dev/build/tasks/clean_tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/build/tasks/clean_tasks.ts b/src/dev/build/tasks/clean_tasks.ts index f555b36ef26c7..3051579d2e6f8 100644 --- a/src/dev/build/tasks/clean_tasks.ts +++ b/src/dev/build/tasks/clean_tasks.ts @@ -45,7 +45,7 @@ export const CleanTypescript: Task = { 'Deleted %d files', await scanDelete({ directory: build.resolvePath(), - regularExpressions: [/\.(ts|tsx|d\.ts)$/, /tsconfig.*\.json$/], + regularExpressions: [/\.(ts|tsx|d\.ts)$/, /tsconfig.*\.(json|tsbuildinfo)$/], }) ); }, From 9a72090ecff9ac3ef1c0efa00841301a63a7da5d Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Tue, 23 Feb 2021 16:29:16 -0500 Subject: [PATCH 55/70] Add ECS audit logging config settings to docker kibana vars (#92497) --- .../resources/base/bin/kibana-docker | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 1598f00354bf8..bac44f6b9c221 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -260,6 +260,20 @@ kibana_vars=( xpack.rollup.enabled xpack.searchprofiler.enabled xpack.security.audit.enabled + xpack.security.audit.appender.type + xpack.security.audit.appender.layout.type + xpack.security.audit.appender.layout.highlight + xpack.security.audit.appender.layout.pattern + xpack.security.audit.appender.legacyLoggingConfig + xpack.security.audit.appender.fileName + xpack.security.audit.appender.policy.type + xpack.security.audit.appender.policy.interval + xpack.security.audit.appender.policy.modulate + xpack.security.audit.appender.policy.size + xpack.security.audit.appender.strategy.type + xpack.security.audit.appender.strategy.max + xpack.security.audit.appender.strategy.pattern + xpack.security.audit.ignore_filters xpack.security.authc.oidc.realm xpack.security.authc.providers xpack.security.authc.saml.maxRedirectURLSize From 0c3d30ccedb60e629f3ce85a54238048277f7409 Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Tue, 23 Feb 2021 17:16:48 -0500 Subject: [PATCH 56/70] [Security Solution] [Detections] Adds integration test to ensure max_signals param is obeyed (#92489) --- .../security_and_spaces/tests/generating_signals.ts | 13 +++++++++++++ .../test/detection_engine_api_integration/utils.ts | 10 ++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index 5dd32600a7938..08fb9222e1789 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -70,6 +70,19 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOpen.hits.hits.length).greaterThan(0); }); + it('should abide by max_signals > 100', async () => { + const maxSignals = 500; + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['auditbeat-*']), + max_signals: maxSignals, + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, maxSignals, [id]); + const signalsOpen = await getSignalsByIds(supertest, [id], maxSignals); + expect(signalsOpen.hits.hits.length).equal(maxSignals); + }); + it('should have recorded the rule_id within the signal', async () => { const rule: QueryCreateSchema = { ...getRuleForSignalTesting(['auditbeat-*']), diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 684cbb6368ad9..7711338b44697 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -226,7 +226,8 @@ export const getQuerySignalsRuleId = (ruleIds: string[]) => ({ * created from that rule's regular id. * @param ruleIds The rule_id to search for signals */ -export const getQuerySignalsId = (ids: string[]) => ({ +export const getQuerySignalsId = (ids: string[], size = 10) => ({ + size, query: { terms: { 'signal.rule.id': ids, @@ -1009,7 +1010,7 @@ export const waitForSignalsToBePresent = async ( signalIds: string[] ): Promise => { await waitFor(async () => { - const signalsOpen = await getSignalsByIds(supertest, signalIds); + const signalsOpen = await getSignalsByIds(supertest, signalIds, numberOfSignals); return signalsOpen.hits.hits.length >= numberOfSignals; }, 'waitForSignalsToBePresent'); }; @@ -1043,7 +1044,8 @@ export const getSignalsByRuleIds = async ( */ export const getSignalsByIds = async ( supertest: SuperTest, - ids: string[] + ids: string[], + size?: number ): Promise< SearchResponse<{ signal: Signal; @@ -1053,7 +1055,7 @@ export const getSignalsByIds = async ( const { body: signalsOpen }: { body: SearchResponse<{ signal: Signal }> } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) .set('kbn-xsrf', 'true') - .send(getQuerySignalsId(ids)) + .send(getQuerySignalsId(ids, size)) .expect(200); return signalsOpen; }; From b1eac3cd8409ff9f47f05f3854694085b4282636 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Tue, 23 Feb 2021 14:19:47 -0800 Subject: [PATCH 57/70] [DOCS] Updates refresh text in index pattern doc (#92469) --- docs/management/index-patterns.asciidoc | 13 +++++-------- .../images/new-index-pattern.png | Bin 129062 -> 113824 bytes 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/management/index-patterns.asciidoc b/docs/management/index-patterns.asciidoc index 28dbacc628ce9..88dbf6ec8761f 100644 --- a/docs/management/index-patterns.asciidoc +++ b/docs/management/index-patterns.asciidoc @@ -139,6 +139,10 @@ which indicates the type of data the field contains in {es}, such as strings or boolean values. The field mapping also determines how you can use the field, such as whether it can be searched or aggregated. +When a new field is added to the index, the index pattern field list is updated +the next time the index pattern is loaded, for example, when you load the page or +move between {kib} apps. + [role="screenshot"] image:management/index-patterns/images/new-index-pattern.png["Create index pattern"] @@ -158,7 +162,7 @@ date values in {es}, you can use a {kib} field formatter to change the display t <>, and <>. -To customize the displayed field name provided by {es}, you can +To customize the displayed field name provided by {es}, you can use *Custom Label* . A popularity counter keeps track of the fields you use most often. @@ -170,13 +174,6 @@ To edit the field display, click the edit icon [role="screenshot"] image:management/index-patterns/images/edit-field-format.png["Edit field format"] -[float] -==== Refresh the data fields - -To pick up newly-added fields, -refresh (image:management/index-patterns/images/refresh-icon.png[Refresh icon]) the index fields list. -This action also resets the {kib} popularity counters for the fields. - [float] [[default-index-pattern]] === Set the default index pattern diff --git a/docs/management/index-patterns/images/new-index-pattern.png b/docs/management/index-patterns/images/new-index-pattern.png index 1bd0344826994b0aa7fb84bb294cbb810dbf95ff..e0365a22b0b147b4b855ddbb519e62e3eae3e883 100644 GIT binary patch literal 113824 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
Ytq~IR>pzmayUefMqpTG!@T}_a(kaF}jvS z)d`2&NwLd}&%A>N{~?{rm0|Toc#5;U_Z$VRlHVU9?e-+sE4eunKa#)PAEQn?Z~_nx zM}}i`Bq~x7krimDH5vsr-P*En*lecjxszb95p85MIY`=^E)q#3lf>gh;0^nOcM9=_ zETM&pY7D$%bF#ek%ENR$lgMPP>?5wWy!r44ea=*L;NvnloyT(ef=KA-=#=T3S?@e| zlsclyncs2V>C6|)-7dgW&WzrFMbsW&4czP@Y@Mxz2-G|rp#OPX1OsVP%BJxQh1)w_ ztyIxAE*+13xItgUk~Rt-W9;?z@(w5s-OzCGM_R5QSL~sLOVUznS*)6#u+7NhcFXF( zL0)bWusT^WzT(TqW2qaGE6_-X&>&;rm|gI?ZMGtPEj0+4m*j|U8ci&-ct)qxyVz)> z)gC}0m8!Knq|`N1J88mfqtL$qG*{K=?fp^zyYn~i?+SX%ofZw zR_yodTAeOTsdYMgIlSJQO^4QbkJRq5nP#|L$LVP_o5>eHUIy1rmrK$Tcm!FvCwmt; zui8yYe(GvXKkQ-5bM&9S?tX8z_ELaYwXYJVc8hnkn#)|;@)(kr8iWT(QY-&;C7!>Z!l|EsPr_Mlc7_ROicEy z`F&M;p!|^jTuvw~nP&g;gaTg6Rfjmn-7aYAcc4c-}F~) zU+yq(p8-j1IhQMJiW3|r1Cuf?CAlj2dzf0o*?irmS6kQ4YKelg0_zLHhjO{9QAx6a z(OBkE{r&6JP(P*MhHUw2!&ypagUfZS>e@VWzu9zkO5lfLt|9xGH&Iu^10GxU(OItJ z{;=E``o-8n=>p5WvP*ucBVW^oc`vhn?OVKL!RP{Y1B8NhkTf2!H{EbTTegzyl=M{5 z3kHop&kM0~10{nhvpILp#iUhb0>Jb2O2mvy3Su1(&(+LRGSkqgS5plpb+U^a^!f&q z-<8^#jzlYlsnXNPPFbp*E;cLPUyBO}{RDJuH@{w zae2LZ<>f@5=jkoWwc08xyKw5RHmJ5=_&fmy#eCG=84d`*>I0!kGp%;V4e$9ajZrx4 zW|d_dZI7yy_3D9P@eG!xGqslGd({bf`jsbS&8!XPODh82Yz0(Ply%I21jzG+tLliO z?Ed_gRz2#}c8BNJhmmA+(!Iy2>70&4VyW)?)83SuHJe@upGjrPN}V_3Bl;cLI^=xB zWaD?0~a%&~8JLocxfz`!Z+IGDX#JFQGQwqWE$cx1U=Ca*)E$xQh z+3wcsR4k9(0My*Q*4LEm5K>x`p>u(gwJ{ zUkDv8-TvraMXpet%D4Li?Cs-Kvy87t-x&WXV*^OTbBhXYxTBr1lx}ujo)zqMLqAca zO4|UHQgapx_4bfDSS<=X?HD9xccN*tp|`hJ-*N~Y3R!mE@+XA1<*HM7g6M|vc#WXu zlj-`C%d38%`nakr?|VAtdV|Hv9bX8uTpt2e4+I@Xa(0m>)u?f8 zvN#-9y;SPj`GhHgW!dcxSwTI5u_EQWyg(P)HI+x?50u#0F%(1ua$)=7y24i!g~|;pw3=v)@m|59#G+2{@dVT#zjf^0o4&S( zQ15wg-&*J>p!M@Mh}kxWwZdUuBOZ}TE^;Yr&KTV#<56unq(lH=s)*I1LhZokVhMpY z7}r)rKBx0~=O7_qv6>>4Aq*K#2VQQtpWN@8@c;04IU(m??qe6=B;Z(=v?FaTR*G1t zM(VFQFQgT8u#5kAIkP4L5m0=;tD7<~k~9C-QN?9uG;iLfa%Do@029`Lt7BvN1>-3P zNn_fY0Ae+ahST|iVSFa*)4^_{7&R{N7D7*y+J1MT2lbBvO_vC0&*vVSMZ^Axx_
imvc$3VYt7vWeOLGIh*H>j8hPL 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

UPtEc(6f_EOWCf#lWl%m9 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

{ui$% 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 Date: Tue, 23 Feb 2021 14:55:42 -0800 Subject: [PATCH 58/70] [Security Solution][Exceptions] - Update exceptions modal to use existing lists plugin useApi hook (#92348) Doing a quick refactor to help with #90634 . While working on #90634 found that I would introduce a circular dependency if I didn't refactor the hook used by the exception builder component to make use of the existing useApi hook in the lists plugin. #90634 adds temporary ids to item entries to mitigate some React key requirements and the logic to remove/add these id's isn't at the boundary but in the hooks (so as not to pollute the data for everyone wanting to use the api. An upside is that it removed some of the looping and seemed to speed things up a bit. I briefly considered adding the bulk SO endpoints for exception items, but they are still experimental. --- .../public/exceptions/hooks/use_api.test.ts | 63 +++++++++++++++- .../lists/public/exceptions/hooks/use_api.ts | 39 +++++++++- .../exceptions/use_add_exception.tsx | 74 ++++++++----------- 3 files changed, 129 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts index 0fec2ab23994b..e61e74ca33236 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts @@ -7,13 +7,20 @@ import { act, renderHook } from '@testing-library/react-hooks'; +import { getUpdateExceptionListItemSchemaMock } from '../../../common/schemas/request/update_exception_list_item_schema.mock'; import { coreMock } from '../../../../../../src/core/public/mocks'; import * as api from '../api'; import { getExceptionListSchemaMock } from '../../../common/schemas/response/exception_list_schema.mock'; import { getFoundExceptionListItemSchemaMock } from '../../../common/schemas/response/found_exception_list_item_schema.mock'; import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock'; +import { getCreateExceptionListItemSchemaMock } from '../../../common/schemas/request/create_exception_list_item_schema.mock'; import { HttpStart } from '../../../../../../src/core/public'; -import { ApiCallByIdProps, ApiCallByListIdProps } from '../types'; +import { + AddExceptionListItemProps, + ApiCallByIdProps, + ApiCallByListIdProps, + UpdateExceptionListItemProps, +} from '../types'; import { ExceptionsApi, useApi } from './use_api'; @@ -366,4 +373,58 @@ describe('useApi', () => { expect(onErrorMock).toHaveBeenCalledWith(mockError); }); }); + + test('it invokes "addExceptionListItem" when "addExceptionListItem" used', async () => { + const payload = getExceptionListItemSchemaMock(); + const itemToCreate = getCreateExceptionListItemSchemaMock(); + const spyOnFetchExceptionListItemById = jest + .spyOn(api, 'addExceptionListItem') + .mockResolvedValue(payload); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useApi(mockKibanaHttpService) + ); + await waitForNextUpdate(); + + await result.current.addExceptionListItem({ + listItem: itemToCreate, + }); + + const expected: AddExceptionListItemProps = { + http: mockKibanaHttpService, + listItem: itemToCreate, + signal: new AbortController().signal, + }; + + expect(spyOnFetchExceptionListItemById).toHaveBeenCalledWith(expected); + }); + }); + + test('it invokes "updateExceptionListItem" when "getExceptionItem" used', async () => { + const payload = getExceptionListItemSchemaMock(); + const itemToUpdate = getUpdateExceptionListItemSchemaMock(); + const spyOnUpdateExceptionListItem = jest + .spyOn(api, 'updateExceptionListItem') + .mockResolvedValue(payload); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useApi(mockKibanaHttpService) + ); + await waitForNextUpdate(); + + await result.current.updateExceptionListItem({ + listItem: itemToUpdate, + }); + + const expected: UpdateExceptionListItemProps = { + http: mockKibanaHttpService, + listItem: itemToUpdate, + signal: new AbortController().signal, + }; + + expect(spyOnUpdateExceptionListItem).toHaveBeenCalledWith(expected); + }); + }); }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts index 91050c5fff795..b0c831ef3b857 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts @@ -9,11 +9,22 @@ import { useMemo } from 'react'; import * as Api from '../api'; import { HttpStart } from '../../../../../../src/core/public'; -import { ExceptionListItemSchema, ExceptionListSchema } from '../../../common/schemas'; +import { + CreateExceptionListItemSchema, + ExceptionListItemSchema, + ExceptionListSchema, + UpdateExceptionListItemSchema, +} from '../../../common/schemas'; import { ApiCallFindListsItemsMemoProps, ApiCallMemoProps, ApiListExportProps } from '../types'; import { getIdsAndNamespaces } from '../utils'; export interface ExceptionsApi { + addExceptionListItem: (arg: { + listItem: CreateExceptionListItemSchema; + }) => Promise; + updateExceptionListItem: (arg: { + listItem: UpdateExceptionListItemSchema; + }) => Promise; deleteExceptionItem: (arg: ApiCallMemoProps) => Promise; deleteExceptionList: (arg: ApiCallMemoProps) => Promise; getExceptionItem: ( @@ -29,6 +40,19 @@ export interface ExceptionsApi { export const useApi = (http: HttpStart): ExceptionsApi => { return useMemo( (): ExceptionsApi => ({ + async addExceptionListItem({ + listItem, + }: { + listItem: CreateExceptionListItemSchema; + }): Promise { + const abortCtrl = new AbortController(); + + return Api.addExceptionListItem({ + http, + listItem, + signal: abortCtrl.signal, + }); + }, async deleteExceptionItem({ id, namespaceType, @@ -184,6 +208,19 @@ export const useApi = (http: HttpStart): ExceptionsApi => { onError(error); } }, + async updateExceptionListItem({ + listItem, + }: { + listItem: UpdateExceptionListItemSchema; + }): Promise { + const abortCtrl = new AbortController(); + + return Api.updateExceptionListItem({ + http, + listItem, + signal: abortCtrl.signal, + }); + }, }), [http] ); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx index a8e6c72e3e165..614f5301c82e2 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx @@ -10,11 +10,9 @@ import { UpdateDocumentByQueryResponse } from 'elasticsearch'; import { HttpStart } from '../../../../../../../src/core/public'; import { - addExceptionListItem, - updateExceptionListItem, ExceptionListItemSchema, CreateExceptionListItemSchema, - UpdateExceptionListItemSchema, + useApi, } from '../../../lists_plugin_deps'; import { updateAlertStatus } from '../../../detections/containers/detection_engine/alerts/api'; import { getUpdateAlertsQuery } from '../../../detections/components/alerts_table/actions'; @@ -25,6 +23,7 @@ import { import { getQueryFilter } from '../../../../common/detection_engine/get_query_filter'; import { Index } from '../../../../common/detection_engine/schemas/common/schemas'; import { formatExceptionItemForUpdate, prepareExceptionItemsForBulkClose } from './helpers'; +import { useKibana } from '../../lib/kibana'; /** * Adds exception items to the list. Also optionally closes alerts. @@ -66,11 +65,13 @@ export const useAddOrUpdateException = ({ onError, onSuccess, }: UseAddOrUpdateExceptionProps): ReturnUseAddOrUpdateException => { + const { services } = useKibana(); const [isLoading, setIsLoading] = useState(false); const addOrUpdateExceptionRef = useRef(null); + const { addExceptionListItem, updateExceptionListItem } = useApi(services.http); const addOrUpdateException = useCallback( async (ruleId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex) => { - if (addOrUpdateExceptionRef.current !== null) { + if (addOrUpdateExceptionRef.current != null) { addOrUpdateExceptionRef.current( ruleId, exceptionItemsToAddOrUpdate, @@ -86,49 +87,33 @@ export const useAddOrUpdateException = ({ let isSubscribed = true; const abortCtrl = new AbortController(); - const addOrUpdateItems = async ( - exceptionItemsToAddOrUpdate: Array - ): Promise => { - const toAdd: CreateExceptionListItemSchema[] = []; - const toUpdate: UpdateExceptionListItemSchema[] = []; - exceptionItemsToAddOrUpdate.forEach( - (item: ExceptionListItemSchema | CreateExceptionListItemSchema) => { - if ('id' in item && item.id !== undefined) { - toUpdate.push(formatExceptionItemForUpdate(item)); - } else { - toAdd.push(item); - } - } - ); - - const promises: Array> = []; - toAdd.forEach((item: CreateExceptionListItemSchema) => { - promises.push( - addExceptionListItem({ - http, - listItem: item, - signal: abortCtrl.signal, - }) - ); - }); - toUpdate.forEach((item: UpdateExceptionListItemSchema) => { - promises.push( - updateExceptionListItem({ - http, - listItem: item, - signal: abortCtrl.signal, - }) - ); - }); - await Promise.all(promises); - }; - - const addOrUpdateExceptionItems: AddOrUpdateExceptionItemsFunc = async ( + const onUpdateExceptionItemsAndAlertStatus: AddOrUpdateExceptionItemsFunc = async ( ruleId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex ) => { + const addOrUpdateItems = async ( + exceptionListItems: Array + ): Promise => { + await Promise.all( + exceptionListItems.map( + (item: ExceptionListItemSchema | CreateExceptionListItemSchema) => { + if ('id' in item && item.id != null) { + const formattedExceptionItem = formatExceptionItemForUpdate(item); + return updateExceptionListItem({ + listItem: formattedExceptionItem, + }); + } else { + return addExceptionListItem({ + listItem: item, + }); + } + } + ) + ); + }; + try { setIsLoading(true); let alertIdResponse: UpdateDocumentByQueryResponse | undefined; @@ -170,7 +155,6 @@ export const useAddOrUpdateException = ({ const updated = (alertIdResponse?.updated ?? 0) + (bulkResponse?.updated ?? 0); const conflicts = alertIdResponse?.version_conflicts ?? 0 + (bulkResponse?.version_conflicts ?? 0); - if (isSubscribed) { setIsLoading(false); onSuccess(updated, conflicts); @@ -187,12 +171,12 @@ export const useAddOrUpdateException = ({ } }; - addOrUpdateExceptionRef.current = addOrUpdateExceptionItems; + addOrUpdateExceptionRef.current = onUpdateExceptionItemsAndAlertStatus; return (): void => { isSubscribed = false; abortCtrl.abort(); }; - }, [http, onSuccess, onError]); + }, [http, onSuccess, onError, updateExceptionListItem, addExceptionListItem]); return [{ isLoading }, addOrUpdateException]; }; From 561a26787690bfdece6c0309f6ba68bdab30b0bb Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 23 Feb 2021 15:12:22 -0800 Subject: [PATCH 59/70] [Alerts][Docs] Alert types doc update. Added refs to applications specific alerts groups. (#91787) * [Alerts][Docs] Alert types doc update. Added refs to applications specific alerts groups. * fixed ci * fixed ci * fixed ci * fixed ci * fixed alignment of pages * fixed addtional links * fixed titles * Apply suggestions from code review Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update docs/management/alerting/alert-management.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * renamed titles * moved maps to own folder * renamed titles * removed mistake file Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- .../alerting/alert-management.asciidoc | 2 +- docs/user/alerting/action-types.asciidoc | 2 +- docs/user/alerting/alert-types.asciidoc | 164 +++--------------- .../alerting-getting-started.asciidoc | 2 +- docs/user/alerting/index.asciidoc | 1 - .../geo-alert-types.asciidoc | 16 +- .../alerting/stack-alerts/es-query.asciidoc | 45 +++++ .../stack-alerts/index-threshold.asciidoc | 101 +++++++++++ 8 files changed, 177 insertions(+), 156 deletions(-) rename docs/user/alerting/{ => maps-alerts}/geo-alert-types.asciidoc (87%) create mode 100644 docs/user/alerting/stack-alerts/es-query.asciidoc create mode 100644 docs/user/alerting/stack-alerts/index-threshold.asciidoc diff --git a/docs/management/alerting/alert-management.asciidoc b/docs/management/alerting/alert-management.asciidoc index b4a5c3bc1931f..4fd1d8a7182db 100644 --- a/docs/management/alerting/alert-management.asciidoc +++ b/docs/management/alerting/alert-management.asciidoc @@ -3,7 +3,7 @@ === Managing Alerts -The *Alerts* tab provides a cross-app view of alerting. Different {kib} apps like <>, <>, <>, and <> can offer their own alerts, and the *Alerts* tab provides a central place to: +The *Alerts* tab provides a cross-app view of alerting. Different {kib} apps like {observability-guide}/create-alerts.html[*Observability*], {security-guide}/prebuilt-rules.html[*Security*], <> and <> can offer their own alerts. The *Alerts* tab provides a central place to: * <> alerts * <> including enabling/disabling, muting/unmuting, and deleting diff --git a/docs/user/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc index 599cce3a03cd9..586feeb032cb4 100644 --- a/docs/user/alerting/action-types.asciidoc +++ b/docs/user/alerting/action-types.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[action-types]] -== Action and connector types +== Actions and connectors Actions are Kibana services or integrations with third-party systems that run as background tasks on the Kibana server when alert conditions are met. {kib} provides the following types of actions: diff --git a/docs/user/alerting/alert-types.asciidoc b/docs/user/alerting/alert-types.asciidoc index 0877f067eee21..993d815c37f71 100644 --- a/docs/user/alerting/alert-types.asciidoc +++ b/docs/user/alerting/alert-types.asciidoc @@ -1,159 +1,35 @@ [role="xpack"] [[alert-types]] -== Standard stack alert types +== Alerts -{kib} supplies alert types in two ways: some are built into {kib} (these are known as stack alerts), while domain-specific alert types are registered by {kib} apps such as <>, <>, <>, and <>. +Kibana provides two types of alerts: -This section covers stack alerts. For domain-specific alert types, refer to the documentation for that app. -Users will need `all` access to the *Stack Alerts* feature to be able to create and edit any of the alerts listed below. -See <> for more information on configuring roles that provide access to this feature. - -Currently {kib} provides two stack alerts: <> and <>. - -[float] -[[alert-type-index-threshold]] -=== Index threshold - -The index threshold alert type is designed to run an {es} query over indices, aggregating field values from documents, comparing them to threshold values, and scheduling actions to run when the thresholds are met. - -[float] -==== Creating the alert - -An index threshold alert can be created from the *Create* button in the <>. Fill in the <>, then select *Index Threshold*. - -[role="screenshot"] -image::images/alert-types-index-threshold-select.png[Choosing an index threshold alert type] - -[float] -==== Defining the conditions - -The index threshold has 5 clauses that define the condition to detect. - -[role="screenshot"] -image::images/alert-types-index-threshold-conditions.png[Five clauses define the condition to detect] - -Index:: This clause requires an *index or index pattern* and a *time field* that will be used for the *time window*. -When:: This clause specifies how the value to be compared to the threshold is calculated. The value is calculated by aggregating a numeric field a the *time window*. The aggregation options are: `count`, `average`, `sum`, `min`, and `max`. When using `count` the document count is used, and an aggregation field is not necessary. -Over/Grouped Over:: This clause lets you configure whether the aggregation is applied over all documents, or should be split into groups using a grouping field. If grouping is used, an <> will be created for each group when it exceeds the threshold. To limit the number of instances on high cardinality fields, you must specify the number of groups to check against the threshold. Only the *top* groups are checked. -Threshold:: This clause defines a threshold value and a comparison operator (one of `is above`, `is above or equals`, `is below`, `is below or equals`, or `is between`). The result of the aggregation is compared to this threshold. -Time window:: This clause determines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be to a value higher than the *check every* value in the <>, to avoid gaps in detection. - -If data is available and all clauses have been defined, a preview chart will render the threshold value and display a line chart showing the value for the last 30 intervals. This can provide an indication of recent values and their proximity to the threshold, and help you tune the clauses. - -[role="screenshot"] -image::images/alert-types-index-threshold-preview.png[Five clauses define the condition to detect] - -[float] -=== Example - -In this section, you will use the {kib} <> to setup and tune the conditions on an index threshold alert. For this example, we want to detect when any of our top three sites have served more than 420,000 bytes over a 24 hour period. - -From the <>, create a new alert, and fill in the <>. This alert will be checked every 4 hours, and will not execute actions more than once per day. Choose the index threshold alert type. - -[role="screenshot"] -image::images/alert-types-index-threshold-select.png[Choosing an index threshold alert type] - -Click on each clause to open a control that helps you set the value: - -[float] -==== Index clause -The index clause control will list and allow you to search for available indices. Choose *kibana_sample_data_logs* - -[role="screenshot"] -image::images/alert-types-index-threshold-example-index.png[Choosing an index] - -Once an index is selected, the list of time fields for that index will be available to select. Choose *@timestamp*. - -[role="screenshot"] -image::images/alert-types-index-threshold-example-timefield.png[Choosing a time field] - -[float] -==== When clause - -We want to detect the number of bytes served during the time window, so we select `sum` as the aggregation, and `bytes` as the field to aggregate. - -[role="screenshot"] -image::images/alert-types-index-threshold-example-aggregation.png[Choosing the aggregation] - -[float] -==== Over/Grouped over clause - -We want to alert on the three sites that have the most traffic, so we'll group the sum of bytes by the `host.keyword` field and take the top 3 values. - -[role="screenshot"] -image::images/alert-types-index-threshold-example-grouping.png[Choosing the groups] - -[float] -==== Threshold clause - -We want to alert when any site exceeds 420,000 bytes over a 24 hour period, so we'll set the threshold to 420,000 and use the `is above` comparison. - -[role="screenshot"] -image::images/alert-types-index-threshold-example-threshold.png[Setting the threshold] - -[float] -==== Time window clause - -Finally, set the time window to 24 hours to complete the alert configuration. - -[role="screenshot"] -image::images/alert-types-index-threshold-example-window.png[Setting the time window] - -The preview chart will render showing the 24 hour sum of bytes at 4 hours intervals (the *check every* interval) for the past 120 hours (the last 30 intervals). - -[role="screenshot"] -image::images/alert-types-index-threshold-example-preview.png[Setting the time window] - -[float] -==== Comparing time windows - -You can interactively change the time window and observe the effect it has on the chart. Compare a 24 window to a 12 hour window. Notice the variability in the sum of bytes, due to different traffic levels during the day compared to at night. This variability would result in noisy alerts, so the 24 hour window is better. The preview chart can help you find the right values for your alert. - -[role="screenshot"] -image::images/alert-types-index-threshold-example-comparison.png[Comparing two time windows] - -[float] -[[alert-type-es-query]] -=== ES query - -The ES query alert type is designed to run a user-configured {es} query over indices, compare the number of matches to a configured threshold, and schedule -actions to run when the threshold condition is met. +* Stack alerts, which are built into {kib} +* Domain-specific alerts, which are registered by {kib} apps. [float] -==== Creating the alert +==== Standard stack alerts -An ES query alert can be created from the *Create* button in the <>. Fill in the <>, then select *ES query*. +Users require the `all` privilege to access to the *Stack Alerts* feature and create and edit alerts. . +See <> for more information. -[role="screenshot"] -image::images/alert-types-es-query-select.png[Choosing an ES query alert type] - -[float] -==== Defining the conditions +{kib} provides two stack alerts: -The ES query alert has 5 clauses that define the condition to detect. +* <> +* <> -[role="screenshot"] -image::images/alert-types-es-query-conditions.png[Four clauses define the condition to detect] - -Index:: This clause requires an *index or index pattern* and a *time field* that will be used for the *time window*. -Size:: This clause specifies the number of documents to pass to the configured actions when the the threshold condition is met. -ES query:: This clause specifies the ES DSL query to execute. The number of documents that match this query will be evaulated against the threshold -condition. Aggregations are not supported at this time. -Threshold:: This clause defines a threshold value and a comparison operator (`is above`, `is above or equals`, `is below`, `is below or equals`, or `is between`). The number of documents that match the specified query is compared to this threshold. -Time window:: This clause determines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be set to a value higher than the *check every* value in the <>, to avoid gaps in detection. [float] -==== Testing your query - -Use the *Test query* feature to verify that your query DSL is valid. - -When your query is valid:: Valid queries will be executed against the configured *index* using the configured *time window*. The number of documents that -match the query will be displayed. +==== Domain-specific alerts -[role="screenshot"] -image::images/alert-types-es-query-valid.png[Test ES query returns number of matches when valid] +For domain-specific alerts, refer to the documentation for that app. +{kib} supports these alerts: -When your query is invalid:: An error message is shown if the query is invalid. +* {observability-guide}/create-alerts.html[Observability alerts] +* {security-guide}/prebuilt-rules.html[Security alerts] +* <> +* <> -[role="screenshot"] -image::images/alert-types-es-query-invalid.png[Test ES query shows error when invalid] \ No newline at end of file +include::stack-alerts/index-threshold.asciidoc[] +include::stack-alerts/es-query.asciidoc[] +include::maps-alerts/geo-alert-types.asciidoc[] diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index 9b402e6cb9c49..0a7c17576de3d 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -5,7 +5,7 @@ -- -Alerting allows you to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with <>, <>, <>, <>, can be centrally managed from the <> UI, and provides a set of built-in <> and <> (known as stack alerts) for you to use. +Alerting allows you to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with {observability-guide}/create-alerts.html[*Observability*], {security-guide}/prebuilt-rules.html[*Security*], <> and <>, can be centrally managed from the <> UI, and provides a set of built-in <> and <> (known as stack alerts) for you to use. image::images/alerting-overview.png[Alerts and actions UI] diff --git a/docs/user/alerting/index.asciidoc b/docs/user/alerting/index.asciidoc index 25e87801f84af..caef0c6e7332d 100644 --- a/docs/user/alerting/index.asciidoc +++ b/docs/user/alerting/index.asciidoc @@ -2,5 +2,4 @@ include::alerting-getting-started.asciidoc[] include::defining-alerts.asciidoc[] include::action-types.asciidoc[] include::alert-types.asciidoc[] -include::geo-alert-types.asciidoc[] include::alerting-production-considerations.asciidoc[] diff --git a/docs/user/alerting/geo-alert-types.asciidoc b/docs/user/alerting/maps-alerts/geo-alert-types.asciidoc similarity index 87% rename from docs/user/alerting/geo-alert-types.asciidoc rename to docs/user/alerting/maps-alerts/geo-alert-types.asciidoc index d9073ecca1145..f899cd0204b9a 100644 --- a/docs/user/alerting/geo-alert-types.asciidoc +++ b/docs/user/alerting/maps-alerts/geo-alert-types.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[geo-alerting]] -== Geo alerting +=== Geo alerting Alerting now includes one additional stack alert: <>. @@ -9,7 +9,7 @@ to be able to create and edit a geo alert. See <> for more information on configuring roles that provide access to this feature. [float] -=== Geo alerting requirements +==== Geo alerting requirements To create a *Tracking containment* alert, the following requirements must be present: - *Tracks index or index pattern*: An index containing a `geo_point` field, `date` field, @@ -29,27 +29,27 @@ than the current time minus the amount of the interval. If data older than `now - ` is ingested, it won't trigger an alert. [float] -=== Creating a geo alert +==== Creating a geo alert Click the *Create* button in the <>. Complete the <>. [role="screenshot"] -image::images/alert-types-tracking-select.png[Choosing a tracking alert type] +image::user/alerting/images/alert-types-tracking-select.png[Choosing a tracking alert type] [float] [[alert-type-tracking-containment]] -=== Tracking containment +==== Tracking containment The Tracking containment alert type runs an {es} query over indices, determining if any documents are currently contained within any boundaries from the specified boundary index. In the event that an entity is contained within a boundary, an alert may be generated. [float] -==== Defining the conditions +===== Defining the conditions Tracking containment alerts have 3 clauses that define the condition to detect, as well as 2 Kuery bars used to provide additional filtering context for each of the indices. [role="screenshot"] -image::images/alert-types-tracking-containment-conditions.png[Five clauses define the condition to detect] +image::user/alerting/images/alert-types-tracking-containment-conditions.png[Five clauses define the condition to detect] Index (entity):: This clause requires an *index or index pattern*, a *time field* that will be used for the *time window*, and a *`geo_point` field* for tracking. When entity:: This clause specifies which crossing option to track. The values @@ -66,4 +66,4 @@ An alert can be triggered either when a containment condition is met or when an is no longer contained. [role="screenshot"] -image::images/alert-types-tracking-containment-action-options.png[Five clauses define the condition to detect] +image::user/alerting/images/alert-types-tracking-containment-action-options.png[Five clauses define the condition to detect] diff --git a/docs/user/alerting/stack-alerts/es-query.asciidoc b/docs/user/alerting/stack-alerts/es-query.asciidoc new file mode 100644 index 0000000000000..772178c9552da --- /dev/null +++ b/docs/user/alerting/stack-alerts/es-query.asciidoc @@ -0,0 +1,45 @@ +[role="xpack"] +[[alert-type-es-query]] +=== ES query + +The ES query alert type is designed to run a user-configured {es} query over indices, compare the number of matches to a configured threshold, and schedule +actions to run when the threshold condition is met. + +[float] +==== Creating the alert + +An ES query alert can be created from the *Create* button in the <>. Fill in the <>, then select *ES query*. + +[role="screenshot"] +image::user/alerting/images/alert-types-es-query-select.png[Choosing an ES query alert type] + +[float] +==== Defining the conditions + +The ES query alert has 5 clauses that define the condition to detect. + +[role="screenshot"] +image::user/alerting/images/alert-types-es-query-conditions.png[Four clauses define the condition to detect] + +Index:: This clause requires an *index or index pattern* and a *time field* that will be used for the *time window*. +Size:: This clause specifies the number of documents to pass to the configured actions when the the threshold condition is met. +ES query:: This clause specifies the ES DSL query to execute. The number of documents that match this query will be evaulated against the threshold +condition. Aggregations are not supported at this time. +Threshold:: This clause defines a threshold value and a comparison operator (`is above`, `is above or equals`, `is below`, `is below or equals`, or `is between`). The number of documents that match the specified query is compared to this threshold. +Time window:: This clause determines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be set to a value higher than the *check every* value in the <>, to avoid gaps in detection. + +[float] +==== Testing your query + +Use the *Test query* feature to verify that your query DSL is valid. + +When your query is valid:: Valid queries will be executed against the configured *index* using the configured *time window*. The number of documents that +match the query will be displayed. + +[role="screenshot"] +image::user/alerting/images/alert-types-es-query-valid.png[Test ES query returns number of matches when valid] + +When your query is invalid:: An error message is shown if the query is invalid. + +[role="screenshot"] +image::user/alerting/images/alert-types-es-query-invalid.png[Test ES query shows error when invalid] \ No newline at end of file diff --git a/docs/user/alerting/stack-alerts/index-threshold.asciidoc b/docs/user/alerting/stack-alerts/index-threshold.asciidoc new file mode 100644 index 0000000000000..424320aea3adc --- /dev/null +++ b/docs/user/alerting/stack-alerts/index-threshold.asciidoc @@ -0,0 +1,101 @@ +[role="xpack"] +[[alert-type-index-threshold]] +=== Index threshold + +The index threshold alert type is designed to run an {es} query over indices, aggregating field values from documents, comparing them to threshold values, and scheduling actions to run when the thresholds are met. + +[float] +==== Creating the alert + +An index threshold alert can be created from the *Create* button in the <>. Fill in the <>, then select *Index Threshold*. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-select.png[Choosing an index threshold alert type] + +[float] +==== Defining the conditions + +The index threshold has 5 clauses that define the condition to detect. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-conditions.png[Five clauses define the condition to detect] + +Index:: This clause requires an *index or index pattern* and a *time field* that will be used for the *time window*. +When:: This clause specifies how the value to be compared to the threshold is calculated. The value is calculated by aggregating a numeric field a the *time window*. The aggregation options are: `count`, `average`, `sum`, `min`, and `max`. When using `count` the document count is used, and an aggregation field is not necessary. +Over/Grouped Over:: This clause lets you configure whether the aggregation is applied over all documents, or should be split into groups using a grouping field. If grouping is used, an <> will be created for each group when it exceeds the threshold. To limit the number of instances on high cardinality fields, you must specify the number of groups to check against the threshold. Only the *top* groups are checked. +Threshold:: This clause defines a threshold value and a comparison operator (one of `is above`, `is above or equals`, `is below`, `is below or equals`, or `is between`). The result of the aggregation is compared to this threshold. +Time window:: This clause determines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be to a value higher than the *check every* value in the <>, to avoid gaps in detection. + +If data is available and all clauses have been defined, a preview chart will render the threshold value and display a line chart showing the value for the last 30 intervals. This can provide an indication of recent values and their proximity to the threshold, and help you tune the clauses. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-preview.png[Five clauses define the condition to detect] + +[float] +==== Example + +In this section, you will use the {kib} <> to setup and tune the conditions on an index threshold alert. For this example, we want to detect when any of our top three sites have served more than 420,000 bytes over a 24 hour period. + +From the <>, create a new alert, and fill in the <>. This alert will be checked every 4 hours, and will not execute actions more than once per day. Choose the index threshold alert type. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-select.png[Choosing an index threshold alert type] + +Click on each clause to open a control that helps you set the value: + +[float] +==== Index clause +The index clause control will list and allow you to search for available indices. Choose *kibana_sample_data_logs* + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-index.png[Choosing an index] + +Once an index is selected, the list of time fields for that index will be available to select. Choose *@timestamp*. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-timefield.png[Choosing a time field] + +[float] +==== When clause + +We want to detect the number of bytes served during the time window, so we select `sum` as the aggregation, and `bytes` as the field to aggregate. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-aggregation.png[Choosing the aggregation] + +[float] +==== Over/Grouped over clause + +We want to alert on the three sites that have the most traffic, so we'll group the sum of bytes by the `host.keyword` field and take the top 3 values. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-grouping.png[Choosing the groups] + +[float] +==== Threshold clause + +We want to alert when any site exceeds 420,000 bytes over a 24 hour period, so we'll set the threshold to 420,000 and use the `is above` comparison. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-threshold.png[Setting the threshold] + +[float] +==== Time window clause + +Finally, set the time window to 24 hours to complete the alert configuration. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-window.png[Setting the time window] + +The preview chart will render showing the 24 hour sum of bytes at 4 hours intervals (the *check every* interval) for the past 120 hours (the last 30 intervals). + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-preview.png[Setting the time window] + +[float] +==== Comparing time windows + +You can interactively change the time window and observe the effect it has on the chart. Compare a 24 window to a 12 hour window. Notice the variability in the sum of bytes, due to different traffic levels during the day compared to at night. This variability would result in noisy alerts, so the 24 hour window is better. The preview chart can help you find the right values for your alert. + +[role="screenshot"] +image::user/alerting/images/alert-types-index-threshold-example-comparison.png[Comparing two time windows] \ No newline at end of file From 4619f02189b5b1ac6dd16f40d9756077e5a0911a Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Tue, 23 Feb 2021 17:47:20 -0600 Subject: [PATCH 60/70] [Enterprise Search] Change icon for error in EuiCallouts (#92527) --- .../applications/shared/flash_messages/flash_messages.tsx | 2 +- .../shared/indexing_status/indexing_status_errors.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.tsx index 60d80487a2593..ef1a4a2d0be86 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.tsx @@ -17,7 +17,7 @@ const FLASH_MESSAGE_TYPES = { success: { color: 'success' as EuiCallOutProps['color'], icon: 'check' }, info: { color: 'primary' as EuiCallOutProps['color'], icon: 'iInCircle' }, warning: { color: 'warning' as EuiCallOutProps['color'], icon: 'alert' }, - error: { color: 'danger' as EuiCallOutProps['color'], icon: 'cross' }, + error: { color: 'danger' as EuiCallOutProps['color'], icon: 'alert' }, }; export const FlashMessages: React.FC = ({ children }) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx index 0e69547cbf6b5..0a4a0a36ce417 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx @@ -20,7 +20,7 @@ interface IIndexingStatusErrorsProps { export const IndexingStatusErrors: React.FC = ({ viewLinkPath }) => ( From 1f1f7037885bac7fb1c704a80a5e1b6f7aa0875f Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 23 Feb 2021 18:15:50 -0600 Subject: [PATCH 61/70] Revert "[build] Add task skips intended for partial builds (#92076)" This reverts commit 8fac02f8cbde0d2b83124f35f9a749f9e1a0d62c. --- src/dev/build/args.test.ts | 21 ------ src/dev/build/args.ts | 6 -- src/dev/build/build_distributables.ts | 69 ++++++++----------- .../tasks/os_packages/docker_generator/run.ts | 27 ++++---- 4 files changed, 41 insertions(+), 82 deletions(-) diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index e749af73241cf..2397c18c04d07 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -33,11 +33,8 @@ it('build default and oss dist for current platform, without packages, by defaul "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": false, - "createGenericFolders": true, - "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, - "initialize": true, "isRelease": false, "targetAllPlatforms": false, "versionQualifier": "", @@ -60,11 +57,8 @@ it('builds packages if --all-platforms is passed', () => { "createDockerCentOS": true, "createDockerContexts": true, "createDockerUBI": true, - "createGenericFolders": true, - "createPlatformFolders": true, "createRpmPackage": true, "downloadFreshNode": true, - "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -87,11 +81,8 @@ it('limits packages if --rpm passed with --all-platforms', () => { "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": false, - "createGenericFolders": true, - "createPlatformFolders": true, "createRpmPackage": true, "downloadFreshNode": true, - "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -114,11 +105,8 @@ it('limits packages if --deb passed with --all-platforms', () => { "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": false, - "createGenericFolders": true, - "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, - "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -142,11 +130,8 @@ it('limits packages if --docker passed with --all-platforms', () => { "createDockerCentOS": true, "createDockerContexts": true, "createDockerUBI": true, - "createGenericFolders": true, - "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, - "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -177,11 +162,8 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform "createDockerCentOS": true, "createDockerContexts": true, "createDockerUBI": false, - "createGenericFolders": true, - "createPlatformFolders": true, "createRpmPackage": false, "downloadFreshNode": true, - "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", @@ -205,11 +187,8 @@ it('limits packages if --all-platforms passed with --skip-docker-centos', () => "createDockerCentOS": false, "createDockerContexts": true, "createDockerUBI": true, - "createGenericFolders": true, - "createPlatformFolders": true, "createRpmPackage": true, "downloadFreshNode": true, - "initialize": true, "isRelease": false, "targetAllPlatforms": true, "versionQualifier": "", diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index bbfbd3e6f8813..c594eacd08c01 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -18,9 +18,6 @@ export function readCliArgs(argv: string[]) { 'oss', 'no-oss', 'skip-archives', - 'skip-initialize', - 'skip-generic-folders', - 'skip-platform-folders', 'skip-os-packages', 'rpm', 'deb', @@ -96,10 +93,7 @@ export function readCliArgs(argv: string[]) { versionQualifier: flags['version-qualifier'], buildOssDist: flags.oss !== false, buildDefaultDist: !flags.oss, - initialize: !Boolean(flags['skip-initialize']), downloadFreshNode: !Boolean(flags['skip-node-download']), - createGenericFolders: !Boolean(flags['skip-generic-folders']), - createPlatformFolders: !Boolean(flags['skip-platform-folders']), createArchives: !Boolean(flags['skip-archives']), createRpmPackage: isOsPackageDesired('rpm'), createDebPackage: isOsPackageDesired('deb'), diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index f0403fac1e26b..237fc71811a41 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -16,9 +16,6 @@ export interface BuildOptions { buildOssDist: boolean; buildDefaultDist: boolean; downloadFreshNode: boolean; - initialize: boolean; - createGenericFolders: boolean; - createPlatformFolders: boolean; createArchives: boolean; createRpmPackage: boolean; createDebPackage: boolean; @@ -44,53 +41,45 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions /** * verify, reset, and initialize the build environment */ - if (options.initialize) { - await run(Tasks.VerifyEnv); - await run(Tasks.Clean); - await run( - options.downloadFreshNode ? Tasks.DownloadNodeBuilds : Tasks.VerifyExistingNodeBuilds - ); - await run(Tasks.ExtractNodeBuilds); - } + await run(Tasks.VerifyEnv); + await run(Tasks.Clean); + await run(options.downloadFreshNode ? Tasks.DownloadNodeBuilds : Tasks.VerifyExistingNodeBuilds); + await run(Tasks.ExtractNodeBuilds); /** * run platform-generic build tasks */ - if (options.createGenericFolders) { - await run(Tasks.CopySource); - await run(Tasks.CopyBinScripts); - await run(Tasks.ReplaceFavicon); - await run(Tasks.CreateEmptyDirsAndFiles); - await run(Tasks.CreateReadme); - await run(Tasks.BuildBazelPackages); - await run(Tasks.BuildPackages); - await run(Tasks.BuildKibanaPlatformPlugins); - await run(Tasks.TranspileBabel); - await run(Tasks.CreatePackageJson); - await run(Tasks.InstallDependencies); - await run(Tasks.CleanPackages); - await run(Tasks.CreateNoticeFile); - await run(Tasks.UpdateLicenseFile); - await run(Tasks.RemovePackageJsonDeps); - await run(Tasks.CleanTypescript); - await run(Tasks.CleanExtraFilesFromModules); - await run(Tasks.CleanEmptyFolders); - } + await run(Tasks.CopySource); + await run(Tasks.CopyBinScripts); + await run(Tasks.ReplaceFavicon); + await run(Tasks.CreateEmptyDirsAndFiles); + await run(Tasks.CreateReadme); + await run(Tasks.BuildBazelPackages); + await run(Tasks.BuildPackages); + await run(Tasks.BuildKibanaPlatformPlugins); + await run(Tasks.TranspileBabel); + await run(Tasks.CreatePackageJson); + await run(Tasks.InstallDependencies); + await run(Tasks.CleanPackages); + await run(Tasks.CreateNoticeFile); + await run(Tasks.UpdateLicenseFile); + await run(Tasks.RemovePackageJsonDeps); + await run(Tasks.CleanTypescript); + await run(Tasks.CleanExtraFilesFromModules); + await run(Tasks.CleanEmptyFolders); /** * copy generic build outputs into platform-specific build * directories and perform platform/architecture-specific steps */ - if (options.createPlatformFolders) { - await run(Tasks.CreateArchivesSources); - await run(Tasks.PatchNativeModules); - await run(Tasks.InstallChromium); - await run(Tasks.CleanExtraBinScripts); - await run(Tasks.CleanNodeBuilds); + await run(Tasks.CreateArchivesSources); + await run(Tasks.PatchNativeModules); + await run(Tasks.InstallChromium); + await run(Tasks.CleanExtraBinScripts); + await run(Tasks.CleanNodeBuilds); - await run(Tasks.PathLength); - await run(Tasks.UuidVerification); - } + await run(Tasks.PathLength); + await run(Tasks.UuidVerification); /** * package platform-specific builds into archives diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index b8029328ac94a..21d2582f205f3 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -83,16 +83,6 @@ export async function runDockerGenerator( revision: config.getBuildSha(), }; - type HostArchitectureToDocker = Record; - const hostTarget: HostArchitectureToDocker = { - x64: 'x64', - arm64: 'aarch64', - }; - const buildArchitectureSupported = hostTarget[process.arch] === flags.architecture && flags.image; - if (!buildArchitectureSupported) { - return; - } - // Verify if we have the needed kibana target in order // to build the kibana docker image. // Also create the docker build target folder @@ -142,11 +132,18 @@ export async function runDockerGenerator( await chmodAsync(`${resolve(dockerBuildDir, 'build_docker.sh')}`, '755'); // Only build images on native targets - - await exec(log, `./build_docker.sh`, [], { - cwd: dockerBuildDir, - level: 'info', - }); + type HostArchitectureToDocker = Record; + const hostTarget: HostArchitectureToDocker = { + x64: 'x64', + arm64: 'aarch64', + }; + const buildImage = hostTarget[process.arch] === flags.architecture && flags.image; + if (buildImage) { + await exec(log, `./build_docker.sh`, [], { + cwd: dockerBuildDir, + level: 'info', + }); + } // Pack Dockerfiles and create a target for them if (flags.context) { From 048425daa0f027dacdb24b78319369ad7ef4b99b Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Tue, 23 Feb 2021 20:58:13 -0500 Subject: [PATCH 62/70] [Security Solution][Endpoint][Admin] Match Policy Details in Security Solution with Fleet (#92047) --- .../pages/policy/view/policy_details.tsx | 145 +++++++++++------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index e14f56881d673..bc89fe572b936 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState, useContext } from 'react'; +import styled, { ThemeContext } from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, @@ -15,7 +16,7 @@ import { EuiConfirmModal, EuiCallOut, EuiLoadingSpinner, - EuiHideFor, + EuiBottomBar, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -32,7 +33,6 @@ import { } from '../store/policy_details/selectors'; import { useKibana, toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; import { AgentsSummary } from './agents_summary'; -import { VerticalDivider } from './vertical_divider'; import { useToasts } from '../../../../common/lib/kibana'; import { AppAction } from '../../../../common/store/actions'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; @@ -46,6 +46,25 @@ import { WrapperPage } from '../../../../common/components/wrapper_page'; import { HeaderPage } from '../../../../common/components/header_page'; import { PolicyDetailsForm } from './policy_details_form'; +const maxFormWidth = '770px'; +const PolicyDetailsHeader = styled.div` + padding: ${(props) => props.theme.eui.paddingSizes.xl} 0; + background-color: #fafbfd; + border-bottom: 1px solid #d3dae6; + .siemHeaderPage { + max-width: ${maxFormWidth}; + margin: 0 auto; + } +`; + +const PolicyDetailsFormDiv = styled.div` + background-color: ${(props) => props.theme.eui.euiHeaderBackgroundColor}; + padding: ${(props) => props.theme.eui.paddingSizes.l} 0; + max-width: ${maxFormWidth}; + flex: 1; + align-self: center; +`; + export const PolicyDetails = React.memo(() => { const dispatch = useDispatch<(action: AppAction) => void>(); const { @@ -69,6 +88,7 @@ export const PolicyDetails = React.memo(() => { const [routeState, setRouteState] = useState(); const policyName = policyItem?.name ?? ''; const hostListRouterPath = getEndpointListPath({ name: 'endpointList' }); + const theme = useContext(ThemeContext); // Handle showing update statuses useEffect(() => { @@ -153,43 +173,12 @@ export const PolicyDetails = React.memo(() => { } const headerRightContent = ( - - - - - - - - - - - - - - - - - - - - + ); return ( @@ -201,24 +190,68 @@ export const PolicyDetails = React.memo(() => { onConfirm={handleSaveConfirmation} /> )} - - - {headerRightContent} - + + + + {headerRightContent} + + - + + + + + + + + + + + + + + + + + + From ae6a18da3266801d09c362fff6e56da8da359f32 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 24 Feb 2021 02:05:38 +0000 Subject: [PATCH 63/70] chore(NA): add missing files to exclude when building bazel prod packages (#92506) * chore(NA): add missing bazel files to exclude when building prod bazel packages * chore(NA): use globs to support all future run targets --- packages/kbn-pm/dist/index.js | 2 +- .../src/production/build_bazel_production_projects.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 1b8bd4784e583..51e8a617e2446 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -59868,7 +59868,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { // We want the package to have the same relative location within the build const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); - const bazelFilesToExclude = ['!*.params', '!*_mappings.json', '!*_options.optionsvalid.d.ts']; + const bazelFilesToExclude = ['!*.sh.runfiles*', '!*.params', '!*_mappings.json', '!*_options.optionsvalid.d.ts', '!*_loader.js', '!*_require_patch.js', '!*.sh']; await cpy__WEBPACK_IMPORTED_MODULE_0___default()(['**/*', '!node_modules/**', ...bazelFilesToExclude], buildProjectPath, { cwd: Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(kibanaRoot, 'bazel', 'bin', 'packages', Object(path__WEBPACK_IMPORTED_MODULE_2__["basename"])(buildProjectPath)), dot: true, diff --git a/packages/kbn-pm/src/production/build_bazel_production_projects.ts b/packages/kbn-pm/src/production/build_bazel_production_projects.ts index a54d6c753d8d7..1034253c5e0dc 100644 --- a/packages/kbn-pm/src/production/build_bazel_production_projects.ts +++ b/packages/kbn-pm/src/production/build_bazel_production_projects.ts @@ -60,8 +60,16 @@ async function copyToBuild(project: Project, kibanaRoot: string, buildRoot: stri // We want the package to have the same relative location within the build const relativeProjectPath = relative(kibanaRoot, project.path); const buildProjectPath = resolve(buildRoot, relativeProjectPath); + const bazelFilesToExclude = [ + '!*.sh.runfiles*', + '!*.params', + '!*_mappings.json', + '!*_options.optionsvalid.d.ts', + '!*_loader.js', + '!*_require_patch.js', + '!*.sh', + ]; - const bazelFilesToExclude = ['!*.params', '!*_mappings.json', '!*_options.optionsvalid.d.ts']; await copy(['**/*', '!node_modules/**', ...bazelFilesToExclude], buildProjectPath, { cwd: join(kibanaRoot, 'bazel', 'bin', 'packages', basename(buildProjectPath)), dot: true, From 69bf12773059f4f6eddb6e1a34fe9f9d42636600 Mon Sep 17 00:00:00 2001 From: ymao1 Date: Tue, 23 Feb 2021 21:29:41 -0500 Subject: [PATCH 64/70] [Actions][Doc] Clean up Actions README (#91789) * Removing REST API from README. Updating configuration docs * Updating action config docs * Cleaning up action type configs in README and user docs * Cleaning up action type configs in README and user docs * Fixing formatting * Apply suggestions from code review Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * PR fixes * Update x-pack/plugins/actions/README.md Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../user/alerting/action-types/email.asciidoc | 20 +- .../user/alerting/action-types/index.asciidoc | 4 +- docs/user/alerting/action-types/jira.asciidoc | 50 +- .../alerting/action-types/pagerduty.asciidoc | 23 +- .../alerting/action-types/resilient.asciidoc | 33 +- .../alerting/action-types/server-log.asciidoc | 1 + .../alerting/action-types/servicenow.asciidoc | 37 +- .../user/alerting/action-types/slack.asciidoc | 14 +- .../user/alerting/action-types/teams.asciidoc | 14 +- .../alerting/action-types/webhook.asciidoc | 17 +- docs/user/alerting/defining-alerts.asciidoc | 5 +- x-pack/plugins/actions/README.md | 561 ++---------------- 12 files changed, 204 insertions(+), 575 deletions(-) diff --git a/docs/user/alerting/action-types/email.asciidoc b/docs/user/alerting/action-types/email.asciidoc index 8a5d8d156245e..0c238da1b39e3 100644 --- a/docs/user/alerting/action-types/email.asciidoc +++ b/docs/user/alerting/action-types/email.asciidoc @@ -13,12 +13,12 @@ NOTE: For emails to have a footer with a link back to {kib}, set the <"` format. See the https://nodemailer.com/message/addresses/[Nodemailer address documentation] for more information. Host:: Host name of the service provider. If you are using the <> setting, make sure this hostname is added to the allowed hosts. Port:: The port to connect to on the service provider. Secure:: If true, the connection will use TLS when connecting to the service provider. Refer to the https://nodemailer.com/smtp/#tls-options[Nodemailer TLS documentation] for more information. If not true, the connection will initially connect over TCP, then attempt to switch to TLS via the SMTP STARTTLS command. -Username:: username for 'login' type authentication. -Password:: password for 'login' type authentication. +User:: Username for login type authentication. +Password:: Password for login type authentication. [float] [[Preconfigured-email-configuration]] @@ -40,11 +40,14 @@ Password:: password for 'login' type authentication. -- [[email-connector-config-properties]] -`config` defines the action type specific to the configuration and contains the following properties: +**`config`** defines the action type specific to the configuration and contains the following properties: [cols="2*<"] |=== +| `service` +| The name of a https://nodemailer.com/smtp/well-known/[well-known email service provider]. If `service` is provided, `host`, `port`, and `secure` properties are ignored. For more information on the `gmail` service value, see the (https://nodemailer.com/usage/using-gmail/)[Nodemailer Gmail documentation]. + | `from` | An email address that corresponds to *Sender*. @@ -57,18 +60,21 @@ Password:: password for 'login' type authentication. | `secure` | A boolean that corresponds to *Secure*. +| `hasAuth` +| If `true`, this connector will require values for `user` and `password` inside the secrets configuration. Defaults to `true`. + |=== -`secrets` defines sensitive information for the action type: +**`secrets`** defines sensitive information for the action type and contains the following properties: [cols="2*<"] |=== | `user` -| A string that corresponds to *User*. +| A string that corresponds to *User*. Required if `hasAuth` is set to `true`. | `password` -| A string that corresponds to *Password*. Should be stored in the <>. +| A string that corresponds to *Password*. Should be stored in the <>. Required if `hasAuth` is set to `true`. |=== diff --git a/docs/user/alerting/action-types/index.asciidoc b/docs/user/alerting/action-types/index.asciidoc index 2f459edea28f1..7ecc1cdb4f74e 100644 --- a/docs/user/alerting/action-types/index.asciidoc +++ b/docs/user/alerting/action-types/index.asciidoc @@ -31,7 +31,7 @@ Execution time field:: This field will be automatically set to the time the ale -- [[index-connector-config-properties]] -`config` defines the action type specific to the configuration and contains the following properties: +**`config`** defines the action type specific to the configuration and contains the following properties: [cols="2*<"] |=== @@ -40,7 +40,7 @@ Execution time field:: This field will be automatically set to the time the ale | A string that corresponds to *Index*. |`refresh` -| A boolean that corresponds to *Refresh*. +| A boolean that corresponds to *Refresh*. Defaults to `false`. |`executionTimeField` | A string that corresponds to *Execution time field*. diff --git a/docs/user/alerting/action-types/jira.asciidoc b/docs/user/alerting/action-types/jira.asciidoc index 6e47d5618d598..0740cf7838b15 100644 --- a/docs/user/alerting/action-types/jira.asciidoc +++ b/docs/user/alerting/action-types/jira.asciidoc @@ -34,7 +34,7 @@ API token (or password):: Jira API authentication token (or password) for HTTP -- [[jira-connector-config-properties]] -`config` defines the action type specific to the configuration and contains the following properties: +**`config`** defines the action type specific to the configuration and contains the following properties: [cols="2*<"] |=== @@ -47,7 +47,7 @@ API token (or password):: Jira API authentication token (or password) for HTTP |=== -`secrets` defines sensitive information for the action type: +**`secrets`** defines sensitive information for the action type and contains the following properties: [cols="2*<"] |=== @@ -65,14 +65,44 @@ API token (or password):: Jira API authentication token (or password) for HTTP Jira actions have the following configuration properties: -Issue type:: The type of the issue. -Priority:: The priority of the incident. -Labels:: The labels of the incident. -Title:: A title for the issue, used for searching the contents of the knowledge base. -Description:: The details about the incident. -Parent:: The parent issue id or key. Only for `Sub-task` issue types. -Priority:: The priority of the incident. -Additional comments:: Additional information for the client, such as how to troubleshoot the issue. +Subaction:: The subaction to perform: `pushToService`, `getIncident`, `issueTypes`, `fieldsByIssueType`, `issues`, `issue`, or `getFields`. +Subaction params:: The parameters of the subaction. + +==== `pushToService` subaction configuration + +Incident:: A Jira incident has the following properties: +* `summary` - The title of the issue. +* `description` - A description of the issue. +* `externalId` - The ID of the issue in Jira. If present, the issue is updated. Otherwise, a new issue is created. +* `issueType` - The ID of the issue type in Jira. +* `priority` - The priority level in Jira. Example: `Medium`. +* `labels` - An array of labels. Labels cannot contain spaces. +* `parent` - The parent issue ID or key. Only for subtask issue types. +Comments:: A comment in the form of `{ commentId: string, version: string, comment: string }`. + +==== `getIncident` subaction configuration + +External ID:: The ID of the issue in Jira. + +==== `issueTypes` subaction configuration + +The `issueTypes` subaction has no parameters. Provide an empty object `{}`. + +==== `fieldsByIssueType` subaction configuration + +ID:: The ID of the issue in Jira. + +==== `issues` subaction configuration + +Title:: The title to search for. + +==== `issue` subaction configuration + +ID:: The ID of the issue in Jira. + +==== `getFields` subaction configuration + +The `getFields` subaction has no parameters. Provide an empty object `{}`. [[configuring-jira]] ==== Configuring and testing Jira diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc index e1078a55ddd0d..c3185aaad553a 100644 --- a/docs/user/alerting/action-types/pagerduty.asciidoc +++ b/docs/user/alerting/action-types/pagerduty.asciidoc @@ -151,14 +151,25 @@ Integration Key:: A 32 character PagerDuty Integration Key for an integration -- [[pagerduty-connector-config-properties]] -`config` defines the action type specific to the configuration. -`config` contains -`apiURL`, a string that corresponds to *API URL*. +**`config`** defines the action type specific to the configuration and contains the following properties: -`secrets` defines sensitive information for the action type. -`secrets` contains -`routingKey`, a string that corresponds to *Integration Key*. +[cols="2*<"] +|=== +|`apiURL` +| A URL string that corresponds to *API URL*. + +|=== + +**`secrets`** defines sensitive information for the action type and contains the following properties: + +[cols="2*<"] +|=== + +|`routingKey` +| A string that corresponds to *Integration Key*. + +|=== [float] [[pagerduty-action-configuration]] diff --git a/docs/user/alerting/action-types/resilient.asciidoc b/docs/user/alerting/action-types/resilient.asciidoc index 112246ab91162..dfa95e2deec00 100644 --- a/docs/user/alerting/action-types/resilient.asciidoc +++ b/docs/user/alerting/action-types/resilient.asciidoc @@ -34,7 +34,7 @@ API key secret:: The authentication key secret for HTTP Basic authentication. -- [[resilient-connector-config-properties]] -`config` defines the action type specific to the configuration and contains the following properties: +**`config`** defines the action type specific to the configuration and contains the following properties: [cols="2*<"] |=== @@ -47,7 +47,7 @@ API key secret:: The authentication key secret for HTTP Basic authentication. |=== -`secrets` defines sensitive information for the action type: +**`secrets`** defines sensitive information for the action type and contains the following properties: [cols="2*<"] |=== @@ -65,11 +65,30 @@ API key secret:: The authentication key secret for HTTP Basic authentication. IBM Resilient actions have the following configuration properties: -Incident types:: The incident types of the incident. -Severity code:: The severity of the incident. -Name:: A name for the issue, used for searching the contents of the knowledge base. -Description:: The details about the incident. -Additional comments:: Additional information for the client, such as how to troubleshoot the issue. +Subaction:: The subaction to perform: `pushToService`, `getFields`, `incidentTypes`, or `severity`. +Subaction params:: The parameters of the subaction. + +==== `pushToService` subaction configuration + +Incident:: The IBM resilient incident has the following properties: +* `name` - A name for the issue, used for searching the contents of the knowledge base. +* `description` - The details about the incident. +* `externalId` - The ID of the incident in IBM Resilient. If present, the incident is updated. Otherwise, a new incident is created. +* `incidentTypes` - An array with the IDs of IBM Resilient incident types. +* `severityCode` - The IBM Resilient ID of the severity code. +Comments:: A comment in the form of `{ commentId: string, version: string, comment: string }`. + +===== `getFields` subaction configuration + +The `getFields` subaction has not parameters. Provide an empty object `{}`. + +===== `incidentTypes` subaction configuration + +The `incidentTypes` subaction has no parameters. Provide an empty object `{}`. + +===== `severity` subaction configuration + +The `severity` subaction has no parameters. Provide an empty object `{}`. [[configuring-resilient]] ==== Configuring and testing IBM Resilient diff --git a/docs/user/alerting/action-types/server-log.asciidoc b/docs/user/alerting/action-types/server-log.asciidoc index 7022320328c85..276f64e7786bd 100644 --- a/docs/user/alerting/action-types/server-log.asciidoc +++ b/docs/user/alerting/action-types/server-log.asciidoc @@ -30,3 +30,4 @@ Name:: The name of the connector. The name is used to identify a connector Server log actions have the following properties: Message:: The message to log. +Level:: The log level of the message: `trace`, `debug`, `info`, `warn`, `error` or `fatal`. Defaults to `info`. diff --git a/docs/user/alerting/action-types/servicenow.asciidoc b/docs/user/alerting/action-types/servicenow.asciidoc index 5d8782c14e581..d1ee1b9357737 100644 --- a/docs/user/alerting/action-types/servicenow.asciidoc +++ b/docs/user/alerting/action-types/servicenow.asciidoc @@ -32,7 +32,7 @@ Password:: Password for HTTP Basic authentication. -- [[servicenow-connector-config-properties]] -`config` defines the action type specific to the configuration and contains the following properties: +**`config`** defines the action type specific to the configuration and contains the following properties: [cols="2*<"] |=== @@ -42,7 +42,7 @@ Password:: Password for HTTP Basic authentication. |=== -`secrets` defines sensitive information for the action type: +**`secrets`** defines sensitive information for the action type and contains the following properties: [cols="2*<"] |=== @@ -60,12 +60,33 @@ Password:: Password for HTTP Basic authentication. ServiceNow actions have the following configuration properties: -Urgency:: The extent to which the incident resolution can delay. -Severity:: The severity of the incident. -Impact:: The effect an incident has on business. Can be measured by the number of affected users or by how critical it is to the business in question. -Short description:: A short description for the incident, used for searching the contents of the knowledge base. -Description:: The details about the incident. -Additional comments:: Additional information for the client, such as how to troubleshoot the issue. +Subaction:: The subaction to perform: `pushToService`, `getFields`, `getIncident`, or `getChoices`. +Subaction params:: The parameters of the subaction. + +==== `pushToService` subaction configuration + +Incident:: The ServiceNow incident has the following properties: +* `short_description` - A short description for the incident, used for searching the contents of the knowledge base. +* `description` - The details about the incident. +* `externalId` - The ID of the incident in ServiceNow. If present, the incident is updated. Otherwise, a new incident is created. +* `severity` - The severity of the incident. +* `urgency` - The extent to which the incident resolution can delay. +* `impact` - The effect an incident has on business. Can be measured by the number of affected users or by how critical it is to the business in question. +* `category` - The name of the category in ServiceNow. +* `subcategory` - The name of the subcategory in ServiceNow. +Comments:: A comment in the form of `{ commentId: string, version: string, comment: string }`. + +===== `getFields` subaction configuration + +The `getFields` subaction has no parameters. Provide an empty object `{}`. + +===== `getIncident` subaction configuration + +External ID:: The ID of the incident in ServiceNow. + +===== `getChoices` subaction configuration + +Fields:: An array of fields. Example: `[priority, category, impact]`. [[configuring-servicenow]] ==== Configuring and testing ServiceNow diff --git a/docs/user/alerting/action-types/slack.asciidoc b/docs/user/alerting/action-types/slack.asciidoc index 6a38e5c827ab2..6258969753356 100644 --- a/docs/user/alerting/action-types/slack.asciidoc +++ b/docs/user/alerting/action-types/slack.asciidoc @@ -22,15 +22,19 @@ Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messa my-slack: name: preconfigured-slack-action-type actionTypeId: .slack - config: + secrets: webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz' -- -[[slack-connector-config-properties]] -`config` defines the action type specific to the configuration. -`config` contains -`webhookUrl`, a string that corresponds to *Webhook URL*. +**`secrets`** defines sensitive information for the action type and contains the following properties: +[cols="2*<"] +|=== + +| `webhookUrl` +| A string that corresponds to *Webhook URL*. + +|=== [float] [[slack-action-configuration]] diff --git a/docs/user/alerting/action-types/teams.asciidoc b/docs/user/alerting/action-types/teams.asciidoc index e1ce91fc0c123..7f4a29dc86fc5 100644 --- a/docs/user/alerting/action-types/teams.asciidoc +++ b/docs/user/alerting/action-types/teams.asciidoc @@ -22,14 +22,20 @@ Webhook URL:: The URL of the incoming webhook. See https://docs.microsoft.com/ my-teams: name: preconfigured-teams-action-type actionTypeId: .teams - config: + secrets: webhookUrl: 'https://outlook.office.com/webhook/abcd@0123456/IncomingWebhook/abcdefgh/ijklmnopqrstuvwxyz' -- [[teams-connector-config-properties]] -`config` defines the action type specific to the configuration. -`config` contains -`webhookUrl`, a string that corresponds to *Webhook URL*. +**`secrets`** defines sensitive information for the action type and contains the following properties: + +[cols="2*<"] +|=== + +| `webhookUrl` +| A string that corresponds to *Webhook URL*. + +|=== [float] diff --git a/docs/user/alerting/action-types/webhook.asciidoc b/docs/user/alerting/action-types/webhook.asciidoc index 2d626d53d1c77..efe1077707ef0 100644 --- a/docs/user/alerting/action-types/webhook.asciidoc +++ b/docs/user/alerting/action-types/webhook.asciidoc @@ -12,10 +12,10 @@ Webhook connectors have the following configuration properties: Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. URL:: The request URL. If you are using the <> setting, make sure the hostname is added to the allowed hosts. -Method:: HTTP request method, either `post`(default) or `put`. +Method:: HTTP request method, either `POST`(default) or `PUT`. Headers:: A set of key-value pairs sent as headers with the request -User:: An optional username. If set, HTTP basic authentication is used. Currently only basic authentication is supported. -Password:: An optional password. If set, HTTP basic authentication is used. Currently only basic authentication is supported. +User:: Username for HTTP basic authentication. +Password:: Password for HTTP basic authentication. [float] [[Preconfigured-webhook-configuration]] @@ -37,7 +37,7 @@ Password:: An optional password. If set, HTTP basic authentication is used. Cur -- [[webhook-connector-config-properties]] -`config` defines the action type specific to the configuration and contains the following properties: +**`config`** defines the action type specific to the configuration and contains the following properties: [cols="2*<"] |=== @@ -51,18 +51,21 @@ Password:: An optional password. If set, HTTP basic authentication is used. Cur |`headers` |A record that corresponds to *Headers*. +| `hasAuth` +| If `true`, this connector will require values for `user` and `password` inside the secrets configuration. Defaults to `true`. + |=== -`secrets` defines sensitive information for the action type: +**`secrets`** defines sensitive information for the action type and contains the following properties: [cols="2*<"] |=== |`user` -|A string that corresponds to *User*. +|A string that corresponds to *User*. Required if `hasAuth` is set to `true`. |`password` -|A string that corresponds to *Password*. Should be stored in the <>. +|A string that corresponds to *Password*. Should be stored in the <>. Required if `hasAuth` is set to `true`. |=== diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc index 528189b8a1216..5f13c6e1774a8 100644 --- a/docs/user/alerting/defining-alerts.asciidoc +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -92,6 +92,7 @@ Actions are not required on alerts. In some cases you may want to run an alert w ============================================== [float] +[[actions-configuration]] === Global actions configuration Some actions configuration options apply to all actions. If you are using an *on-prem* Elastic Stack deployment, you can set these in the kibana.yml file. @@ -99,8 +100,10 @@ If you are using a cloud deployment, you can set these via the console. Here's a list of the available global configuration options and an explanation of what each one does: +* `xpack.actions.enabled`: Feature toggle that enables Actions in {kib}. Default: `true` * `xpack.actions.allowedHosts`: Specifies an array of host names which actions such as email, Slack, PagerDuty, and webhook can connect to. An element of * indicates any host can be connected to. An empty array indicates no hosts can be connected to. Default: [ {asterisk} ] -* `xpack.actions.enabledActionTypes`: Specifies to an array of action types that are enabled. An {asterisk} indicates all action types registered are enabled. The action types that {kib} provides are: .server-log, .slack, .email, .index, .pagerduty, .webhook. Default: [ {asterisk} ] +* `xpack.actions.enabledActionTypes`: Specifies an array of action types that are enabled. An {asterisk} indicates all action types registered are enabled. The action types that {kib} provides are `.email`, `.index`, `.jira`, `.pagerduty`, `.resilient`, `.server-log`, `.servicenow`, `.servicenow-sir`, `.slack`, `.teams`, and `.webhook`. Default: [ {asterisk} ] +* `xpack.actions.preconfigured`: Specifies preconfigured action IDs and configs. Default: {} * `xpack.actions.proxyUrl`: Specifies the proxy URL to use, if using a proxy for actions. * `xpack.actions.proxyHeader`: Specifies HTTP headers for proxy, if using a proxy for actions. * `xpack.actions.proxyRejectUnauthorizedCertificates`: Set to `false` to bypass certificate validation for proxy, if using a proxy for actions. diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 9d48e618b76dc..78094f4c0eb0b 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -26,19 +26,12 @@ Table of Contents - [Executor](#executor) - [Example](#example) - [RESTful API](#restful-api) - - [`POST /api/actions/action`: Create action](#post-apiactionsaction-create-action) - - [`DELETE /api/actions/action/{id}`: Delete action](#delete-apiactionsactionid-delete-action) - - [`GET /api/actions`: Get all actions](#get-apiactions-get-all-actions) - - [`GET /api/actions/action/{id}`: Get action](#get-apiactionsactionid-get-action) - - [`GET /api/actions/list_action_types`: List action types](#get-apiactionslist_action_types-list-action-types) - - [`PUT /api/actions/action/{id}`: Update action](#put-apiactionsactionid-update-action) - - [`POST /api/actions/action/{id}/_execute`: Execute action](#post-apiactionsactionid_execute-execute-action) - [Firing actions](#firing-actions) - - [Accessing a scoped ActionsClient](#accessing-a-scoped-actionsclient) + - [Accessing a scoped ActionsClient](#accessing-a-scoped-actionsclient) - [actionsClient.enqueueExecution(options)](#actionsclientenqueueexecutionoptions) - - [Example](#example-1) + - [Example](#example-1) - [actionsClient.execute(options)](#actionsclientexecuteoptions) - - [Example](#example-2) + - [Example](#example-2) - [Built-in Action Types](#built-in-action-types) - [Server log](#server-log) - [`config`](#config) @@ -109,9 +102,9 @@ action types. ## Usage -1. Develop and register an action type (see action types -> example). -2. Create an action by using the RESTful API (see actions -> create action). -3. Use alerts to execute actions or execute manually (see firing actions). +1. Develop and register an action type (see [Action types -> Example](#example)). +2. Create an action by using the [RESTful API](#restful-api). +3. Use alerts to execute actions or execute manually (see [Firing actions](#firing-actions)). ## Kibana Actions Configuration @@ -119,50 +112,48 @@ Implemented under the [Actions Config](./server/actions_config.ts). ### Configuration Options -Built-In-Actions are configured using the _xpack.actions_ namespoace under _kibana.yml_, and have the following configuration options: +Built-In-Actions are configured using the _xpack.actions_ namespace under _kibana.yml_. See the [Actions configuration Documentation](https://www.elastic.co/guide/en/kibana/master/defining-alerts.html#actions-configuration) for all configuration options. -| Namespaced Key | Description | Type | -| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -| _xpack.actions._**enabled** | Feature toggle which enabled Actions in Kibana. | boolean | -| _xpack.actions._**allowedHosts** | Which _hostnames_ are allowed for the Built-In-Action? This list should contain hostnames of every external service you wish to interact with using Webhooks, Email or any other built in Action. Note that you may use the string "\*" in place of a specific hostname to enable Kibana to target any URL, but keep in mind the potential use of such a feature to execute [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery) attacks from your server. | Array | -| _xpack.actions._**enabledActionTypes** | A list of _actionTypes_ id's that are enabled. A "\*" may be used as an element to indicate all registered actionTypes should be enabled. The actionTypes registered for Kibana are `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, `.webhook`. Default: `["*"]` | Array | -| _xpack.actions._**preconfigured** | A object of action id / preconfigured actions. Default: `{}` | Array | +#### **allowedHosts** configuration -#### Adding Built-in Action Types to allowedHosts +- You can use the string "*" in the **allowedHosts** configuration in place of a specific hostname to enable Kibana to target any URL, but keep in mind the potential to use such a feature to execute [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery) attacks from your server. -It is worth noting that the **allowedHosts** configuation applies to built-in action types (such as Slack, or PagerDuty) as well. - -Uniquely, the _PagerDuty Action Type_ has been configured to support the service's Events API (at _https://events.pagerduty.com/v2/enqueue_, which you can read about [here](https://v2.developer.pagerduty.com/docs/events-api-v2)) as a default, but this too, must be included in the allowedHosts before the PagerDuty action can be used. +- The **allowedHosts** configuration applies to built-in action types (such as Slack and PagerDuty). While the _PagerDuty Action Type_ has been configured to support the service's Events API (at _https://events.pagerduty.com/v2/enqueue_, which you can read about in [Pagerduty's documentation](https://v2.developer.pagerduty.com/docs/events-api-v2)), the PagerDuty domain must still be included in the allowedHosts configuration before the action can be used. ### Configuration Utilities -This module provides a Utilities for interacting with the configuration. +This module provides utilities for interacting with the configuration. -| Method | Arguments | Description | Return Type | -| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | -| isUriAllowed | _uri_: The URI you wish to validate is allowed | Validates whether the URI is allowed. This checks the configuration and validates that the hostname of the URI is in the list of allowed Hosts and returns `true` if it is allowed. If the configuration says that all URI's are allowed (using an "\*") then it will always return `true`. | Boolean | -| isHostnameAllowed | _hostname_: The Hostname you wish to validate is allowed | Validates whether the Hostname is allowed. This checks the configuration and validates that the hostname is in the list of allowed Hosts and returns `true` if it is allowed. If the configuration says that all Hostnames are allowed (using an "\*") then it will always return `true`. | Boolean | -| isActionTypeEnabled | _actionType_: The actionType to check to see if it's enabled | Returns true if the actionType is enabled, otherwise false. | Boolean | -| ensureUriAllowed | _uri_: The URI you wish to validate is allowed | Validates whether the URI is allowed. This checks the configuration and validates that the hostname of the URI is in the list of allowed Hosts and throws an error if it is not allowed. If the configuration says that all URI's are allowed (using an "\*") then it will never throw. | No return value, throws if URI isn't allowed | -| ensureHostnameAllowed | _hostname_: The Hostname you wish to validate is allowed | Validates whether the Hostname is allowed. This checks the configuration and validates that the hostname is in the list of allowed Hosts and throws an error if it is not allowed. If the configuration says that all Hostnames are allowed (using an "\*") then it will never throw | No return value, throws if Hostname isn't allowed . | -| ensureActionTypeEnabled | _actionType_: The actionType to check to see if it's enabled | Throws an error if the actionType is not enabled | No return value, throws if actionType isn't enabled | +| Method | Arguments | Description | Return Type | +| --------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | +| isUriAllowed | _uri_: The URI you wish to validate is allowed | Validates whether the URI is allowed. This checks the configuration and validates that the hostname of the URI is in the list of allowed Hosts and returns `true` if it is allowed. If the configuration says that all URI's are allowed (using an "\*") then it will always return `true`. | Boolean | +| isHostnameAllowed | _hostname_: The Hostname you wish to validate is allowed | Validates whether the Hostname is allowed. This checks the configuration and validates that the hostname is in the list of allowed Hosts and returns `true` if it is allowed. If the configuration says that all Hostnames are allowed (using an "\*") then it will always return `true`. | Boolean | +| isActionTypeEnabled | _actionType_: The actionType to check to see if it's enabled | Returns true if the actionType is enabled, otherwise false. | Boolean | +| ensureUriAllowed | _uri_: The URI you wish to validate is allowed | Validates whether the URI is allowed. This checks the configuration and validates that the hostname of the URI is in the list of allowed Hosts and throws an error if it is not allowed. If the configuration says that all URI's are allowed (using an "\*") then it will never throw. | No return value, throws if URI isn't allowed | +| ensureHostnameAllowed | _hostname_: The Hostname you wish to validate is allowed | Validates whether the Hostname is allowed. This checks the configuration and validates that the hostname is in the list of allowed Hosts and throws an error if it is not allowed. If the configuration says that all Hostnames are allowed (using an "\*") then it will never throw | No return value, throws if Hostname isn't allowed . | +| ensureActionTypeEnabled | _actionType_: The actionType to check to see if it's enabled | Throws an error if the actionType is not enabled | No return value, throws if actionType isn't enabled | +| isRejectUnauthorizedCertificatesEnabled | _none_ | Returns value of `rejectUnauthorized` from configuration. | Boolean | +| getProxySettings | _none_ | If `proxyUrl` is set in the configuration, returns the proxy settings `proxyUrl`, `proxyHeaders` and `proxyRejectUnauthorizedCertificates`. Otherwise returns _undefined_. | Undefined or ProxySettings | ## Action types ### Methods -**server.plugins.actions.setup.registerType(options)** +**server.plugins.actions.setup.registerType (options)** The following table describes the properties of the `options` object. -| Property | Description | Type | -| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -| id | Unique identifier for the action type. For convention, ids starting with `.` are reserved for built in action types. We recommend using a convention like `.mySpecialAction` for your action types. | string | -| name | A user-friendly name for the action type. These will be displayed in dropdowns when chosing action types. | string | -| unencryptedAttributes | A list of opt-out attributes that don't need to be encrypted. These attributes won't need to be re-entered on import / export when the feature becomes available. These attributes will also be readable / displayed when it comes to a table / edit screen. | array of strings | -| validate.params | When developing an action type, it needs to accept parameters to know what to do with the action. (Example to, from, subject, body of an email). See the current built-in email action type for an example of the state-of-the-art validation.

Technically, the value of this property should have a property named `validate()` which is a function that takes a params object to validate and returns a sanitized version of that object to pass to the execution function. Validation errors should be thrown from the `validate()` function and will be available as an error message | schema / validation function | -| validate.config | Similar to params, a config is required when creating an action (for example host, port, username, and password of an email server). | schema / validation function | -| executor | This is where the code of an action type lives. This is a function gets called for executing an action from either alerting or manually by using the exposed function (see firing actions). For full details, see executor section below. | Function | +| Property | Description | Type | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| id | Unique identifier for the action type. For convention, ids starting with `.` are reserved for built in action types. We recommend using a convention like `.mySpecialAction` for your action types. | string | +| name | A user-friendly name for the action type. These will be displayed in dropdowns when chosing action types. | string | +| maxAttempts | The maximum number of times this action will attempt to execute when scheduled. | number | +| minimumLicenseRequired | The license required to use the action type. | string | +| validate.params | When developing an action type, it needs to accept parameters to know what to do with the action. (Example `to`, `from`, `subject`, `body` of an email). See the current built-in email action type for an example of the state-of-the-art validation.

Technically, the value of this property should have a property named `validate()` which is a function that takes a params object to validate and returns a sanitized version of that object to pass to the execution function. Validation errors should be thrown from the `validate()` function and will be available as an error message | schema / validation function | +| validate.config | Similar to params, a config may be required when creating an action (for example `host` and `port` for an email server). | schema / validation function | +| validate.secrets | Similar to params, a secrets object may be required when creating an action (for example `user` and `password` for an email server). | schema / validation function | +| executor | This is where the code of an action type lives. This is a function gets called for executing an action from either alerting or manually by using the exposed function (see firing actions). For full details, see executor section below. | Function | +| renderParameterTemplates | Optionally define a function to provide custom rendering for this action type. | Function | **Important** - The config object is persisted in ElasticSearch and updated via the ElasticSearch update document API. This API allows "partial updates" - and this can cause issues with the encryption used on specified properties. So, a `validate()` function should return values for all configuration properties, so that partial updates do not occur. Setting property values to `null` rather than `undefined`, or not including a property in the config object, is all you need to do to ensure partial updates won't occur. @@ -175,12 +166,13 @@ This is the primary function for an action type. Whenever the action needs to ex | Property | Description | | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | actionId | The action saved object id that the action type is executing for. | -| config | The decrypted configuration given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. | +| config | The action configuration. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. | +| secrets | The decrypted secrets object given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the secrets object before being passed to the executor, define `validate.secrets` within the action type. | | params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. | | services.callCluster(path, opts) | Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but runs in the context of the user who is calling the action when security is enabled. | | services.getLegacyScopedClusterClient | This function returns an instance of the LegacyScopedClusterClient scoped to the user who is calling the action when security is enabled. | | services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.

The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). | -| services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) | +| services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) ### Example @@ -189,83 +181,14 @@ The built-in email action type provides a good example of creating an action typ ## RESTful API -Using an action type requires an action to be created that will contain and encrypt configuration for a given action type. See below for CRUD operations using the API. - -### `POST /api/actions/action`: Create action - -Payload: - -| Property | Description | Type | -| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| name | A name to reference and search in the future. This value will be used to populate dropdowns. | string | -| actionTypeId | The id value of the action type you want to call when the action executes. | string | -| config | The configuration the action type expects. See related action type to see what attributes are expected. This will also validate against the action type if config validation is defined. | object | -| secrets | The secrets the action type expects. See related action type to see what attributes are expected. This will also validate against the action type if secrets validation is defined. | object | - -### `DELETE /api/actions/action/{id}`: Delete action - -Params: - -| Property | Description | Type | -| -------- | --------------------------------------------- | ------ | -| id | The id of the action you're trying to delete. | string | - -### `GET /api/actions`: Get all actions - -No parameters. - -Return all actions from saved objects merged with predefined list. -Use the [saved objects API for find](https://www.elastic.co/guide/en/kibana/master/saved-objects-api-find.html) with the proprties: `type: 'action'` and `perPage: 10000`. -List of predefined actions should be set up in Kibana.yaml. - -### `GET /api/actions/action/{id}`: Get action - -Params: - -| Property | Description | Type | -| -------- | ------------------------------------------ | ------ | -| id | The id of the action you're trying to get. | string | - -### `GET /api/actions/list_action_types`: List action types - -No parameters. - -### `PUT /api/actions/action/{id}`: Update action - -Params: - -| Property | Description | Type | -| -------- | --------------------------------------------- | ------ | -| id | The id of the action you're trying to update. | string | - -Payload: - -| Property | Description | Type | -| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| name | A name to reference and search in the future. This value will be used to populate dropdowns. | string | -| config | The configuration the action type expects. See related action type to see what attributes are expected. This will also validate against the action type if config validation is defined. | object | -| secrets | The secrets the action type expects. See related action type to see what attributes are expected. This will also validate against the action type if secrets validation is defined. | object | - -### `POST /api/actions/action/{id}/_execute`: Execute action - -Params: - -| Property | Description | Type | -| -------- | ---------------------------------------------- | ------ | -| id | The id of the action you're trying to execute. | string | - -Payload: - -| Property | Description | Type | -| -------- | ---------------------------------------------------------- | ------ | -| params | The parameters the action type requires for the execution. | object | +Using an action type requires an action to be created that will contain and encrypt configuration for a given action type. See the [REST API Documentation](https://www.elastic.co/guide/en/kibana/master/actions-and-connectors-api.html) API for CRUD operations for Actions. ## Firing actions Running actions is possible by using the ActionsClient which is provided by the `getActionsClientWithRequest` function part of the plugin's Start Contract. By providing the user's Request you'll receive an instance of the ActionsClient which is tailered to the current user and is scoped to the resources the user is authorized to access. -## Accessing a scoped ActionsClient +### Accessing a scoped ActionsClient ``` const actionsClient = server.plugins.actions.getActionsClientWithRequest(request); @@ -279,7 +202,7 @@ This api schedules a task which will run the action using the current user scope Running the action by scheduling a task means that we will no longer have a user request by which to ascertain the action's privileges and so you might need to provide these yourself: -- The **SpaceId** in which the user's action is expected to run +- The **spaceId** in which the user's action is expected to run - When security is enabled you'll also need to provide an **apiKey** which allows us to mimic the user and their privileges. The following table describes the properties of the `options` object. @@ -292,7 +215,7 @@ The following table describes the properties of the `options` object. | apiKey | The Elasticsearch API key to use for context. (Note: only required and used when security is enabled). | string | | source | The source of the execution, either an HTTP request or a reference to a Saved Object. | object, optional | -## Example +#### Example This example makes action `3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5` send an email. The action plugin will load the saved object and find what action type to call with `params`. @@ -324,7 +247,7 @@ The following table describes the properties of the `options` object. | params | The `params` value to give the action type executor. | object | | source | The source of the execution, either an HTTP request or a reference to a Saved Object. | object, optional | -## Example +#### Example As with the previous example, we'll use the action `3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5` to send an email. @@ -347,405 +270,7 @@ const result = await actionsClient.execute({ # Built-in Action Types -Kibana ships with a set of built-in action types: - -| Type | Id | Description | -| ------------------------------- | ----------------- | ------------------------------------------------------------------ | -| [Server log](#server-log) | `.server-log` | Logs messages to the Kibana log using Kibana's logger | -| [Email](#email) | `.email` | Sends an email using SMTP | -| [Slack](#slack) | `.slack` | Posts a message to a slack channel | -| [Index](#index) | `.index` | Indexes document(s) into Elasticsearch | -| [Webhook](#webhook) | `.webhook` | Send a payload to a web service using HTTP POST or PUT | -| [PagerDuty](#pagerduty) | `.pagerduty` | Trigger, resolve, or acknowlege an incident to a PagerDuty service | -| [ServiceNow ITSM](#servicenow) | `.servicenow` | Create or update an incident to a ServiceNow ITSM instance | -| [ServiceNow SIR](#servicenow) | `.servicenow-sir` | Create or update an incident to a ServiceNow SIR instance | -| [Jira](#jira) | `.jira` | Create or update an issue to a Jira instance | -| [IBM Resilient](#ibm-resilient) | `.resilient` | Create or update an incident to a IBM Resilient instance | - ---- - -## Server log - -ID: `.log` - -The params properties are modelled after the arguments to the [Hapi.server.log()](https://hapijs.com/api#-serverlogtags-data-timestamp) function. - -### `config` - -This action has no `config` properties. - -### `secrets` - -This action type has no `secrets` properties. - -### `params` - -| Property | Description | Type | -| -------- | ---------------------------------------- | --------------------- | -| message | The message to log. | string | -| tags | Tags associated with the message to log. | string[] _(optional)_ | - ---- - -## Email - -ID: `.email` - -This action type uses [nodemailer](https://nodemailer.com/about/) to send emails. - -### `config` - -Either the property `service` must be provided, or the `host` and `port` properties must be provided. If `service` is provided, `host`, `port` and `secure` are ignored. For more information on the `gmail` service value specifically, see the [nodemailer gmail documentation](https://nodemailer.com/usage/using-gmail/). - -The `secure` property defaults to `false`. See the [nodemailer TLS documentation](https://nodemailer.com/smtp/#tls-options) for more information. - -The `from` field can be specified as in typical `"user@host-name"` format, or as `"human name "` format. See the [nodemailer address documentation](https://nodemailer.com/message/addresses/) for more information. - -| Property | Description | Type | -| -------- | ------------------------------------------------------------------------------------------ | -------------------- | -| service | the name of a [well-known email service provider](https://nodemailer.com/smtp/well-known/) | string _(optional)_ | -| host | host name of the service provider | string _(optional)_ | -| port | port number of the service provider | number _(optional)_ | -| secure | whether to use TLS with the service provider | boolean _(optional)_ | -| from | the from address for all emails sent with this action type | string | - -### `secrets` - -| Property | Description | Type | -| -------- | ----------------------------------------- | ------ | -| user | userid to use with the service provider | string | -| password | password to use with the service provider | string | - -### `params` - -There must be at least one entry in the `to`, `cc` and `bcc` arrays. - -The message text will be sent as both plain text and html text. Additional function may be provided later. - -The `to`, `cc`, and `bcc` array entries can be in the same format as the `from` property described in the config object above. - -| Property | Description | Type | -| -------- | ----------------------------- | --------------------- | -| to | list of to addressees | string[] _(optional)_ | -| cc | list of cc addressees | string[] _(optional)_ | -| bcc | list of bcc addressees | string[] _(optional)_ | -| subject | the subject line of the email | string | -| message | the message text | string | - ---- - -## Slack - -ID: `.slack` - -This action type interfaces with the [Slack Incoming Webhooks feature](https://api.slack.com/incoming-webhooks). Currently the params property `message` will be used as the `text` property of the Slack incoming message. Additional function may be provided later. - -### `config` - -This action type has no `config` properties. - -### `secrets` - -| Property | Description | Type | -| ---------- | ------------------------------------- | ------ | -| webhookUrl | the url of the Slack incoming webhook | string | - -### `params` - -| Property | Description | Type | -| -------- | ---------------- | ------ | -| message | the message text | string | - ---- - -## Index - -ID: `.index` - -The config and params properties are modelled after the [Watcher Index Action](https://www.elastic.co/guide/en/elasticsearch/reference/master/actions-index.html). The index can be set in the config or params, and if set in config, then the index set in the params will be ignored. - -### `config` - -| Property | Description | Type | -| -------------------- | ---------------------------------------------------------- | -------------------- | -| index | The Elasticsearch index to index into. | string _(optional)_ | -| doc_id | The optional \_id of the document. | string _(optional)_ | -| execution_time_field | The field that will store/index the action execution time. | string _(optional)_ | -| refresh | Setting of the refresh policy for the write request. | boolean _(optional)_ | - -### `secrets` - -This action type has no `secrets` properties. - -### `params` - -| Property | Description | Type | -| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -| documents | JSON object that describes the [document](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-index.html#getting-started-batch-processing). | object[] | - ---- - -## Webhook - -ID: `.webhook` - -The webhook action uses [axios](https://github.com/axios/axios) to send a POST or PUT request to a web service. - -### `config` - -| Property | Description | Type | -| -------- | ------------------------------------------------------- | ------------------------------------------------ | -| url | Request URL | string | -| method | HTTP request method, either `post`_(default)_ or `put` | string _(optional)_ | -| headers | Key-value pairs of the headers to send with the request | object, keys and values are strings _(optional)_ | - -### `secrets` - -| Property | Description | Type | -| -------- | -------------------------------------- | ------------------- | -| user | Username for HTTP Basic authentication | string _(optional)_ | -| password | Password for HTTP Basic authentication | string _(optional)_ | - -### `params` - -| Property | Description | Type | -| -------- | --------------------- | ------------------- | -| body | The HTTP request body | string _(optional)_ | - ---- - -## PagerDuty - -ID: `.pagerduty` - -The PagerDuty action uses the [V2 Events API](https://v2.developer.pagerduty.com/docs/events-api-v2) to trigger, acknowlege, and resolve PagerDuty alerts. - -### `config` - -| Property | Description | Type | -| -------- | -------------------------------------------------------------------------- | ------------------- | -| apiUrl | PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue` | string _(optional)_ | - -### `secrets` - -| Property | Description | Type | -| ---------- | ---------------------------------------------------------------------------------------------------------- | ------ | -| routingKey | This is the 32 character PagerDuty Integration Key for an integration on a service or on a global ruleset. | string | - -### `params` - -| Property | Description | Type | -| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | -| eventAction | One of `trigger` _(default)_, `resolve`, or `acknowlege`. See [event action](https://v2.developer.pagerduty.com/docs/events-api-v2#event-action) for more details. | string _(optional)_ | -| dedupKey | All actions sharing this key will be associated with the same PagerDuty alert. Used to correlate trigger and resolution. The maximum length is **255** characters. See [alert deduplication](https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication) for details. | string _(optional)_ | -| summary | A text summary of the event, defaults to `No summary provided`. The maximum length is **1024** characters. | string _(optional)_ | -| source | The affected system, preferably a hostname or fully qualified domain name. Defaults to `Kibana Action `. | string _(optional)_ | -| severity | The perceived severity of on the affected system. This can be one of `critical`, `error`, `warning` or `info`_(default)_. | string _(optional)_ | -| timestamp | An [ISO-8601 format date-time](https://v2.developer.pagerduty.com/v2/docs/types#datetime), indicating the time the event was detected or generated. | string _(optional)_ | -| component | The component of the source machine that is responsible for the event, for example `mysql` or `eth0`. | string _(optional)_ | -| group | Logical grouping of components of a service, for example `app-stack`. | string _(optional)_ | -| class | The class/type of the event, for example `ping failure` or `cpu load`. | string _(optional)_ | - -For more details see [PagerDuty v2 event parameters](https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2). - ---- - -## ServiceNow - -ServiceNow ITSM ID: `.servicenow` - -ServiceNow SIR ID: `.servicenow-sir` - -The ServiceNow actions use the [V2 Table API](https://developer.servicenow.com/app.do#!/rest_api_doc?v=orlando&id=c_TableAPI) to create and update ServiceNow incidents. Both action types use the same `config`, `secrets`, and `params` schema. - -### `config` - -| Property | Description | Type | -| -------- | ------------------------ | ------ | -| apiUrl | ServiceNow instance URL. | string | - -### `secrets` - -| Property | Description | Type | -| -------- | -------------------------------------- | ------ | -| username | Username for HTTP Basic authentication | string | -| password | Password for HTTP Basic authentication | string | - -### `params` - -| Property | Description | Type | -| --------------- | -------------------------------------------------------------------------------------------------- | ------ | -| subAction | The sub action to perform. It can be `pushToService`, `getFields`, `getIncident`, and `getChoices` | string | -| subActionParams | The parameters of the sub action | object | - -#### `subActionParams (pushToService)` - -| Property | Description | Type | -| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- | -| incident | The ServiceNow incident. | object | -| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | - -The following table describes the properties of the `incident` object. - -| Property | Description | Type | -| ----------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------- | -| short_description | The title of the incident. | string | -| description | The description of the incident. | string _(optional)_ | -| externalId | The id of the incident in ServiceNow. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | -| severity | The name of the severity in ServiceNow. | string _(optional)_ | -| urgency | The name of the urgency in ServiceNow. | string _(optional)_ | -| impact | The name of the impact in ServiceNow. | string _(optional)_ | -| category | The name of the category in ServiceNow. | string _(optional)_ | -| subcategory | The name of the subcategory in ServiceNow. | string _(optional)_ | - -#### `subActionParams (getFields)` - -No parameters for `getFields` sub-action. Provide an empty object `{}`. - -#### `subActionParams (getIncident)` - -| Property | Description | Type | -| ---------- | ------------------------------------- | ------ | -| externalId | The id of the incident in ServiceNow. | string | - - -#### `subActionParams (getChoices)` - -| Property | Description | Type | -| -------- | ------------------------------------------------------------ | -------- | -| fields | An array of fields. Example: `[priority, category, impact]`. | string[] | - ---- - -## Jira - -ID: `.jira` - -The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/platform/rest/v2/) to create and update Jira incidents. - -### `config` - -| Property | Description | Type | -| -------- | ------------------ | ------ | -| apiUrl | Jira instance URL. | string | - -### `secrets` - -| Property | Description | Type | -| -------- | ----------------------------------------------------- | ------ | -| email | email (or username) for HTTP Basic authentication | string | -| apiToken | API token (or password) for HTTP Basic authentication | string | - -### `params` - -| Property | Description | Type | -| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------ | -| subAction | The sub action to perform. It can be `pushToService`, `getIncident`, `issueTypes`, `fieldsByIssueType`, `issues`, `issue`, and `getFields` | string | -| subActionParams | The parameters of the sub action | object | - -#### `subActionParams (pushToService)` - -| Property | Description | Type | -| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- | -| incident | The Jira incident. | object | -| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | - -The following table describes the properties of the `incident` object. - -| Property | Description | Type | -| ----------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- | -| summary | The title of the issue | string | -| description | The description of the issue | string _(optional)_ | -| externalId | The id of the issue in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | -| issueType | The id of the issue type in Jira. | string _(optional)_ | -| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | -| labels | An array of labels. Labels cannot contain spaces. | string[] _(optional)_ | -| parent | The parent issue id or key. Only for `Sub-task` issue types. | string _(optional)_ | - -#### `subActionParams (getIncident)` - -| Property | Description | Type | -| ---------- | --------------------------- | ------ | -| externalId | The id of the issue in Jira | string | - -#### `subActionParams (issueTypes)` - -No parameters for `issueTypes` sub-action. Provide an empty object `{}`. - -#### `subActionParams (fieldsByIssueType)` - -| Property | Description | Type | -| -------- | -------------------------------- | ------ | -| id | The id of the issue type in Jira | string | - -#### `subActionParams (issues)` - -| Property | Description | Type | -| -------- | ----------------------- | ------ | -| title | The title to search for | string | - -#### `subActionParams (issue)` - -| Property | Description | Type | -| -------- | --------------------------- | ------ | -| id | The id of the issue in Jira | string | - -#### `subActionParams (getFields)` - -No parameters for `getFields` sub-action. Provide an empty object `{}`. - -## IBM Resilient - -ID: `.resilient` - -### `config` - -| Property | Description | Type | -| -------- | --------------------------- | ------ | -| apiUrl | IBM Resilient instance URL. | string | - -### `secrets` - -| Property | Description | Type | -| ------------ | -------------------------------------------- | ------ | -| apiKeyId | API key ID for HTTP Basic authentication | string | -| apiKeySecret | API key secret for HTTP Basic authentication | string | - -### `params` - -| Property | Description | Type | -| --------------- | -------------------------------------------------------------------------------------------------- | ------ | -| subAction | The sub action to perform. It can be `pushToService`, `getFields`, `incidentTypes`, and `severity` | string | -| subActionParams | The parameters of the sub action | object | - -#### `subActionParams (pushToService)` - -| Property | Description | Type | -| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- | -| incident | The IBM Resilient incident. | object | -| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | - -The following table describes the properties of the `incident` object. - -| Property | Description | Type | -| ------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| name | The title of the incident | string _(optional)_ | -| description | The description of the incident | string _(optional)_ | -| externalId | The id of the incident in IBM Resilient. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | -| incidentTypes | An array with the ids of IBM Resilient incident types. | number[] _(optional)_ | -| severityCode | IBM Resilient id of the severity code. | number _(optional)_ | - -#### `subActionParams (getFields)` - -No parameters for `getFields` sub-action. Provide an empty object `{}`. - -#### `subActionParams (incidentTypes)` - -No parameters for `incidentTypes` sub-action. Provide an empty object `{}`. - -#### `subActionParams (severity)` - -No parameters for `severity` sub-action. Provide an empty object `{}`. +Kibana ships with a set of built-in action types. See [Actions and connector types Documentation](https://www.elastic.co/guide/en/kibana/master/action-types.html). # Command Line Utility @@ -801,4 +326,4 @@ Instead of `schema.maybe()`, use `schema.nullable()`, which is the same as `sche ## user interface -In order to make this action usable in the Kibana UI, you will need to provide all the UI editing aspects of the action. The existing action type user interfaces are defined in [`x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`](../triggers_actions_ui/public/application/components/builtin_action_types). For more information, see the [UI documentation](../triggers_actions_ui/README.md#create-and-register-new-action-type-ui). \ No newline at end of file +To make this action usable in the Kibana UI, you will need to provide all the UI editing aspects of the action. The existing action type user interfaces are defined in [`x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`](../triggers_actions_ui/public/application/components/builtin_action_types). For more information, see the [UI documentation](../triggers_actions_ui/README.md#create-and-register-new-action-type-ui). From 698a06aad2a1db4162572af26f63a21f916b296c Mon Sep 17 00:00:00 2001 From: Daniil Date: Wed, 24 Feb 2021 11:09:35 +0300 Subject: [PATCH 65/70] Fix text align in Safari (#92374) --- .../public/application/components/visualize_listing.scss | 4 ++++ .../visualize/public/application/utils/get_table_columns.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/visualize/public/application/components/visualize_listing.scss b/src/plugins/visualize/public/application/components/visualize_listing.scss index 8eb2a8911ee02..c3b0df67e317d 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.scss +++ b/src/plugins/visualize/public/application/components/visualize_listing.scss @@ -16,6 +16,10 @@ margin-left: $euiSizeS; } +.visListingTable__titleLink { + text-align: left; +} + .visListingCallout { max-width: 1000px; width: 100%; diff --git a/src/plugins/visualize/public/application/utils/get_table_columns.tsx b/src/plugins/visualize/public/application/utils/get_table_columns.tsx index 930d3b7ce1890..ac8fee452aa6c 100644 --- a/src/plugins/visualize/public/application/utils/get_table_columns.tsx +++ b/src/plugins/visualize/public/application/utils/get_table_columns.tsx @@ -85,7 +85,7 @@ export const getTableColumns = ( render: (field: string, { editApp, editUrl, title, error }: VisualizationListItem) => // In case an error occurs i.e. the vis has wrong type, we render the vis but without the link !error ? ( - + Date: Wed, 24 Feb 2021 13:12:55 +0300 Subject: [PATCH 66/70] [TSVB] Enable `dual mode`, support index patterns and strings (#92395) Part of #91367 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/lib/get_fields.ts | 1 - .../server/lib/get_vis_data.ts | 1 - .../lib/get_index_pattern.ts | 32 +++++++++++++ .../abstract_search_strategy.test.ts | 1 + .../strategies/abstract_search_strategy.ts | 18 ++++--- .../annotations/get_request_params.js | 11 +++-- .../server/lib/vis_data/get_table_data.js | 7 ++- .../lib/vis_data/helpers/get_index_pattern.js | 48 ------------------- .../lib/vis_data/series/get_request_params.js | 7 ++- 9 files changed, 59 insertions(+), 67 deletions(-) create mode 100644 src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/get_index_pattern.ts delete mode 100644 src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_index_pattern.js diff --git a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts b/src/plugins/vis_type_timeseries/server/lib/get_fields.ts index 4b58624a7d1a4..dd74a6c780f4b 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_fields.ts @@ -47,7 +47,6 @@ export async function getFields( ), }, getUiSettingsService: () => requestContext.core.uiSettings.client, - getSavedObjectsClient: () => requestContext.core.savedObjects.client, getEsShardTimeout: async () => { return await framework.globalConfig$ .pipe( diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts index 4c4e1d4603763..f99d02d00b2ef 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts @@ -52,7 +52,6 @@ export function getVisData( pre: {}, payload: request.body, getUiSettingsService: () => requestContext.core.uiSettings.client, - getSavedObjectsClient: () => requestContext.core.savedObjects.client, getEsShardTimeout: async () => { return await framework.globalConfig$ .pipe( diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/get_index_pattern.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/get_index_pattern.ts new file mode 100644 index 0000000000000..512494de290fd --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/get_index_pattern.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { IndexPatternsService, IndexPattern } from '../../../../../data/server'; + +interface IndexPatternObjectDependencies { + indexPatternsService: IndexPatternsService; +} +export async function getIndexPatternObject( + indexPatternString: string, + { indexPatternsService }: IndexPatternObjectDependencies +) { + let indexPatternObject: IndexPattern | undefined | null; + + if (!indexPatternString) { + indexPatternObject = await indexPatternsService.getDefault(); + } else { + indexPatternObject = (await indexPatternsService.find(indexPatternString)).find( + (index) => index.title === indexPatternString + ); + } + + return { + indexPatternObject, + indexPatternString: indexPatternObject?.title || indexPatternString || '', + }; +} diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts index 017fa6837f562..774ef19ef1cd7 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts @@ -36,6 +36,7 @@ describe('AbstractSearchStrategy', () => { getIndexPatternsService: jest.fn(() => Promise.resolve({ find: jest.fn(() => []), + getDefault: jest.fn(() => {}), }) ), } as unknown) as ReqFacade; diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts index 620f494021f0f..dec0330bde6fc 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { FakeRequest, IUiSettingsClient, SavedObjectsClientContract } from 'kibana/server'; +import type { FakeRequest, IUiSettingsClient } from 'kibana/server'; import { indexPatterns, IndexPatternsFetcher } from '../../../../../data/server'; @@ -14,6 +14,7 @@ import type { Framework } from '../../../plugin'; import type { FieldSpec, IndexPatternsService } from '../../../../../data/common'; import type { VisPayload, SanitizedFieldType } from '../../../../common/types'; import type { VisTypeTimeseriesRequestHandlerContext } from '../../../types'; +import { getIndexPatternObject } from '../lib/get_index_pattern'; /** * ReqFacade is a regular KibanaRequest object extended with additional service @@ -29,7 +30,6 @@ export interface ReqFacade extends FakeRequest { indexPatternsFetcher?: IndexPatternsFetcher; }; getUiSettingsService: () => IUiSettingsClient; - getSavedObjectsClient: () => SavedObjectsClientContract; getEsShardTimeout: () => Promise; getIndexPatternsService: () => Promise; } @@ -89,16 +89,14 @@ export abstract class AbstractSearchStrategy { rollupIndex: string; }> ) { - const { indexPatternsFetcher } = req.pre; - const indexPatternsService = await req.getIndexPatternsService(); - const kibanaIndexPattern = (await indexPatternsService.find(indexPattern)).find( - (index) => index.title === indexPattern - ); + const { indexPatternObject } = await getIndexPatternObject(indexPattern, { + indexPatternsService: await req.getIndexPatternsService(), + }); return toSanitizedFieldType( - kibanaIndexPattern - ? kibanaIndexPattern.getNonScriptedFields() - : await indexPatternsFetcher!.getFieldsForWildcard({ + indexPatternObject + ? indexPatternObject.getNonScriptedFields() + : await req.pre.indexPatternsFetcher!.getFieldsForWildcard({ pattern: indexPattern, fieldCapsOptions: { allow_no_indices: true }, metaFields: [], diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.js index b3287957801a3..49697205a8582 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.js @@ -8,7 +8,7 @@ import { buildAnnotationRequest } from './build_request_body'; import { getEsShardTimeout } from '../helpers/get_es_shard_timeout'; -import { getIndexPatternObject } from '../helpers/get_index_pattern'; +import { getIndexPatternObject } from '../../search_strategies/lib/get_index_pattern'; export async function getAnnotationRequestParams( req, @@ -19,8 +19,13 @@ export async function getAnnotationRequestParams( ) { const uiSettings = req.getUiSettingsService(); const esShardTimeout = await getEsShardTimeout(req); - const indexPattern = annotation.index_pattern; - const { indexPatternObject, indexPatternString } = await getIndexPatternObject(req, indexPattern); + const { indexPatternObject, indexPatternString } = await getIndexPatternObject( + annotation.index_pattern, + { + indexPatternsService: await req.getIndexPatternsService(), + } + ); + const request = await buildAnnotationRequest( req, panel, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.js index 97015db5d435c..e0604145eef33 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.js @@ -11,7 +11,7 @@ import { handleErrorResponse } from './handle_error_response'; import { get } from 'lodash'; import { processBucket } from './table/process_bucket'; import { getEsQueryConfig } from './helpers/get_es_query_uisettings'; -import { getIndexPatternObject } from './helpers/get_index_pattern'; +import { getIndexPatternObject } from '../search_strategies/lib/get_index_pattern'; import { createFieldsFetcher } from './helpers/fields_fetcher'; import { extractFieldLabel } from '../../../common/calculate_label'; @@ -23,7 +23,10 @@ export async function getTableData(req, panel) { capabilities, } = await req.framework.searchStrategyRegistry.getViableStrategy(req, panelIndexPattern); const esQueryConfig = await getEsQueryConfig(req); - const { indexPatternObject } = await getIndexPatternObject(req, panelIndexPattern); + const { indexPatternObject } = await getIndexPatternObject(panelIndexPattern, { + indexPatternsService: await req.getIndexPatternsService(), + }); + const extractFields = createFieldsFetcher(req, searchStrategy, capabilities); const calculatePivotLabel = async () => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_index_pattern.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_index_pattern.js deleted file mode 100644 index bddd7a7011612..0000000000000 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_index_pattern.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 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 { get } from 'lodash'; - -export async function getIndexPatternObject(req, indexPatternString) { - let defaultIndex; - - if (!indexPatternString) { - defaultIndex = await req.getUiSettingsService().get('defaultIndex'); - } - - // getting the matching index pattern - const savedObjectClient = req.getSavedObjectsClient(); - const indexPatternObjects = await savedObjectClient.find({ - type: 'index-pattern', - fields: ['title', 'fields', 'timeFieldName'], - search: indexPatternString ? `"${indexPatternString}"` : null, - search_fields: ['title'], - }); - - // getting the index pattern fields - const indexPatterns = indexPatternObjects.saved_objects - .filter( - (obj) => - obj.attributes.title === indexPatternString || (defaultIndex && obj.id === defaultIndex) - ) - .map((indexPattern) => { - const { title, fields, timeFieldName } = indexPattern.attributes; - return { - title, - timeFieldName, - fields: JSON.parse(fields), - }; - }); - - const indexPatternObject = indexPatterns.length === 1 ? indexPatterns[0] : null; - - return { - indexPatternObject, - indexPatternString: indexPatternString || get(indexPatternObject, 'title', ''), - }; -} diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.js index cb9561847b90b..ca4676d363a15 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.js @@ -8,13 +8,16 @@ import { buildRequestBody } from './build_request_body'; import { getEsShardTimeout } from '../helpers/get_es_shard_timeout'; -import { getIndexPatternObject } from '../helpers/get_index_pattern'; +import { getIndexPatternObject } from '../../../lib/search_strategies/lib/get_index_pattern'; export async function getSeriesRequestParams(req, panel, series, esQueryConfig, capabilities) { const uiSettings = req.getUiSettingsService(); const indexPattern = (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; - const { indexPatternObject, indexPatternString } = await getIndexPatternObject(req, indexPattern); + + const { indexPatternObject, indexPatternString } = await getIndexPatternObject(indexPattern, { + indexPatternsService: await req.getIndexPatternsService(), + }); const request = await buildRequestBody( req, From f0a9b7af45a5ec33ac20c7d947630f9392f706f0 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 24 Feb 2021 13:36:12 +0200 Subject: [PATCH 67/70] Fix timelion deprecation documentation links (#92576) --- src/core/public/doc_links/doc_links_service.ts | 2 +- src/plugins/timelion/server/plugin.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 23534b3cf9210..7792e25e54d01 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -175,7 +175,7 @@ export class DocLinksService { }, visualize: { guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html`, - timelionDeprecation: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html#timelion-deprecation`, + timelionDeprecation: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/create-panels-with-timelion.html`, lens: `${ELASTIC_WEBSITE_URL}what-is/kibana-lens`, lensPanels: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html#create-panels-with-lens`, maps: `${ELASTIC_WEBSITE_URL}maps`, diff --git a/src/plugins/timelion/server/plugin.ts b/src/plugins/timelion/server/plugin.ts index 2fb3918c17089..66348c572117d 100644 --- a/src/plugins/timelion/server/plugin.ts +++ b/src/plugins/timelion/server/plugin.ts @@ -31,7 +31,7 @@ const showWarningMessageIfTimelionSheetWasFound = (core: CoreStart, logger: Logg ({ total }) => total && logger.warn( - 'Deprecated since 7.0, the Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation.' + 'Deprecated since 7.0, the Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html.' ) ); }; From fd9e159816295b4c642a61634ae5b2310aaaeade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Wed, 24 Feb 2021 12:21:40 +0000 Subject: [PATCH 68/70] [Usage Collection] Remove unused UI Metric APIs (#91620) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/core/TESTING.md | 11 ++++++++--- src/plugins/data/public/public.api.md | 1 - .../delete_field_provider/get_delete_provider.tsx | 7 ++----- .../field_editor_flyout_content_container.tsx | 13 +++---------- src/plugins/usage_collection/public/mocks.tsx | 4 +--- src/plugins/usage_collection/public/plugin.tsx | 15 ++------------- .../public/application/services/ui_metric.ts | 3 ++- 7 files changed, 18 insertions(+), 36 deletions(-) diff --git a/src/core/TESTING.md b/src/core/TESTING.md index a0fd0a6ffc255..ef6db57c1a993 100644 --- a/src/core/TESTING.md +++ b/src/core/TESTING.md @@ -830,6 +830,7 @@ data. ```typescript // src/plugins/myplugin/public/plugin.ts +import { METRIC_TYPE } from '@kbn/analytics'; import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; import { UsageCollectionSetup } from '../../usage_collection/public'; @@ -853,8 +854,10 @@ export class MyPlugin implements Plugin { @@ -1090,8 +1095,8 @@ describe('Plugin', () => { plugin.setup(coreSetup, setupDeps); - expect(usageCollectionSetup.allowTrackUserAgent).toHaveBeenCalledTimes(1); - expect(usageCollectionSetup.allowTrackUserAgent).toHaveBeenCalledWith(true); + expect(usageCollectionSetup.reportUiCounter).toHaveBeenCalledTimes(2); + expect(usageCollectionSetup.reportUiCounter).toHaveBeenCalledWith('my_plugin', METRIC_TYPE.LOADED, 'my_event'); }); }); ``` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 63b766a82e8e6..05e5c8577ebe3 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -60,7 +60,6 @@ import { LocationDescriptorObject } from 'history'; import { Logger } from '@kbn/logging'; import { LogMeta } from '@kbn/logging'; import { MaybePromise } from '@kbn/utility-types'; -import { METRIC_TYPE } from '@kbn/analytics'; import { Moment } from 'moment'; import moment from 'moment'; import { NameList } from 'elasticsearch'; diff --git a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx index c8f1ad9035761..c5d17f4cf8d8d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx @@ -9,6 +9,7 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; +import { METRIC_TYPE } from '@kbn/analytics'; import { NotificationsStart } from 'src/core/public'; import { IndexPattern, UsageCollectionStart } from '../../shared_imports'; import { pluginName } from '../../constants'; @@ -33,11 +34,7 @@ export const getDeleteProvider = ( }); try { - usageCollection.reportUiCounter( - pluginName, - usageCollection.METRIC_TYPE.COUNT, - 'delete_runtime' - ); + usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'delete_runtime'); // eslint-disable-next-line no-empty } catch {} diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index ade25424c2250..e01b3f9bb422c 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -9,6 +9,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { DocLinksStart, NotificationsStart, CoreStart } from 'src/core/public'; import { i18n } from '@kbn/i18n'; +import { METRIC_TYPE } from '@kbn/analytics'; import { IndexPatternField, @@ -98,11 +99,7 @@ export const FieldEditorFlyoutContentContainer = ({ if (fieldTypeToProcess === 'runtime') { try { - usageCollection.reportUiCounter( - pluginName, - usageCollection.METRIC_TYPE.COUNT, - 'save_runtime' - ); + usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); // eslint-disable-next-line no-empty } catch {} // rename an existing runtime field @@ -116,11 +113,7 @@ export const FieldEditorFlyoutContentContainer = ({ }); } else { try { - usageCollection.reportUiCounter( - pluginName, - usageCollection.METRIC_TYPE.COUNT, - 'save_concrete' - ); + usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_concrete'); // eslint-disable-next-line no-empty } catch {} } diff --git a/src/plugins/usage_collection/public/mocks.tsx b/src/plugins/usage_collection/public/mocks.tsx index 72cefaf026490..268ed3316542e 100644 --- a/src/plugins/usage_collection/public/mocks.tsx +++ b/src/plugins/usage_collection/public/mocks.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ApplicationUsageTracker } from '@kbn/analytics'; -import { UsageCollectionSetup, METRIC_TYPE } from '.'; +import { UsageCollectionSetup } from '.'; import { ApplicationUsageContext } from './components/track_application_view'; export type Setup = jest.Mocked; @@ -33,9 +33,7 @@ const createSetupContract = (): Setup => { ), }, applicationUsageTracker: applicationUsageTrackerMock, - allowTrackUserAgent: jest.fn(), reportUiCounter: jest.fn(), - METRIC_TYPE, }; return setupContract; diff --git a/src/plugins/usage_collection/public/plugin.tsx b/src/plugins/usage_collection/public/plugin.tsx index 6d1eb751d907a..79260b17e0c82 100644 --- a/src/plugins/usage_collection/public/plugin.tsx +++ b/src/plugins/usage_collection/public/plugin.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Reporter, METRIC_TYPE, ApplicationUsageTracker } from '@kbn/analytics'; +import { Reporter, ApplicationUsageTracker } from '@kbn/analytics'; import type { Subscription } from 'rxjs'; import React from 'react'; import type { @@ -35,15 +35,12 @@ export interface UsageCollectionSetup { components: { ApplicationUsageTrackingProvider: React.FC; }; - allowTrackUserAgent: (allow: boolean) => void; applicationUsageTracker: IApplicationUsageTracker; reportUiCounter: Reporter['reportUiCounter']; - METRIC_TYPE: typeof METRIC_TYPE; } export interface UsageCollectionStart { reportUiCounter: Reporter['reportUiCounter']; - METRIC_TYPE: typeof METRIC_TYPE; applicationUsageTracker: Pick< ApplicationUsageTracker, 'trackApplicationViewUsage' | 'flushTrackedView' | 'updateViewClickCounter' @@ -57,7 +54,6 @@ export function isUnauthenticated(http: HttpSetup) { export class UsageCollectionPlugin implements Plugin { private applicationUsageTracker?: ApplicationUsageTracker; - private trackUserAgent: boolean = true; private subscriptions: Subscription[] = []; private reporter?: Reporter; private config: PublicConfigType; @@ -88,11 +84,7 @@ export class UsageCollectionPlugin implements Plugin { - this.trackUserAgent = allow; - }, reportUiCounter: this.reporter.reportUiCounter, - METRIC_TYPE, }; } @@ -110,14 +102,11 @@ export class UsageCollectionPlugin implements Plugin Date: Wed, 24 Feb 2021 14:08:26 +0100 Subject: [PATCH 69/70] Add metricbeat steps to jenkins_build_load_testing.sh (#90290) * Update jenkins_build_load_testing.sh * Change variables name * Change to tar.gz package * change install location * Rename folder * Debug and new folder name * fix debug * Fix path not changed * more logging * add cat logs * more logging and skipping tests for debug * Update jenkins_build_load_testing.sh * Update jenkins_build_load_testing.sh * Fix kibana_dir var * Change log file path * Update jenkins_build_load_testing.sh * Update jenkins_build_load_testing.sh * Fix metricbeat start * Remove extra ls logging * Changing to MB 7.11.0 * Fix package name * Fix wrong copy * Add vault credentials for stats * Adding system data and removing log * Update jenkins_build_load_testing.sh Update script * Update jenkins_build_load_testing.sh Remove `ls` commands Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dmitry --- test/scripts/jenkins_build_load_testing.sh | 54 ++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/test/scripts/jenkins_build_load_testing.sh b/test/scripts/jenkins_build_load_testing.sh index 659321f1d3975..a635e34bcbeda 100755 --- a/test/scripts/jenkins_build_load_testing.sh +++ b/test/scripts/jenkins_build_load_testing.sh @@ -15,10 +15,48 @@ if [[ ! "$TASK_QUEUE_PROCESS_ID" ]]; then ./test/scripts/jenkins_xpack_build_plugins.sh fi +echo " -> Configure Metricbeat monitoring" +# Configure Metricbeat monitoring for Kibana and ElasticSearch, ingest monitoring data into Kibana Stats cluster +# Getting the URL +TOP="$(curl -L http://snapshots.elastic.co/latest/master.json)" +MB_BUILD=$(echo $TOP | sed 's/.*"version" : "\(.*\)", "build_id.*/\1/') +echo $MB_BUILD +MB_BUILD_ID=$(echo $TOP | sed 's/.*"build_id" : "\(.*\)", "manifest_url.*/\1/') + +URL=https://snapshots.elastic.co/${MB_BUILD_ID}/downloads/beats/metricbeat/metricbeat-${MB_BUILD}-linux-x86_64.tar.gz +URL=https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.11.0-linux-x86_64.tar.gz +echo $URL +# Downloading the Metricbeat package +while [ 1 ]; do + wget -q --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 -t 0 --continue --no-check-certificate --tries=3 $URL + if [ $? = 0 ]; then break; fi; # check return value, break if successful (0) + sleep 1s; +done; + +# Install Metricbeat +echo "untar metricbeat and config" +#tar -xzf metricbeat-${MB_BUILD}-linux-x86_64.tar.gz +tar -xzf metricbeat-7.11.0-linux-x86_64.tar.gz +#mv metricbeat-${MB_BUILD}-linux-x86_64 metricbeat-install +mv metricbeat-7.11.0-linux-x86_64 metricbeat-install + +# Configure Metricbeat +echo " -> Changing metricbeat config" +pushd ../kibana-load-testing +cp cfg/metricbeat/elasticsearch-xpack.yml $KIBANA_DIR/metricbeat-install/modules.d/elasticsearch-xpack.yml +cp cfg/metricbeat/kibana-xpack.yml $KIBANA_DIR/metricbeat-install/modules.d/kibana-xpack.yml +echo "fields.build: ${BUILD_ID}" >> cfg/metricbeat/metricbeat.yml +echo "path.config: ${KIBANA_DIR}/metricbeat-install" >> cfg/metricbeat/metricbeat.yml +echo "cloud.auth: ${USER_FROM_VAULT}:${PASS_FROM_VAULT}" >> cfg/metricbeat/metricbeat.yml +cp cfg/metricbeat/metricbeat.yml $KIBANA_DIR/metricbeat-install/metricbeat.yml +# Disable system monitoring: enabled for now to have more data +#mv $KIBANA_DIR/metricbeat-install/modules.d/system.yml $KIBANA_DIR/metricbeat-install/modules.d/system.yml.disabled +popd + # doesn't persist, also set in kibanaPipeline.groovy export KBN_NP_PLUGINS_BUILT=true -echo " -> building and extracting default Kibana distributable for use in functional tests" +echo " -> Building and extracting default Kibana distributable for use in functional tests" cd "$KIBANA_DIR" node scripts/build --debug --no-oss linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" @@ -29,11 +67,21 @@ tar -xzf "$linuxBuild" -C "$installDir" --strip=1 mkdir -p "$WORKSPACE/kibana-build-xpack" cp -pR install/kibana/. $WORKSPACE/kibana-build-xpack/ -echo " -> test setup" +echo " -> Setup env for tests" source test/scripts/jenkins_test_setup_xpack.sh -echo " -> run gatling load testing" +# Start Metricbeat +echo " -> Starting metricbeat" +pushd $KIBANA_DIR/metricbeat-install +nohup ./metricbeat > metricbeat.log 2>&1 & +popd + +echo " -> Running gatling load testing" export GATLING_SIMULATIONS="$simulations" node scripts/functional_tests \ --kibana-install-dir "$KIBANA_INSTALL_DIR" \ --config test/load/config.ts + +# Show output of Metricbeat. Disabled. Enable for debug purposes +#echo "output of metricbeat.log" +#cat $KIBANA_DIR/metricbeat-install/metricbeat.log From fdc637df83610d2fbe9dba61bd30257076dbf154 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 24 Feb 2021 14:50:30 +0100 Subject: [PATCH 70/70] [Discover] Fix sorting by _score behavior (#92132) --- .../discover/public/__mocks__/config.ts | 5 +- .../angular/doc_table/actions/columns.test.ts | 78 +++++++++++++++++++ .../angular/doc_table/actions/columns.ts | 10 ++- .../application/components/discover.tsx | 3 +- 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 src/plugins/discover/public/application/angular/doc_table/actions/columns.test.ts diff --git a/src/plugins/discover/public/__mocks__/config.ts b/src/plugins/discover/public/__mocks__/config.ts index a79c56aa77f3b..977dc5699a57a 100644 --- a/src/plugins/discover/public/__mocks__/config.ts +++ b/src/plugins/discover/public/__mocks__/config.ts @@ -6,12 +6,15 @@ * Side Public License, v 1. */ -import { IUiSettingsClient } from '../../../../core/public'; +import { IUiSettingsClient } from 'kibana/public'; +import { SORT_DEFAULT_ORDER_SETTING } from '../../common'; export const configMock = ({ get: (key: string) => { if (key === 'defaultIndex') { return 'the-index-pattern-id'; + } else if (key === SORT_DEFAULT_ORDER_SETTING) { + return 'desc'; } return ''; diff --git a/src/plugins/discover/public/application/angular/doc_table/actions/columns.test.ts b/src/plugins/discover/public/application/angular/doc_table/actions/columns.test.ts new file mode 100644 index 0000000000000..43044d08f6ac0 --- /dev/null +++ b/src/plugins/discover/public/application/angular/doc_table/actions/columns.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { getStateColumnActions } from './columns'; +import { configMock } from '../../../../__mocks__/config'; +import { indexPatternMock } from '../../../../__mocks__/index_pattern'; +import { indexPatternsMock } from '../../../../__mocks__/index_patterns'; +import { Capabilities } from '../../../../../../../core/types'; +import { AppState } from '../../discover_state'; + +function getStateColumnAction(state: {}, setAppState: (state: Partial) => void) { + return getStateColumnActions({ + capabilities: ({ + discover: { + save: false, + }, + } as unknown) as Capabilities, + config: configMock, + indexPattern: indexPatternMock, + indexPatterns: indexPatternsMock, + useNewFieldsApi: true, + setAppState, + state, + }); +} + +describe('Test column actions', () => { + test('getStateColumnActions with empty state', () => { + const setAppState = jest.fn(); + const actions = getStateColumnAction({}, setAppState); + + actions.onAddColumn('_score'); + expect(setAppState).toHaveBeenCalledWith({ columns: ['_score'], sort: [['_score', 'desc']] }); + actions.onAddColumn('test'); + expect(setAppState).toHaveBeenCalledWith({ columns: ['test'] }); + }); + test('getStateColumnActions with columns and sort in state', () => { + const setAppState = jest.fn(); + const actions = getStateColumnAction( + { columns: ['first', 'second'], sort: [['first', 'desc']] }, + setAppState + ); + + actions.onAddColumn('_score'); + expect(setAppState).toHaveBeenCalledWith({ + columns: ['first', 'second', '_score'], + sort: [['first', 'desc']], + }); + setAppState.mockClear(); + actions.onAddColumn('third'); + expect(setAppState).toHaveBeenCalledWith({ + columns: ['first', 'second', 'third'], + sort: [['first', 'desc']], + }); + setAppState.mockClear(); + actions.onRemoveColumn('first'); + expect(setAppState).toHaveBeenCalledWith({ + columns: ['second'], + sort: [], + }); + setAppState.mockClear(); + actions.onSetColumns(['first', 'second', 'third']); + expect(setAppState).toHaveBeenCalledWith({ + columns: ['first', 'second', 'third'], + }); + setAppState.mockClear(); + + actions.onMoveColumn('second', 0); + expect(setAppState).toHaveBeenCalledWith({ + columns: ['second', 'first'], + }); + }); +}); diff --git a/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts b/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts index 53ced59b17c5d..8028aa6c08634 100644 --- a/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts +++ b/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts @@ -5,10 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Capabilities } from 'kibana/public'; +import { Capabilities, IUiSettingsClient } from 'kibana/public'; import { popularizeField } from '../../../helpers/popularize_field'; import { IndexPattern, IndexPatternsContract } from '../../../../kibana_services'; import { AppState } from '../../discover_state'; +import { SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; /** * Helper function to provide a fallback to a single _source column if the given array of columns @@ -54,6 +55,7 @@ export function moveColumn(columns: string[], columnName: string, newIndex: numb export function getStateColumnActions({ capabilities, + config, indexPattern, indexPatterns, useNewFieldsApi, @@ -61,6 +63,7 @@ export function getStateColumnActions({ state, }: { capabilities: Capabilities; + config: IUiSettingsClient; indexPattern: IndexPattern; indexPatterns: IndexPatternsContract; useNewFieldsApi: boolean; @@ -72,7 +75,10 @@ export function getStateColumnActions({ popularizeField(indexPattern, columnName, indexPatterns); } const columns = addColumn(state.columns || [], columnName, useNewFieldsApi); - setAppState({ columns }); + const defaultOrder = config.get(SORT_DEFAULT_ORDER_SETTING); + const sort = + columnName === '_score' && !state.sort?.length ? [['_score', defaultOrder]] : state.sort; + setAppState({ columns, sort }); } function onRemoveColumn(columnName: string) { diff --git a/src/plugins/discover/public/application/components/discover.tsx b/src/plugins/discover/public/application/components/discover.tsx index d0c839aac5a6c..1c4a5be2f2b24 100644 --- a/src/plugins/discover/public/application/components/discover.tsx +++ b/src/plugins/discover/public/application/components/discover.tsx @@ -104,13 +104,14 @@ export function Discover({ () => getStateColumnActions({ capabilities, + config, indexPattern, indexPatterns, setAppState, state, useNewFieldsApi, }), - [capabilities, indexPattern, indexPatterns, setAppState, state, useNewFieldsApi] + [capabilities, config, indexPattern, indexPatterns, setAppState, state, useNewFieldsApi] ); const onOpenInspector = useCallback(() => {

yC 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 Xvz3*0pMmWG|J1K(U(LIGEBOBb2LF@M literal 95857 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 R$$tSqh^m%KiIREX{{d1{>$Ly? diff --git a/docs/user/alerting/images/alert-flyout-action-variables.png b/docs/user/alerting/images/alert-flyout-action-variables.png index 73fc1fe9dea6364dde29e335339178f1147214e6..5b3684ad3fae481013ae5206ba4228243b3de0fa 100644 GIT binary patch literal 271027 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%0DEOxJO^S+t_Dbeq)Fn|AmM@_==+&G!kh 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