From f8f17116889f5fcb2f8766d0dceafd1d8b7f231c Mon Sep 17 00:00:00 2001
From: Steph Milovic
Date: Fri, 18 Dec 2020 09:23:09 -0700
Subject: [PATCH 01/40] [Security Solution] [Cases] Follow up jest tests for
#84587 (#86231)
---
.../case/common/api/connectors/mappings.ts | 2 +
.../client/configure/get_fields.test.ts | 60 ++
.../client/configure/get_mappings.test.ts | 55 ++
.../case/server/client/configure/mock.ts | 625 ++++++++++++++++++
.../server/client/configure/utils.test.ts | 545 +--------------
.../api/__fixtures__/mock_saved_objects.ts | 24 +-
.../routes/api/__mocks__/request_responses.ts | 24 +-
.../api/cases/configure/get_configure.test.ts | 7 +-
.../routes/api/cases/configure/get_fields.ts | 70 --
.../server/routes/api/cases/configure/mock.ts | 35 +-
.../configure/post_push_to_service.test.ts | 104 +++
.../cases/configure/post_push_to_service.ts | 2 +-
.../routes/api/cases/configure/utils.test.ts | 174 ++++-
.../routes/api/cases/configure/utils.ts | 6 +-
.../plugins/case/server/routes/api/index.ts | 2 -
.../public/cases/containers/api.ts | 19 -
.../cases/containers/use_get_fields.tsx | 82 ---
17 files changed, 1087 insertions(+), 749 deletions(-)
create mode 100644 x-pack/plugins/case/server/client/configure/get_fields.test.ts
create mode 100644 x-pack/plugins/case/server/client/configure/get_mappings.test.ts
create mode 100644 x-pack/plugins/case/server/client/configure/mock.ts
delete mode 100644 x-pack/plugins/case/server/routes/api/cases/configure/get_fields.ts
create mode 100644 x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts
delete mode 100644 x-pack/plugins/security_solution/public/cases/containers/use_get_fields.tsx
diff --git a/x-pack/plugins/case/common/api/connectors/mappings.ts b/x-pack/plugins/case/common/api/connectors/mappings.ts
index 3e8baf0af2834..b91f9d69e85e2 100644
--- a/x-pack/plugins/case/common/api/connectors/mappings.ts
+++ b/x-pack/plugins/case/common/api/connectors/mappings.ts
@@ -180,6 +180,8 @@ export const PostPushRequestRt = rt.type({
params: ServiceConnectorCaseParamsRt,
});
+export type PostPushRequest = rt.TypeOf;
+
export interface SimpleComment {
comment: string;
commentId: string;
diff --git a/x-pack/plugins/case/server/client/configure/get_fields.test.ts b/x-pack/plugins/case/server/client/configure/get_fields.test.ts
new file mode 100644
index 0000000000000..b465d916b2292
--- /dev/null
+++ b/x-pack/plugins/case/server/client/configure/get_fields.test.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ConnectorTypes } from '../../../common/api';
+
+import { createMockSavedObjectsRepository, mockCaseMappings } from '../../routes/api/__fixtures__';
+import { createCaseClientWithMockSavedObjectsClient } from '../mocks';
+import { actionsClientMock } from '../../../../actions/server/actions_client.mock';
+import { actionsErrResponse, mappings, mockGetFieldsResponse } from './mock';
+describe('get_fields', () => {
+ const execute = jest.fn().mockReturnValue(mockGetFieldsResponse);
+ const actionsMock = { ...actionsClientMock.create(), execute };
+ beforeEach(async () => {
+ jest.clearAllMocks();
+ });
+
+ describe('happy path', () => {
+ test('it gets fields', async () => {
+ const savedObjectsClient = createMockSavedObjectsRepository({
+ caseMappingsSavedObject: mockCaseMappings,
+ });
+ const caseClient = await createCaseClientWithMockSavedObjectsClient(savedObjectsClient);
+ const res = await caseClient.client.getFields({
+ actionsClient: actionsMock,
+ connectorType: ConnectorTypes.jira,
+ connectorId: '123',
+ });
+ expect(res).toEqual({
+ fields: [
+ { id: 'summary', name: 'Summary', required: true, type: 'text' },
+ { id: 'description', name: 'Description', required: false, type: 'text' },
+ ],
+ defaultMappings: mappings[ConnectorTypes.jira],
+ });
+ });
+ });
+
+ describe('unhappy path', () => {
+ test('it throws error', async () => {
+ const savedObjectsClient = createMockSavedObjectsRepository({
+ caseMappingsSavedObject: mockCaseMappings,
+ });
+ const caseClient = await createCaseClientWithMockSavedObjectsClient(savedObjectsClient);
+ await caseClient.client
+ .getFields({
+ actionsClient: { ...actionsMock, execute: jest.fn().mockReturnValue(actionsErrResponse) },
+ connectorType: ConnectorTypes.jira,
+ connectorId: '123',
+ })
+ .catch((e) => {
+ expect(e).not.toBeNull();
+ expect(e.isBoom).toBe(true);
+ expect(e.output.statusCode).toBe(424);
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/case/server/client/configure/get_mappings.test.ts b/x-pack/plugins/case/server/client/configure/get_mappings.test.ts
new file mode 100644
index 0000000000000..e68db5cde940b
--- /dev/null
+++ b/x-pack/plugins/case/server/client/configure/get_mappings.test.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ConnectorTypes } from '../../../common/api';
+
+import { createMockSavedObjectsRepository, mockCaseMappings } from '../../routes/api/__fixtures__';
+import { createCaseClientWithMockSavedObjectsClient } from '../mocks';
+import { actionsClientMock } from '../../../../actions/server/actions_client.mock';
+import { mappings, mockGetFieldsResponse } from './mock';
+
+describe('get_mappings', () => {
+ const execute = jest.fn().mockReturnValue(mockGetFieldsResponse);
+ const actionsMock = { ...actionsClientMock.create(), execute };
+ beforeEach(async () => {
+ jest.restoreAllMocks();
+ const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>;
+ spyOnDate.mockImplementation(() => ({
+ toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'),
+ }));
+ });
+
+ describe('happy path', () => {
+ test('it gets existing mappings', async () => {
+ const savedObjectsClient = createMockSavedObjectsRepository({
+ caseMappingsSavedObject: mockCaseMappings,
+ });
+ const caseClient = await createCaseClientWithMockSavedObjectsClient(savedObjectsClient);
+ const res = await caseClient.client.getMappings({
+ actionsClient: actionsMock,
+ caseClient: caseClient.client,
+ connectorType: ConnectorTypes.jira,
+ connectorId: '123',
+ });
+
+ expect(res).toEqual(mappings[ConnectorTypes.jira]);
+ });
+ test('it creates new mappings', async () => {
+ const savedObjectsClient = createMockSavedObjectsRepository({
+ caseMappingsSavedObject: [],
+ });
+ const caseClient = await createCaseClientWithMockSavedObjectsClient(savedObjectsClient);
+ const res = await caseClient.client.getMappings({
+ actionsClient: actionsMock,
+ caseClient: caseClient.client,
+ connectorType: ConnectorTypes.jira,
+ connectorId: '123',
+ });
+
+ expect(res).toEqual(mappings[ConnectorTypes.jira]);
+ });
+ });
+});
diff --git a/x-pack/plugins/case/server/client/configure/mock.ts b/x-pack/plugins/case/server/client/configure/mock.ts
new file mode 100644
index 0000000000000..bb57755250ba2
--- /dev/null
+++ b/x-pack/plugins/case/server/client/configure/mock.ts
@@ -0,0 +1,625 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+ ConnectorField,
+ ConnectorMappingsAttributes,
+ ConnectorTypes,
+} from '../../../common/api/connectors';
+import {
+ JiraGetFieldsResponse,
+ ResilientGetFieldsResponse,
+ ServiceNowGetFieldsResponse,
+} from './utils.test';
+interface TestMappings {
+ [key: string]: ConnectorMappingsAttributes[];
+}
+export const mappings: TestMappings = {
+ [ConnectorTypes.jira]: [
+ {
+ source: 'title',
+ target: 'summary',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'description',
+ target: 'description',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'comments',
+ target: 'comments',
+ action_type: 'append',
+ },
+ ],
+ [`${ConnectorTypes.jira}-alt`]: [
+ {
+ source: 'title',
+ target: 'title',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'description',
+ target: 'description',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'comments',
+ target: 'comments',
+ action_type: 'append',
+ },
+ ],
+ [ConnectorTypes.resilient]: [
+ {
+ source: 'title',
+ target: 'name',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'description',
+ target: 'description',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'comments',
+ target: 'comments',
+ action_type: 'append',
+ },
+ ],
+ [ConnectorTypes.servicenow]: [
+ {
+ source: 'title',
+ target: 'short_description',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'description',
+ target: 'description',
+ action_type: 'overwrite',
+ },
+ {
+ source: 'comments',
+ target: 'comments',
+ action_type: 'append',
+ },
+ ],
+};
+
+const jiraFields: JiraGetFieldsResponse = {
+ summary: {
+ required: true,
+ allowedValues: [],
+ defaultValue: {},
+ schema: {
+ type: 'string',
+ },
+ name: 'Summary',
+ },
+ issuetype: {
+ required: true,
+ allowedValues: [
+ {
+ self: 'https://siem-kibana.atlassian.net/rest/api/2/issuetype/10023',
+ id: '10023',
+ description: 'A problem or error.',
+ iconUrl:
+ 'https://siem-kibana.atlassian.net/secure/viewavatar?size=medium&avatarId=10303&avatarType=issuetype',
+ name: 'Bug',
+ subtask: false,
+ avatarId: 10303,
+ },
+ ],
+ defaultValue: {},
+ schema: {
+ type: 'issuetype',
+ },
+ name: 'Issue Type',
+ },
+ attachment: {
+ required: false,
+ allowedValues: [],
+ defaultValue: {},
+ schema: {
+ type: 'array',
+ items: 'attachment',
+ },
+ name: 'Attachment',
+ },
+ duedate: {
+ required: false,
+ allowedValues: [],
+ defaultValue: {},
+ schema: {
+ type: 'date',
+ },
+ name: 'Due date',
+ },
+ description: {
+ required: false,
+ allowedValues: [],
+ defaultValue: {},
+ schema: {
+ type: 'string',
+ },
+ name: 'Description',
+ },
+ project: {
+ required: true,
+ allowedValues: [
+ {
+ self: 'https://siem-kibana.atlassian.net/rest/api/2/project/10015',
+ id: '10015',
+ key: 'RJ2',
+ name: 'RJ2',
+ projectTypeKey: 'business',
+ simplified: false,
+ avatarUrls: {
+ '48x48':
+ 'https://siem-kibana.atlassian.net/secure/projectavatar?pid=10015&avatarId=10412',
+ '24x24':
+ 'https://siem-kibana.atlassian.net/secure/projectavatar?size=small&s=small&pid=10015&avatarId=10412',
+ '16x16':
+ 'https://siem-kibana.atlassian.net/secure/projectavatar?size=xsmall&s=xsmall&pid=10015&avatarId=10412',
+ '32x32':
+ 'https://siem-kibana.atlassian.net/secure/projectavatar?size=medium&s=medium&pid=10015&avatarId=10412',
+ },
+ },
+ ],
+ defaultValue: {},
+ schema: {
+ type: 'project',
+ },
+ name: 'Project',
+ },
+ assignee: {
+ required: false,
+ allowedValues: [],
+ defaultValue: {},
+ schema: {
+ type: 'user',
+ },
+ name: 'Assignee',
+ },
+ labels: {
+ required: false,
+ allowedValues: [],
+ defaultValue: {},
+ schema: {
+ type: 'array',
+ items: 'string',
+ },
+ name: 'Labels',
+ },
+};
+const resilientFields: ResilientGetFieldsResponse = [
+ { input_type: 'text', name: 'addr', read_only: false, text: 'Address' },
+ {
+ input_type: 'boolean',
+ name: 'alberta_health_risk_assessment',
+ read_only: false,
+ text: 'Alberta Health Risk Assessment',
+ },
+ { input_type: 'number', name: 'hard_liability', read_only: true, text: 'Assessed Liability' },
+ { input_type: 'text', name: 'city', read_only: false, text: 'City' },
+ { input_type: 'select', name: 'country', read_only: false, text: 'Country/Region' },
+ { input_type: 'select_owner', name: 'creator_id', read_only: true, text: 'Created By' },
+ { input_type: 'select', name: 'crimestatus_id', read_only: false, text: 'Criminal Activity' },
+ { input_type: 'boolean', name: 'data_encrypted', read_only: false, text: 'Data Encrypted' },
+ { input_type: 'select', name: 'data_format', read_only: false, text: 'Data Format' },
+ { input_type: 'datetimepicker', name: 'end_date', read_only: true, text: 'Date Closed' },
+ { input_type: 'datetimepicker', name: 'create_date', read_only: true, text: 'Date Created' },
+ {
+ input_type: 'datetimepicker',
+ name: 'determined_date',
+ read_only: false,
+ text: 'Date Determined',
+ },
+ {
+ input_type: 'datetimepicker',
+ name: 'discovered_date',
+ read_only: false,
+ required: 'always',
+ text: 'Date Discovered',
+ },
+ { input_type: 'datetimepicker', name: 'start_date', read_only: false, text: 'Date Occurred' },
+ { input_type: 'select', name: 'exposure_dept_id', read_only: false, text: 'Department' },
+ { input_type: 'textarea', name: 'description', read_only: false, text: 'Description' },
+ { input_type: 'boolean', name: 'employee_involved', read_only: false, text: 'Employee Involved' },
+ { input_type: 'boolean', name: 'data_contained', read_only: false, text: 'Exposure Resolved' },
+ { input_type: 'select', name: 'exposure_type_id', read_only: false, text: 'Exposure Type' },
+ {
+ input_type: 'multiselect',
+ name: 'gdpr_breach_circumstances',
+ read_only: false,
+ text: 'GDPR Breach Circumstances',
+ },
+ { input_type: 'select', name: 'gdpr_breach_type', read_only: false, text: 'GDPR Breach Type' },
+ {
+ input_type: 'textarea',
+ name: 'gdpr_breach_type_comment',
+ read_only: false,
+ text: 'GDPR Breach Type Comment',
+ },
+ { input_type: 'select', name: 'gdpr_consequences', read_only: false, text: 'GDPR Consequences' },
+ {
+ input_type: 'textarea',
+ name: 'gdpr_consequences_comment',
+ read_only: false,
+ text: 'GDPR Consequences Comment',
+ },
+ {
+ input_type: 'select',
+ name: 'gdpr_final_assessment',
+ read_only: false,
+ text: 'GDPR Final Assessment',
+ },
+ {
+ input_type: 'textarea',
+ name: 'gdpr_final_assessment_comment',
+ read_only: false,
+ text: 'GDPR Final Assessment Comment',
+ },
+ {
+ input_type: 'select',
+ name: 'gdpr_identification',
+ read_only: false,
+ text: 'GDPR Identification',
+ },
+ {
+ input_type: 'textarea',
+ name: 'gdpr_identification_comment',
+ read_only: false,
+ text: 'GDPR Identification Comment',
+ },
+ {
+ input_type: 'select',
+ name: 'gdpr_personal_data',
+ read_only: false,
+ text: 'GDPR Personal Data',
+ },
+ {
+ input_type: 'textarea',
+ name: 'gdpr_personal_data_comment',
+ read_only: false,
+ text: 'GDPR Personal Data Comment',
+ },
+ {
+ input_type: 'boolean',
+ name: 'gdpr_subsequent_notification',
+ read_only: false,
+ text: 'GDPR Subsequent Notification',
+ },
+ { input_type: 'number', name: 'id', read_only: true, text: 'ID' },
+ { input_type: 'boolean', name: 'impact_likely', read_only: false, text: 'Impact Likely' },
+ {
+ input_type: 'boolean',
+ name: 'ny_impact_likely',
+ read_only: false,
+ text: 'Impact Likely for New York',
+ },
+ {
+ input_type: 'boolean',
+ name: 'or_impact_likely',
+ read_only: false,
+ text: 'Impact Likely for Oregon',
+ },
+ {
+ input_type: 'boolean',
+ name: 'wa_impact_likely',
+ read_only: false,
+ text: 'Impact Likely for Washington',
+ },
+ { input_type: 'boolean', name: 'confirmed', read_only: false, text: 'Incident Disposition' },
+ { input_type: 'multiselect', name: 'incident_type_ids', read_only: false, text: 'Incident Type' },
+ {
+ input_type: 'text',
+ name: 'exposure_individual_name',
+ read_only: false,
+ text: 'Individual Name',
+ },
+ {
+ input_type: 'select',
+ name: 'harmstatus_id',
+ read_only: false,
+ text: 'Is harm/risk/misuse foreseeable?',
+ },
+ { input_type: 'text', name: 'jurisdiction_name', read_only: false, text: 'Jurisdiction' },
+ {
+ input_type: 'datetimepicker',
+ name: 'inc_last_modified_date',
+ read_only: true,
+ text: 'Last Modified',
+ },
+ {
+ input_type: 'multiselect',
+ name: 'gdpr_lawful_data_processing_categories',
+ read_only: false,
+ text: 'Lawful Data Processing Categories',
+ },
+ { input_type: 'multiselect_members', name: 'members', read_only: false, text: 'Members' },
+ { input_type: 'text', name: 'name', read_only: false, required: 'always', text: 'Name' },
+ { input_type: 'boolean', name: 'negative_pr_likely', read_only: false, text: 'Negative PR' },
+ { input_type: 'datetimepicker', name: 'due_date', read_only: true, text: 'Next Due Date' },
+ {
+ input_type: 'multiselect',
+ name: 'nist_attack_vectors',
+ read_only: false,
+ text: 'NIST Attack Vectors',
+ },
+ { input_type: 'select', name: 'org_handle', read_only: true, text: 'Organization' },
+ { input_type: 'select_owner', name: 'owner_id', read_only: false, text: 'Owner' },
+ { input_type: 'select', name: 'phase_id', read_only: true, text: 'Phase' },
+ {
+ input_type: 'select',
+ name: 'pipeda_other_factors',
+ read_only: false,
+ text: 'PIPEDA Other Factors',
+ },
+ {
+ input_type: 'textarea',
+ name: 'pipeda_other_factors_comment',
+ read_only: false,
+ text: 'PIPEDA Other Factors Comment',
+ },
+ {
+ input_type: 'select',
+ name: 'pipeda_overall_assessment',
+ read_only: false,
+ text: 'PIPEDA Overall Assessment',
+ },
+ {
+ input_type: 'textarea',
+ name: 'pipeda_overall_assessment_comment',
+ read_only: false,
+ text: 'PIPEDA Overall Assessment Comment',
+ },
+ {
+ input_type: 'select',
+ name: 'pipeda_probability_of_misuse',
+ read_only: false,
+ text: 'PIPEDA Probability of Misuse',
+ },
+ {
+ input_type: 'textarea',
+ name: 'pipeda_probability_of_misuse_comment',
+ read_only: false,
+ text: 'PIPEDA Probability of Misuse Comment',
+ },
+ {
+ input_type: 'select',
+ name: 'pipeda_sensitivity_of_pi',
+ read_only: false,
+ text: 'PIPEDA Sensitivity of PI',
+ },
+ {
+ input_type: 'textarea',
+ name: 'pipeda_sensitivity_of_pi_comment',
+ read_only: false,
+ text: 'PIPEDA Sensitivity of PI Comment',
+ },
+ { input_type: 'text', name: 'reporter', read_only: false, text: 'Reporting Individual' },
+ {
+ input_type: 'select',
+ name: 'resolution_id',
+ read_only: false,
+ required: 'close',
+ text: 'Resolution',
+ },
+ {
+ input_type: 'textarea',
+ name: 'resolution_summary',
+ read_only: false,
+ required: 'close',
+ text: 'Resolution Summary',
+ },
+ { input_type: 'select', name: 'gdpr_harm_risk', read_only: false, text: 'Risk of Harm' },
+ { input_type: 'select', name: 'severity_code', read_only: false, text: 'Severity' },
+ { input_type: 'boolean', name: 'inc_training', read_only: true, text: 'Simulation' },
+ { input_type: 'multiselect', name: 'data_source_ids', read_only: false, text: 'Source of Data' },
+ { input_type: 'select', name: 'state', read_only: false, text: 'State' },
+ { input_type: 'select', name: 'plan_status', read_only: false, text: 'Status' },
+ { input_type: 'select', name: 'exposure_vendor_id', read_only: false, text: 'Vendor' },
+ {
+ input_type: 'boolean',
+ name: 'data_compromised',
+ read_only: false,
+ text: 'Was personal information or personal data involved?',
+ },
+ {
+ input_type: 'select',
+ name: 'workspace',
+ read_only: false,
+ required: 'always',
+ text: 'Workspace',
+ },
+ { input_type: 'text', name: 'zip', read_only: false, text: 'Zip' },
+];
+const serviceNowFields: ServiceNowGetFieldsResponse = [
+ {
+ column_label: 'Approval',
+ mandatory: 'false',
+ max_length: '40',
+ element: 'approval',
+ },
+ {
+ column_label: 'Close notes',
+ mandatory: 'false',
+ max_length: '4000',
+ element: 'close_notes',
+ },
+ {
+ column_label: 'Contact type',
+ mandatory: 'false',
+ max_length: '40',
+ element: 'contact_type',
+ },
+ {
+ column_label: 'Correlation display',
+ mandatory: 'false',
+ max_length: '100',
+ element: 'correlation_display',
+ },
+ {
+ column_label: 'Correlation ID',
+ mandatory: 'false',
+ max_length: '100',
+ element: 'correlation_id',
+ },
+ {
+ column_label: 'Description',
+ mandatory: 'false',
+ max_length: '4000',
+ element: 'description',
+ },
+ {
+ column_label: 'Number',
+ mandatory: 'false',
+ max_length: '40',
+ element: 'number',
+ },
+ {
+ column_label: 'Short description',
+ mandatory: 'false',
+ max_length: '160',
+ element: 'short_description',
+ },
+ {
+ column_label: 'Created by',
+ mandatory: 'false',
+ max_length: '40',
+ element: 'sys_created_by',
+ },
+ {
+ column_label: 'Updated by',
+ mandatory: 'false',
+ max_length: '40',
+ element: 'sys_updated_by',
+ },
+ {
+ column_label: 'Upon approval',
+ mandatory: 'false',
+ max_length: '40',
+ element: 'upon_approval',
+ },
+ {
+ column_label: 'Upon reject',
+ mandatory: 'false',
+ max_length: '40',
+ element: 'upon_reject',
+ },
+];
+interface FormatFieldsTestData {
+ expected: ConnectorField[];
+ fields: JiraGetFieldsResponse | ResilientGetFieldsResponse | ServiceNowGetFieldsResponse;
+ type: ConnectorTypes;
+}
+export const formatFieldsTestData: FormatFieldsTestData[] = [
+ {
+ expected: [
+ { id: 'summary', name: 'Summary', required: true, type: 'text' },
+ { id: 'description', name: 'Description', required: false, type: 'text' },
+ ],
+ fields: jiraFields,
+ type: ConnectorTypes.jira,
+ },
+ {
+ expected: [
+ { id: 'addr', name: 'Address', required: false, type: 'text' },
+ { id: 'city', name: 'City', required: false, type: 'text' },
+ { id: 'description', name: 'Description', required: false, type: 'textarea' },
+ {
+ id: 'gdpr_breach_type_comment',
+ name: 'GDPR Breach Type Comment',
+ required: false,
+ type: 'textarea',
+ },
+ {
+ id: 'gdpr_consequences_comment',
+ name: 'GDPR Consequences Comment',
+ required: false,
+ type: 'textarea',
+ },
+ {
+ id: 'gdpr_final_assessment_comment',
+ name: 'GDPR Final Assessment Comment',
+ required: false,
+ type: 'textarea',
+ },
+ {
+ id: 'gdpr_identification_comment',
+ name: 'GDPR Identification Comment',
+ required: false,
+ type: 'textarea',
+ },
+ {
+ id: 'gdpr_personal_data_comment',
+ name: 'GDPR Personal Data Comment',
+ required: false,
+ type: 'textarea',
+ },
+ { id: 'exposure_individual_name', name: 'Individual Name', required: false, type: 'text' },
+ { id: 'jurisdiction_name', name: 'Jurisdiction', required: false, type: 'text' },
+ { id: 'name', name: 'Name', required: true, type: 'text' },
+ {
+ id: 'pipeda_other_factors_comment',
+ name: 'PIPEDA Other Factors Comment',
+ required: false,
+ type: 'textarea',
+ },
+ {
+ id: 'pipeda_overall_assessment_comment',
+ name: 'PIPEDA Overall Assessment Comment',
+ required: false,
+ type: 'textarea',
+ },
+ {
+ id: 'pipeda_probability_of_misuse_comment',
+ name: 'PIPEDA Probability of Misuse Comment',
+ required: false,
+ type: 'textarea',
+ },
+ {
+ id: 'pipeda_sensitivity_of_pi_comment',
+ name: 'PIPEDA Sensitivity of PI Comment',
+ required: false,
+ type: 'textarea',
+ },
+ { id: 'reporter', name: 'Reporting Individual', required: false, type: 'text' },
+ { id: 'resolution_summary', name: 'Resolution Summary', required: false, type: 'textarea' },
+ { id: 'zip', name: 'Zip', required: false, type: 'text' },
+ ],
+ fields: resilientFields,
+ type: ConnectorTypes.resilient,
+ },
+ {
+ expected: [
+ { id: 'approval', name: 'Approval', required: false, type: 'text' },
+ { id: 'close_notes', name: 'Close notes', required: false, type: 'textarea' },
+ { id: 'contact_type', name: 'Contact type', required: false, type: 'text' },
+ { id: 'correlation_display', name: 'Correlation display', required: false, type: 'text' },
+ { id: 'correlation_id', name: 'Correlation ID', required: false, type: 'text' },
+ { id: 'description', name: 'Description', required: false, type: 'textarea' },
+ { id: 'number', name: 'Number', required: false, type: 'text' },
+ { id: 'short_description', name: 'Short description', required: false, type: 'text' },
+ { id: 'sys_created_by', name: 'Created by', required: false, type: 'text' },
+ { id: 'sys_updated_by', name: 'Updated by', required: false, type: 'text' },
+ { id: 'upon_approval', name: 'Upon approval', required: false, type: 'text' },
+ { id: 'upon_reject', name: 'Upon reject', required: false, type: 'text' },
+ ],
+ fields: serviceNowFields,
+ type: ConnectorTypes.servicenow,
+ },
+];
+export const mockGetFieldsResponse = {
+ status: 'ok',
+ data: jiraFields,
+ actionId: '123',
+};
+
+export const actionsErrResponse = {
+ status: 'error',
+ serviceMessage: 'this is an actions error',
+};
diff --git a/x-pack/plugins/case/server/client/configure/utils.test.ts b/x-pack/plugins/case/server/client/configure/utils.test.ts
index 91c8259cb2c55..f4f0e07742521 100644
--- a/x-pack/plugins/case/server/client/configure/utils.test.ts
+++ b/x-pack/plugins/case/server/client/configure/utils.test.ts
@@ -4,535 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
+export {
JiraGetFieldsResponse,
ResilientGetFieldsResponse,
ServiceNowGetFieldsResponse,
} from '../../../../actions/server/types';
-import { formatFields } from './utils';
+import { createDefaultMapping, formatFields } from './utils';
import { ConnectorTypes } from '../../../common/api/connectors';
+import { mappings, formatFieldsTestData } from './mock';
-const jiraFields: JiraGetFieldsResponse = {
- summary: {
- required: true,
- allowedValues: [],
- defaultValue: {},
- schema: {
- type: 'string',
- },
- name: 'Summary',
- },
- issuetype: {
- required: true,
- allowedValues: [
- {
- self: 'https://siem-kibana.atlassian.net/rest/api/2/issuetype/10023',
- id: '10023',
- description: 'A problem or error.',
- iconUrl:
- 'https://siem-kibana.atlassian.net/secure/viewavatar?size=medium&avatarId=10303&avatarType=issuetype',
- name: 'Bug',
- subtask: false,
- avatarId: 10303,
- },
- ],
- defaultValue: {},
- schema: {
- type: 'issuetype',
- },
- name: 'Issue Type',
- },
- attachment: {
- required: false,
- allowedValues: [],
- defaultValue: {},
- schema: {
- type: 'array',
- items: 'attachment',
- },
- name: 'Attachment',
- },
- duedate: {
- required: false,
- allowedValues: [],
- defaultValue: {},
- schema: {
- type: 'date',
- },
- name: 'Due date',
- },
- description: {
- required: false,
- allowedValues: [],
- defaultValue: {},
- schema: {
- type: 'string',
- },
- name: 'Description',
- },
- project: {
- required: true,
- allowedValues: [
- {
- self: 'https://siem-kibana.atlassian.net/rest/api/2/project/10015',
- id: '10015',
- key: 'RJ2',
- name: 'RJ2',
- projectTypeKey: 'business',
- simplified: false,
- avatarUrls: {
- '48x48':
- 'https://siem-kibana.atlassian.net/secure/projectavatar?pid=10015&avatarId=10412',
- '24x24':
- 'https://siem-kibana.atlassian.net/secure/projectavatar?size=small&s=small&pid=10015&avatarId=10412',
- '16x16':
- 'https://siem-kibana.atlassian.net/secure/projectavatar?size=xsmall&s=xsmall&pid=10015&avatarId=10412',
- '32x32':
- 'https://siem-kibana.atlassian.net/secure/projectavatar?size=medium&s=medium&pid=10015&avatarId=10412',
- },
- },
- ],
- defaultValue: {},
- schema: {
- type: 'project',
- },
- name: 'Project',
- },
- assignee: {
- required: false,
- allowedValues: [],
- defaultValue: {},
- schema: {
- type: 'user',
- },
- name: 'Assignee',
- },
- labels: {
- required: false,
- allowedValues: [],
- defaultValue: {},
- schema: {
- type: 'array',
- items: 'string',
- },
- name: 'Labels',
- },
-};
-const resilientFields: ResilientGetFieldsResponse = [
- { input_type: 'text', name: 'addr', read_only: false, text: 'Address' },
- {
- input_type: 'boolean',
- name: 'alberta_health_risk_assessment',
- read_only: false,
- text: 'Alberta Health Risk Assessment',
- },
- { input_type: 'number', name: 'hard_liability', read_only: true, text: 'Assessed Liability' },
- { input_type: 'text', name: 'city', read_only: false, text: 'City' },
- { input_type: 'select', name: 'country', read_only: false, text: 'Country/Region' },
- { input_type: 'select_owner', name: 'creator_id', read_only: true, text: 'Created By' },
- { input_type: 'select', name: 'crimestatus_id', read_only: false, text: 'Criminal Activity' },
- { input_type: 'boolean', name: 'data_encrypted', read_only: false, text: 'Data Encrypted' },
- { input_type: 'select', name: 'data_format', read_only: false, text: 'Data Format' },
- { input_type: 'datetimepicker', name: 'end_date', read_only: true, text: 'Date Closed' },
- { input_type: 'datetimepicker', name: 'create_date', read_only: true, text: 'Date Created' },
- {
- input_type: 'datetimepicker',
- name: 'determined_date',
- read_only: false,
- text: 'Date Determined',
- },
- {
- input_type: 'datetimepicker',
- name: 'discovered_date',
- read_only: false,
- required: 'always',
- text: 'Date Discovered',
- },
- { input_type: 'datetimepicker', name: 'start_date', read_only: false, text: 'Date Occurred' },
- { input_type: 'select', name: 'exposure_dept_id', read_only: false, text: 'Department' },
- { input_type: 'textarea', name: 'description', read_only: false, text: 'Description' },
- { input_type: 'boolean', name: 'employee_involved', read_only: false, text: 'Employee Involved' },
- { input_type: 'boolean', name: 'data_contained', read_only: false, text: 'Exposure Resolved' },
- { input_type: 'select', name: 'exposure_type_id', read_only: false, text: 'Exposure Type' },
- {
- input_type: 'multiselect',
- name: 'gdpr_breach_circumstances',
- read_only: false,
- text: 'GDPR Breach Circumstances',
- },
- { input_type: 'select', name: 'gdpr_breach_type', read_only: false, text: 'GDPR Breach Type' },
- {
- input_type: 'textarea',
- name: 'gdpr_breach_type_comment',
- read_only: false,
- text: 'GDPR Breach Type Comment',
- },
- { input_type: 'select', name: 'gdpr_consequences', read_only: false, text: 'GDPR Consequences' },
- {
- input_type: 'textarea',
- name: 'gdpr_consequences_comment',
- read_only: false,
- text: 'GDPR Consequences Comment',
- },
- {
- input_type: 'select',
- name: 'gdpr_final_assessment',
- read_only: false,
- text: 'GDPR Final Assessment',
- },
- {
- input_type: 'textarea',
- name: 'gdpr_final_assessment_comment',
- read_only: false,
- text: 'GDPR Final Assessment Comment',
- },
- {
- input_type: 'select',
- name: 'gdpr_identification',
- read_only: false,
- text: 'GDPR Identification',
- },
- {
- input_type: 'textarea',
- name: 'gdpr_identification_comment',
- read_only: false,
- text: 'GDPR Identification Comment',
- },
- {
- input_type: 'select',
- name: 'gdpr_personal_data',
- read_only: false,
- text: 'GDPR Personal Data',
- },
- {
- input_type: 'textarea',
- name: 'gdpr_personal_data_comment',
- read_only: false,
- text: 'GDPR Personal Data Comment',
- },
- {
- input_type: 'boolean',
- name: 'gdpr_subsequent_notification',
- read_only: false,
- text: 'GDPR Subsequent Notification',
- },
- { input_type: 'number', name: 'id', read_only: true, text: 'ID' },
- { input_type: 'boolean', name: 'impact_likely', read_only: false, text: 'Impact Likely' },
- {
- input_type: 'boolean',
- name: 'ny_impact_likely',
- read_only: false,
- text: 'Impact Likely for New York',
- },
- {
- input_type: 'boolean',
- name: 'or_impact_likely',
- read_only: false,
- text: 'Impact Likely for Oregon',
- },
- {
- input_type: 'boolean',
- name: 'wa_impact_likely',
- read_only: false,
- text: 'Impact Likely for Washington',
- },
- { input_type: 'boolean', name: 'confirmed', read_only: false, text: 'Incident Disposition' },
- { input_type: 'multiselect', name: 'incident_type_ids', read_only: false, text: 'Incident Type' },
- {
- input_type: 'text',
- name: 'exposure_individual_name',
- read_only: false,
- text: 'Individual Name',
- },
- {
- input_type: 'select',
- name: 'harmstatus_id',
- read_only: false,
- text: 'Is harm/risk/misuse foreseeable?',
- },
- { input_type: 'text', name: 'jurisdiction_name', read_only: false, text: 'Jurisdiction' },
- {
- input_type: 'datetimepicker',
- name: 'inc_last_modified_date',
- read_only: true,
- text: 'Last Modified',
- },
- {
- input_type: 'multiselect',
- name: 'gdpr_lawful_data_processing_categories',
- read_only: false,
- text: 'Lawful Data Processing Categories',
- },
- { input_type: 'multiselect_members', name: 'members', read_only: false, text: 'Members' },
- { input_type: 'text', name: 'name', read_only: false, required: 'always', text: 'Name' },
- { input_type: 'boolean', name: 'negative_pr_likely', read_only: false, text: 'Negative PR' },
- { input_type: 'datetimepicker', name: 'due_date', read_only: true, text: 'Next Due Date' },
- {
- input_type: 'multiselect',
- name: 'nist_attack_vectors',
- read_only: false,
- text: 'NIST Attack Vectors',
- },
- { input_type: 'select', name: 'org_handle', read_only: true, text: 'Organization' },
- { input_type: 'select_owner', name: 'owner_id', read_only: false, text: 'Owner' },
- { input_type: 'select', name: 'phase_id', read_only: true, text: 'Phase' },
- {
- input_type: 'select',
- name: 'pipeda_other_factors',
- read_only: false,
- text: 'PIPEDA Other Factors',
- },
- {
- input_type: 'textarea',
- name: 'pipeda_other_factors_comment',
- read_only: false,
- text: 'PIPEDA Other Factors Comment',
- },
- {
- input_type: 'select',
- name: 'pipeda_overall_assessment',
- read_only: false,
- text: 'PIPEDA Overall Assessment',
- },
- {
- input_type: 'textarea',
- name: 'pipeda_overall_assessment_comment',
- read_only: false,
- text: 'PIPEDA Overall Assessment Comment',
- },
- {
- input_type: 'select',
- name: 'pipeda_probability_of_misuse',
- read_only: false,
- text: 'PIPEDA Probability of Misuse',
- },
- {
- input_type: 'textarea',
- name: 'pipeda_probability_of_misuse_comment',
- read_only: false,
- text: 'PIPEDA Probability of Misuse Comment',
- },
- {
- input_type: 'select',
- name: 'pipeda_sensitivity_of_pi',
- read_only: false,
- text: 'PIPEDA Sensitivity of PI',
- },
- {
- input_type: 'textarea',
- name: 'pipeda_sensitivity_of_pi_comment',
- read_only: false,
- text: 'PIPEDA Sensitivity of PI Comment',
- },
- { input_type: 'text', name: 'reporter', read_only: false, text: 'Reporting Individual' },
- {
- input_type: 'select',
- name: 'resolution_id',
- read_only: false,
- required: 'close',
- text: 'Resolution',
- },
- {
- input_type: 'textarea',
- name: 'resolution_summary',
- read_only: false,
- required: 'close',
- text: 'Resolution Summary',
- },
- { input_type: 'select', name: 'gdpr_harm_risk', read_only: false, text: 'Risk of Harm' },
- { input_type: 'select', name: 'severity_code', read_only: false, text: 'Severity' },
- { input_type: 'boolean', name: 'inc_training', read_only: true, text: 'Simulation' },
- { input_type: 'multiselect', name: 'data_source_ids', read_only: false, text: 'Source of Data' },
- { input_type: 'select', name: 'state', read_only: false, text: 'State' },
- { input_type: 'select', name: 'plan_status', read_only: false, text: 'Status' },
- { input_type: 'select', name: 'exposure_vendor_id', read_only: false, text: 'Vendor' },
- {
- input_type: 'boolean',
- name: 'data_compromised',
- read_only: false,
- text: 'Was personal information or personal data involved?',
- },
- {
- input_type: 'select',
- name: 'workspace',
- read_only: false,
- required: 'always',
- text: 'Workspace',
- },
- { input_type: 'text', name: 'zip', read_only: false, text: 'Zip' },
-];
-const serviceNowFields: ServiceNowGetFieldsResponse = [
- {
- column_label: 'Approval',
- mandatory: 'false',
- max_length: '40',
- element: 'approval',
- },
- {
- column_label: 'Close notes',
- mandatory: 'false',
- max_length: '4000',
- element: 'close_notes',
- },
- {
- column_label: 'Contact type',
- mandatory: 'false',
- max_length: '40',
- element: 'contact_type',
- },
- {
- column_label: 'Correlation display',
- mandatory: 'false',
- max_length: '100',
- element: 'correlation_display',
- },
- {
- column_label: 'Correlation ID',
- mandatory: 'false',
- max_length: '100',
- element: 'correlation_id',
- },
- {
- column_label: 'Description',
- mandatory: 'false',
- max_length: '4000',
- element: 'description',
- },
- {
- column_label: 'Number',
- mandatory: 'false',
- max_length: '40',
- element: 'number',
- },
- {
- column_label: 'Short description',
- mandatory: 'false',
- max_length: '160',
- element: 'short_description',
- },
- {
- column_label: 'Created by',
- mandatory: 'false',
- max_length: '40',
- element: 'sys_created_by',
- },
- {
- column_label: 'Updated by',
- mandatory: 'false',
- max_length: '40',
- element: 'sys_updated_by',
- },
- {
- column_label: 'Upon approval',
- mandatory: 'false',
- max_length: '40',
- element: 'upon_approval',
- },
- {
- column_label: 'Upon reject',
- mandatory: 'false',
- max_length: '40',
- element: 'upon_reject',
- },
-];
-
-const formatFieldsTestData = [
- {
- expected: [
- { id: 'summary', name: 'Summary', required: true, type: 'text' },
- { id: 'description', name: 'Description', required: false, type: 'text' },
- ],
- fields: jiraFields,
- type: ConnectorTypes.jira,
- },
- {
- expected: [
- { id: 'addr', name: 'Address', required: false, type: 'text' },
- { id: 'city', name: 'City', required: false, type: 'text' },
- { id: 'description', name: 'Description', required: false, type: 'textarea' },
- {
- id: 'gdpr_breach_type_comment',
- name: 'GDPR Breach Type Comment',
- required: false,
- type: 'textarea',
- },
- {
- id: 'gdpr_consequences_comment',
- name: 'GDPR Consequences Comment',
- required: false,
- type: 'textarea',
- },
- {
- id: 'gdpr_final_assessment_comment',
- name: 'GDPR Final Assessment Comment',
- required: false,
- type: 'textarea',
- },
- {
- id: 'gdpr_identification_comment',
- name: 'GDPR Identification Comment',
- required: false,
- type: 'textarea',
- },
- {
- id: 'gdpr_personal_data_comment',
- name: 'GDPR Personal Data Comment',
- required: false,
- type: 'textarea',
- },
- { id: 'exposure_individual_name', name: 'Individual Name', required: false, type: 'text' },
- { id: 'jurisdiction_name', name: 'Jurisdiction', required: false, type: 'text' },
- { id: 'name', name: 'Name', required: true, type: 'text' },
- {
- id: 'pipeda_other_factors_comment',
- name: 'PIPEDA Other Factors Comment',
- required: false,
- type: 'textarea',
- },
- {
- id: 'pipeda_overall_assessment_comment',
- name: 'PIPEDA Overall Assessment Comment',
- required: false,
- type: 'textarea',
- },
- {
- id: 'pipeda_probability_of_misuse_comment',
- name: 'PIPEDA Probability of Misuse Comment',
- required: false,
- type: 'textarea',
- },
- {
- id: 'pipeda_sensitivity_of_pi_comment',
- name: 'PIPEDA Sensitivity of PI Comment',
- required: false,
- type: 'textarea',
- },
- { id: 'reporter', name: 'Reporting Individual', required: false, type: 'text' },
- { id: 'resolution_summary', name: 'Resolution Summary', required: false, type: 'textarea' },
- { id: 'zip', name: 'Zip', required: false, type: 'text' },
- ],
- fields: resilientFields,
- type: ConnectorTypes.resilient,
- },
- {
- expected: [
- { id: 'approval', name: 'Approval', required: false, type: 'text' },
- { id: 'close_notes', name: 'Close notes', required: false, type: 'textarea' },
- { id: 'contact_type', name: 'Contact type', required: false, type: 'text' },
- { id: 'correlation_display', name: 'Correlation display', required: false, type: 'text' },
- { id: 'correlation_id', name: 'Correlation ID', required: false, type: 'text' },
- { id: 'description', name: 'Description', required: false, type: 'textarea' },
- { id: 'number', name: 'Number', required: false, type: 'text' },
- { id: 'short_description', name: 'Short description', required: false, type: 'text' },
- { id: 'sys_created_by', name: 'Created by', required: false, type: 'text' },
- { id: 'sys_updated_by', name: 'Updated by', required: false, type: 'text' },
- { id: 'upon_approval', name: 'Upon approval', required: false, type: 'text' },
- { id: 'upon_reject', name: 'Upon reject', required: false, type: 'text' },
- ],
- fields: serviceNowFields,
- type: ConnectorTypes.servicenow,
- },
-];
describe('client/configure/utils', () => {
describe('formatFields', () => {
formatFieldsTestData.forEach(({ expected, fields, type }) => {
@@ -542,4 +22,23 @@ describe('client/configure/utils', () => {
});
});
});
+ describe('createDefaultMapping', () => {
+ formatFieldsTestData.forEach(({ expected, fields, type }) => {
+ it(`normalizes ${type} fields to common type ConnectorField`, () => {
+ const result = createDefaultMapping(expected, type);
+ expect(result).toEqual(mappings[type]);
+ });
+ });
+ it(`if the preferredField is not required and another field is, use the other field`, () => {
+ const result = createDefaultMapping(
+ [
+ { id: 'summary', name: 'Summary', required: false, type: 'text' },
+ { id: 'title', name: 'Title', required: true, type: 'text' },
+ { id: 'description', name: 'Description', required: false, type: 'text' },
+ ],
+ ConnectorTypes.jira
+ );
+ expect(result).toEqual(mappings[`${ConnectorTypes.jira}-alt`]);
+ });
+ });
});
diff --git a/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts b/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts
index 45ccb4f2c539f..0d78bceeaf2fa 100644
--- a/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts
+++ b/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { SavedObject, SavedObjectsFindResponse } from 'kibana/server';
+import { SavedObject } from 'kibana/server';
import {
CaseStatuses,
CommentAttributes,
@@ -14,8 +14,8 @@ import {
ESCaseAttributes,
ESCasesConfigureAttributes,
} from '../../../../common/api';
-import { mappings } from '../cases/configure/mock';
import { CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT } from '../../../saved_object_types';
+import { mappings } from '../../../client/configure/mock';
export const mockCases: Array> = [
{
@@ -381,31 +381,13 @@ export const mockCaseConfigure: Array> =
},
];
-export const mockCaseConfigureFind: Array> = [
- {
- page: 1,
- per_page: 5,
- total: mockCaseConfigure.length,
- saved_objects: [{ ...mockCaseConfigure[0], score: 0 }],
- },
-];
-
export const mockCaseMappings: Array> = [
{
type: CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT,
id: 'mock-mappings-1',
attributes: {
- mappings,
+ mappings: mappings[ConnectorTypes.jira],
},
references: [],
},
];
-
-export const mockCaseMappingsFind: Array> = [
- {
- page: 1,
- per_page: 5,
- total: mockCaseConfigure.length,
- saved_objects: [{ ...mockCaseMappings[0], score: 0 }],
- },
-];
diff --git a/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts b/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts
index b6da21927e342..efc3b6044a804 100644
--- a/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts
+++ b/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts
@@ -3,9 +3,15 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { CasePostRequest, CasesConfigureRequest, ConnectorTypes } from '../../../../common/api';
+import {
+ CasePostRequest,
+ CasesConfigureRequest,
+ ConnectorTypes,
+ PostPushRequest,
+} from '../../../../common/api';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { FindActionResult } from '../../../../../actions/server/types';
+import { params } from '../cases/configure/mock';
export const newCase: CasePostRequest = {
title: 'My new case',
@@ -76,3 +82,19 @@ export const newConfiguration: CasesConfigureRequest = {
},
closure_type: 'close-by-pushing',
};
+
+export const newPostPushRequest: PostPushRequest = {
+ params: params[ConnectorTypes.jira],
+ connector_type: ConnectorTypes.jira,
+};
+
+export const executePushResponse = {
+ status: 'ok',
+ data: {
+ title: 'RJ2-200',
+ id: '10663',
+ pushedDate: '2020-12-17T00:32:40.738Z',
+ url: 'https://siem-kibana.atlassian.net/browse/RJ2-200',
+ comments: [],
+ },
+};
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts
index d75f42f6e486b..87e165f8e0014 100644
--- a/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts
+++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts
@@ -17,7 +17,8 @@ import {
import { initGetCaseConfigure } from './get_configure';
import { CASE_CONFIGURE_URL } from '../../../../../common/constants';
-import { mappings } from './mock';
+import { mappings } from '../../../../client/configure/mock';
+import { ConnectorTypes } from '../../../../../common/api/connectors';
describe('GET configuration', () => {
let routeHandler: RequestHandler;
@@ -42,7 +43,7 @@ describe('GET configuration', () => {
expect(res.status).toEqual(200);
expect(res.payload).toEqual({
...mockCaseConfigure[0].attributes,
- mappings,
+ mappings: mappings[ConnectorTypes.jira],
version: mockCaseConfigure[0].version,
});
});
@@ -76,7 +77,7 @@ describe('GET configuration', () => {
email: 'testemail@elastic.co',
username: 'elastic',
},
- mappings,
+ mappings: mappings[ConnectorTypes.jira],
updated_at: '2020-04-09T09:43:51.778Z',
updated_by: {
full_name: 'elastic',
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_fields.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_fields.ts
deleted file mode 100644
index c9b8e671b7df8..0000000000000
--- a/x-pack/plugins/case/server/routes/api/cases/configure/get_fields.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Boom from '@hapi/boom';
-import { pipe } from 'fp-ts/lib/pipeable';
-import { fold } from 'fp-ts/lib/Either';
-import { identity } from 'fp-ts/lib/function';
-import { RouteDeps } from '../../types';
-import { escapeHatch, wrapError } from '../../utils';
-
-import { CASE_CONFIGURE_CONNECTOR_DETAILS_URL } from '../../../../../common/constants';
-import {
- ConnectorRequestParamsRt,
- GetFieldsRequestQueryRt,
- throwErrors,
-} from '../../../../../common/api';
-
-export function initCaseConfigureGetFields({ router }: RouteDeps) {
- router.get(
- {
- path: CASE_CONFIGURE_CONNECTOR_DETAILS_URL,
- validate: {
- params: escapeHatch,
- query: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.case) {
- throw Boom.badRequest('RouteHandlerContext is not registered for cases');
- }
- const query = pipe(
- GetFieldsRequestQueryRt.decode(request.query),
- fold(throwErrors(Boom.badRequest), identity)
- );
- const params = pipe(
- ConnectorRequestParamsRt.decode(request.params),
- fold(throwErrors(Boom.badRequest), identity)
- );
-
- const caseClient = context.case.getCaseClient();
-
- const connectorType = query.connector_type;
- if (connectorType == null) {
- throw Boom.illegal('no connectorType value provided');
- }
-
- const actionsClient = await context.actions?.getActionsClient();
- if (actionsClient == null) {
- throw Boom.notFound('Action client have not been found');
- }
-
- const res = await caseClient.getFields({
- actionsClient,
- connectorId: params.connector_id,
- connectorType,
- });
-
- return response.ok({
- body: res.fields,
- });
- } catch (error) {
- return response.customError(wrapError(error));
- }
- }
- );
-}
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/mock.ts b/x-pack/plugins/case/server/routes/api/cases/configure/mock.ts
index ed8b208864611..771b09cec2a35 100644
--- a/x-pack/plugins/case/server/routes/api/cases/configure/mock.ts
+++ b/x-pack/plugins/case/server/routes/api/cases/configure/mock.ts
@@ -7,6 +7,7 @@ import {
ServiceConnectorCaseParams,
ServiceConnectorCommentParams,
ConnectorMappingsAttributes,
+ ConnectorTypes,
} from '../../../../../common/api/connectors';
export const updateUser = {
updatedAt: '2020-03-13T08:34:53.450Z',
@@ -24,16 +25,36 @@ export const comment: ServiceConnectorCommentParams = {
...entity,
};
export const defaultPipes = ['informationCreated'];
-export const params = {
+const basicParams = {
comments: [comment],
description: 'a description',
- impact: '3',
- savedObjectId: '1231231231232',
- severity: '1',
title: 'a title',
- urgency: '2',
- ...entity,
-} as ServiceConnectorCaseParams;
+ savedObjectId: '1231231231232',
+ externalId: null,
+};
+export const params = {
+ [ConnectorTypes.jira]: {
+ ...basicParams,
+ issueType: '10003',
+ priority: 'Highest',
+ parent: '5002',
+ ...entity,
+ } as ServiceConnectorCaseParams,
+ [ConnectorTypes.resilient]: {
+ ...basicParams,
+ incidentTypes: ['10003'],
+ severityCode: '1',
+ ...entity,
+ } as ServiceConnectorCaseParams,
+ [ConnectorTypes.servicenow]: {
+ ...basicParams,
+ impact: '3',
+ severity: '1',
+ urgency: '2',
+ ...entity,
+ } as ServiceConnectorCaseParams,
+ [ConnectorTypes.none]: {},
+};
export const mappings: ConnectorMappingsAttributes[] = [
{
source: 'title',
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts
new file mode 100644
index 0000000000000..ff0939fdcce1f
--- /dev/null
+++ b/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts
@@ -0,0 +1,104 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { kibanaResponseFactory, RequestHandler, RequestHandlerContext } from 'src/core/server';
+import { httpServerMock } from 'src/core/server/mocks';
+
+import {
+ createMockSavedObjectsRepository,
+ createRoute,
+ createRouteContext,
+ mockCaseMappings,
+} from '../../__fixtures__';
+
+import { initPostPushToService } from './post_push_to_service';
+import { executePushResponse, newPostPushRequest } from '../../__mocks__/request_responses';
+import { CASE_CONFIGURE_PUSH_URL } from '../../../../../common/constants';
+
+describe('Post push to service', () => {
+ let routeHandler: RequestHandler;
+ const req = httpServerMock.createKibanaRequest({
+ path: `${CASE_CONFIGURE_PUSH_URL}`,
+ method: 'post',
+ params: {
+ connector_id: '666',
+ },
+ body: newPostPushRequest,
+ });
+ let context: RequestHandlerContext;
+ beforeAll(async () => {
+ routeHandler = await createRoute(initPostPushToService, 'post');
+ const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>;
+ spyOnDate.mockImplementation(() => ({
+ toISOString: jest.fn().mockReturnValue('2020-04-09T09:43:51.778Z'),
+ }));
+ context = await createRouteContext(
+ createMockSavedObjectsRepository({
+ caseMappingsSavedObject: mockCaseMappings,
+ })
+ );
+ });
+
+ it('Happy path - posts success', async () => {
+ const betterContext = ({
+ ...context,
+ actions: {
+ ...context.actions,
+ getActionsClient: () => {
+ const actions = context!.actions!.getActionsClient();
+ return {
+ ...actions,
+ execute: jest.fn().mockImplementation(({ actionId }) => {
+ return {
+ status: 'ok',
+ data: {
+ title: 'RJ2-200',
+ id: '10663',
+ pushedDate: '2020-12-17T00:32:40.738Z',
+ url: 'https://siem-kibana.atlassian.net/browse/RJ2-200',
+ comments: [],
+ },
+ actionId,
+ };
+ }),
+ };
+ },
+ },
+ } as unknown) as RequestHandlerContext;
+
+ const res = await routeHandler(betterContext, req, kibanaResponseFactory);
+
+ expect(res.status).toEqual(200);
+ expect(res.payload).toEqual({
+ ...executePushResponse,
+ actionId: '666',
+ });
+ });
+ it('Unhappy path - context case missing', async () => {
+ const betterContext = ({
+ ...context,
+ case: null,
+ } as unknown) as RequestHandlerContext;
+
+ const res = await routeHandler(betterContext, req, kibanaResponseFactory);
+ expect(res.status).toEqual(400);
+ expect(res.payload.isBoom).toBeTruthy();
+ expect(res.payload.output.payload.message).toEqual(
+ 'RouteHandlerContext is not registered for cases'
+ );
+ });
+ it('Unhappy path - context actions missing', async () => {
+ const betterContext = ({
+ ...context,
+ actions: null,
+ } as unknown) as RequestHandlerContext;
+
+ const res = await routeHandler(betterContext, req, kibanaResponseFactory);
+ expect(res.status).toEqual(404);
+ expect(res.payload.isBoom).toBeTruthy();
+ expect(res.payload.output.payload.message).toEqual('Action client have not been found');
+ });
+});
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.ts b/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.ts
index 9c4c06c5f4e18..fb7a91d046313 100644
--- a/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.ts
+++ b/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.ts
@@ -19,7 +19,7 @@ import {
} from '../../../../../common/api';
import { mapIncident } from './utils';
-export function initPostPushToService({ router, connectorMappingsService }: RouteDeps) {
+export function initPostPushToService({ router }: RouteDeps) {
router.post(
{
path: CASE_CONFIGURE_PUSH_URL,
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/utils.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/utils.test.ts
index d2ecdf61c882d..d1f8391ad082a 100644
--- a/x-pack/plugins/case/server/routes/api/cases/configure/utils.test.ts
+++ b/x-pack/plugins/case/server/routes/api/cases/configure/utils.test.ts
@@ -5,25 +5,31 @@
*/
import {
+ mapIncident,
prepareFieldsForTransformation,
- transformFields,
+ serviceFormatter,
transformComments,
transformers,
+ transformFields,
} from './utils';
-import { comment as commentObj, defaultPipes, mappings, params, updateUser } from './mock';
+import { comment as commentObj, mappings, defaultPipes, params, updateUser } from './mock';
import {
- ServiceConnectorCaseParams,
+ ConnectorTypes,
ExternalServiceParams,
Incident,
+ ServiceConnectorCaseParams,
} from '../../../../../common/api/connectors';
+import { actionsClientMock } from '../../../../../../actions/server/actions_client.mock';
+import { mappings as mappingsMock } from '../../../../client/configure/mock';
const formatComment = { commentId: commentObj.commentId, comment: commentObj.comment };
+const serviceNowParams = params[ConnectorTypes.servicenow] as ServiceConnectorCaseParams;
describe('api/cases/configure/utils', () => {
describe('prepareFieldsForTransformation', () => {
test('prepare fields with defaults', () => {
const res = prepareFieldsForTransformation({
defaultPipes,
- params,
+ params: serviceNowParams,
mappings,
});
expect(res).toEqual([
@@ -46,7 +52,7 @@ describe('api/cases/configure/utils', () => {
const res = prepareFieldsForTransformation({
defaultPipes: ['myTestPipe'],
mappings,
- params,
+ params: serviceNowParams,
});
expect(res).toEqual([
{
@@ -69,11 +75,11 @@ describe('api/cases/configure/utils', () => {
const fields = prepareFieldsForTransformation({
defaultPipes,
mappings,
- params,
+ params: serviceNowParams,
});
const res = transformFields({
- params,
+ params: serviceNowParams,
fields,
});
@@ -85,14 +91,14 @@ describe('api/cases/configure/utils', () => {
test('transform fields for update correctly', () => {
const fields = prepareFieldsForTransformation({
- params,
+ params: serviceNowParams,
mappings,
defaultPipes: ['informationUpdated'],
});
const res = transformFields({
params: {
- ...params,
+ ...serviceNowParams,
updatedAt: '2020-03-15T08:34:53.450Z',
updatedBy: {
username: 'anotherUser',
@@ -114,13 +120,13 @@ describe('api/cases/configure/utils', () => {
test('add newline character to description', () => {
const fields = prepareFieldsForTransformation({
- params,
+ params: serviceNowParams,
mappings,
defaultPipes: ['informationUpdated'],
});
const res = transformFields({
- params,
+ params: serviceNowParams,
fields,
currentIncident: {
short_description: 'first title',
@@ -134,12 +140,12 @@ describe('api/cases/configure/utils', () => {
const fields = prepareFieldsForTransformation({
defaultPipes,
mappings,
- params,
+ params: serviceNowParams,
});
const res = transformFields({
params: {
- ...params,
+ ...serviceNowParams,
createdBy: { fullName: '', username: 'elastic' },
},
fields,
@@ -155,12 +161,12 @@ describe('api/cases/configure/utils', () => {
const fields = prepareFieldsForTransformation({
defaultPipes: ['informationUpdated'],
mappings,
- params,
+ params: serviceNowParams,
});
const res = transformFields({
params: {
- ...params,
+ ...serviceNowParams,
updatedAt: '2020-03-15T08:34:53.450Z',
updatedBy: { username: 'anotherUser', fullName: '' },
},
@@ -382,4 +388,142 @@ describe('api/cases/configure/utils', () => {
});
});
});
+ describe('mapIncident', () => {
+ let actionsMock = actionsClientMock.create();
+ it('maps an external incident', async () => {
+ const res = await mapIncident(
+ actionsMock,
+ '123',
+ ConnectorTypes.servicenow,
+ mappingsMock[ConnectorTypes.servicenow],
+ serviceNowParams
+ );
+ expect(res).toEqual({
+ incident: {
+ description: 'a description (created at 2020-03-13T08:34:53.450Z by Elastic User)',
+ externalId: null,
+ impact: '3',
+ severity: '1',
+ short_description: 'a title (created at 2020-03-13T08:34:53.450Z by Elastic User)',
+ urgency: '2',
+ },
+ comments: [
+ {
+ comment: 'first comment (added at 2020-03-13T08:34:53.450Z by Elastic User)',
+ commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631',
+ },
+ ],
+ });
+ });
+ it('throws error if invalid service', async () => {
+ await mapIncident(
+ actionsMock,
+ '123',
+ 'invalid',
+ mappingsMock[ConnectorTypes.servicenow],
+ serviceNowParams
+ ).catch((e) => {
+ expect(e).not.toBeNull();
+ expect(e).toEqual(new Error(`Invalid service`));
+ });
+ });
+ it('updates an existing incident', async () => {
+ const existingIncidentData = {
+ description: 'fun description',
+ impact: '3',
+ severity: '3',
+ short_description: 'fun title',
+ urgency: '3',
+ };
+ const execute = jest.fn().mockReturnValue(existingIncidentData);
+ actionsMock = { ...actionsMock, execute };
+ const res = await mapIncident(
+ actionsMock,
+ '123',
+ ConnectorTypes.servicenow,
+ mappingsMock[ConnectorTypes.servicenow],
+ { ...serviceNowParams, externalId: '123' }
+ );
+ expect(res).toEqual({
+ incident: {
+ description: 'a description (updated at 2020-03-13T08:34:53.450Z by Elastic User)',
+ externalId: '123',
+ impact: '3',
+ severity: '1',
+ short_description: 'a title (updated at 2020-03-13T08:34:53.450Z by Elastic User)',
+ urgency: '2',
+ },
+ comments: [
+ {
+ comment: 'first comment (added at 2020-03-13T08:34:53.450Z by Elastic User)',
+ commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631',
+ },
+ ],
+ });
+ });
+ it('throws error when existing incident throws', async () => {
+ const execute = jest.fn().mockImplementation(() => {
+ throw new Error('exception');
+ });
+ actionsMock = { ...actionsMock, execute };
+ await mapIncident(
+ actionsMock,
+ '123',
+ ConnectorTypes.servicenow,
+ mappingsMock[ConnectorTypes.servicenow],
+ { ...serviceNowParams, externalId: '123' }
+ ).catch((e) => {
+ expect(e).not.toBeNull();
+ expect(e).toEqual(
+ new Error(
+ `Retrieving Incident by id 123 from ServiceNow failed with exception: Error: exception`
+ )
+ );
+ });
+ });
+ });
+
+ const connectors = [
+ {
+ name: ConnectorTypes.jira,
+ result: {
+ incident: {
+ issueType: '10003',
+ parent: '5002',
+ priority: 'Highest',
+ },
+ thirdPartyName: 'Jira',
+ },
+ },
+ {
+ name: ConnectorTypes.resilient,
+ result: {
+ incident: {
+ incidentTypes: ['10003'],
+ severityCode: '1',
+ },
+ thirdPartyName: 'Resilient',
+ },
+ },
+ {
+ name: ConnectorTypes.servicenow,
+ result: {
+ incident: {
+ impact: '3',
+ severity: '1',
+ urgency: '2',
+ },
+ thirdPartyName: 'ServiceNow',
+ },
+ },
+ ];
+ describe('serviceFormatter', () => {
+ connectors.forEach((c) =>
+ it(`formats ${c.name}`, () => {
+ const caseParams = params[c.name] as ServiceConnectorCaseParams;
+ const res = serviceFormatter(c.name, caseParams);
+ expect(res).toEqual(c.result);
+ })
+ );
+ });
});
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/utils.ts b/x-pack/plugins/case/server/routes/api/cases/configure/utils.ts
index b8a37661fe9f7..89109af4cecb9 100644
--- a/x-pack/plugins/case/server/routes/api/cases/configure/utils.ts
+++ b/x-pack/plugins/case/server/routes/api/cases/configure/utils.ts
@@ -25,9 +25,7 @@ import {
TransformerArgs,
TransformFieldsArgs,
} from '../../../../../common/api';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { ActionsClient } from '../../../../../../actions/server/actions_client';
-
+import { ActionsClient } from '../../../../../../actions/server';
export const mapIncident = async (
actionsClient: ActionsClient,
connectorId: string,
@@ -59,13 +57,11 @@ export const mapIncident = async (
);
}
}
-
const fields = prepareFieldsForTransformation({
defaultPipes,
mappings,
params,
});
-
const transformedFields = transformFields<
ServiceConnectorCaseParams,
ExternalServiceParams,
diff --git a/x-pack/plugins/case/server/routes/api/index.ts b/x-pack/plugins/case/server/routes/api/index.ts
index 587e43b218f44..15817b425021e 100644
--- a/x-pack/plugins/case/server/routes/api/index.ts
+++ b/x-pack/plugins/case/server/routes/api/index.ts
@@ -25,7 +25,6 @@ import { initPostCommentApi } from './cases/comments/post_comment';
import { initCaseConfigureGetActionConnector } from './cases/configure/get_connectors';
import { initGetCaseConfigure } from './cases/configure/get_configure';
-import { initCaseConfigureGetFields } from './cases/configure/get_fields';
import { initPatchCaseConfigure } from './cases/configure/patch_configure';
import { initPostCaseConfigure } from './cases/configure/post_configure';
import { initPostPushToService } from './cases/configure/post_push_to_service';
@@ -54,7 +53,6 @@ export function initCaseApi(deps: RouteDeps) {
initGetCaseConfigure(deps);
initPatchCaseConfigure(deps);
initPostCaseConfigure(deps);
- initCaseConfigureGetFields(deps);
initPostPushToService(deps);
// Reporters
initGetReportersApi(deps);
diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.ts b/x-pack/plugins/security_solution/public/cases/containers/api.ts
index ef1e35b8ceb4b..07f7391ca94d9 100644
--- a/x-pack/plugins/security_solution/public/cases/containers/api.ts
+++ b/x-pack/plugins/security_solution/public/cases/containers/api.ts
@@ -16,7 +16,6 @@ import {
CaseUserActionsResponse,
CommentRequest,
CommentType,
- ConnectorField,
ServiceConnectorCaseParams,
ServiceConnectorCaseResponse,
User,
@@ -24,7 +23,6 @@ import {
import {
ACTION_TYPES_URL,
- CASE_CONFIGURE_CONNECTORS_URL,
CASE_REPORTERS_URL,
CASE_STATUS_URL,
CASE_TAGS_URL,
@@ -273,20 +271,3 @@ export const getActionLicense = async (signal: AbortSignal): Promise => {
- const response = await KibanaServices.get().http.fetch(
- `${CASE_CONFIGURE_CONNECTORS_URL}/${connectorId}`,
- {
- query: {
- connector_type: connectorType,
- },
- method: 'GET',
- signal,
- }
- );
- return response;
-};
diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_fields.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_fields.tsx
deleted file mode 100644
index 6b594fa60e0c7..0000000000000
--- a/x-pack/plugins/security_solution/public/cases/containers/use_get_fields.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { useCallback, useEffect, useState } from 'react';
-
-import { errorToToaster, useStateToaster } from '../../common/components/toasters';
-import { getFields } from './api';
-import * as i18n from './translations';
-import { ConnectorField } from '../../../../case/common/api';
-
-interface FieldsState {
- fields: ConnectorField[];
- isLoading: boolean;
- isError: boolean;
-}
-
-const initialData: FieldsState = {
- fields: [],
- isLoading: false,
- isError: false,
-};
-
-export interface UseGetFields extends FieldsState {
- fetchFields: () => void;
-}
-
-export const useGetFields = (connectorId: string, connectorType: string): UseGetFields => {
- const [fieldsState, setFieldsState] = useState(initialData);
- const [, dispatchToaster] = useStateToaster();
-
- const fetchFields = useCallback(() => {
- let didCancel = false;
- const abortCtrl = new AbortController();
- const fetchData = async () => {
- setFieldsState({
- ...fieldsState,
- isLoading: true,
- });
- try {
- const response = await getFields(connectorId, connectorType, abortCtrl.signal);
- if (!didCancel) {
- setFieldsState({
- fields: response,
- isLoading: false,
- isError: false,
- });
- }
- } catch (error) {
- if (!didCancel) {
- errorToToaster({
- title: i18n.ERROR_TITLE,
- error: error.body && error.body.message ? new Error(error.body.message) : error,
- dispatchToaster,
- });
- setFieldsState({
- fields: [],
- isLoading: false,
- isError: true,
- });
- }
- }
- };
- fetchData();
- return () => {
- didCancel = true;
- abortCtrl.abort();
- };
- }, [connectorId, connectorType, dispatchToaster, fieldsState]);
-
- useEffect(() => {
- fetchFields();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- return {
- ...fieldsState,
- fetchFields,
- };
-};
From 62833a35df8e6d279113498b51d7ec68865b040f Mon Sep 17 00:00:00 2001
From: Scotty Bollinger
Date: Fri, 18 Dec 2020 10:32:13 -0600
Subject: [PATCH 02/40] [Workplace Search] Add AddSourceLogic tests (#86334)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Remove missed action calls
These were missed in the PR that split apart add_source_logic from source_logic. These 2 mthods aren’t even in this file. My mistake
* Change order of method calls
For some reason the test would not pass with the order of the methods the way they were. Changing the order to where the error callback is called first somehow made the test pass
* Fix bug where query params never set
Uncovered a bug while writing this test. After moving from ent-search to Kibana, it was discovered that the Kibana server did not work well with an empty question mark in the URL. The fix attempted to use lodash’s isEmpty metthod. The problem is that the URLSearchParams class instance created is not an object but has getter/setter functionality so isEmpty always returned true. This fixes it to actually work.
* Remove throw blocks
In ent-search we had to account for edge cases where the error state wasn’t bubbling up. One of these has been covered with global flash messages. The other will be tested at later date. A tech-debt item has been created.
Reference PRs:
https://github.com/elastic/ent-search/pull/394
https://github.com/elastic/ent-search/pull/701
* Export interfaces for use in tests
* Add tests for add_source_logic
* Remove weird import
I guess the IDE autocompleted that. Should not have been there
* Remove redundant test
This test was added before this comit when trying to get coverage for the throw blocks
https://github.com/elastic/kibana/pull/86334/commits/7ca9c2b2440eeff22d04bdce7290c136a07f320c
* Lint fixes
---
.../add_source/add_source_logic.test.ts | 553 ++++++++++++++++++
.../components/add_source/add_source_logic.ts | 18 +-
2 files changed, 560 insertions(+), 11 deletions(-)
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
new file mode 100644
index 0000000000000..ba38b46aa0552
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
@@ -0,0 +1,553 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { resetContext } from 'kea';
+import { mockHttpValues } from '../../../../../__mocks__';
+jest.mock('../../../../../shared/http', () => ({
+ HttpLogic: {
+ values: { http: mockHttpValues.http },
+ },
+}));
+import { HttpLogic } from '../../../../../shared/http';
+
+jest.mock('../../../../../shared/flash_messages', () => ({
+ FlashMessagesLogic: { actions: { clearFlashMessages: jest.fn(), setQueuedMessages: jest.fn() } },
+ flashAPIErrors: jest.fn(),
+ setSuccessMessage: jest.fn(),
+ setQueuedSuccessMessage: jest.fn(),
+}));
+import { FlashMessagesLogic, flashAPIErrors } from '../../../../../shared/flash_messages';
+
+import { AppLogic } from '../../../../app_logic';
+jest.mock('../../../../app_logic', () => ({
+ AppLogic: { values: { isOrganization: true } },
+}));
+
+import { CustomSource } from '../../../../types';
+
+import { sourceConfigData } from '../../../../__mocks__/content_sources.mock';
+
+import {
+ AddSourceLogic,
+ SourceConfigData,
+ SourceConnectData,
+ OrganizationsMap,
+} from './add_source_logic';
+
+describe('AddSourceLogic', () => {
+ const defaultValues = {
+ dataLoading: true,
+ sectionLoading: true,
+ buttonLoading: false,
+ customSourceNameValue: '',
+ clientIdValue: '',
+ clientSecretValue: '',
+ baseUrlValue: '',
+ loginValue: '',
+ passwordValue: '',
+ subdomainValue: '',
+ indexPermissionsValue: false,
+ sourceConfigData: {} as SourceConfigData,
+ sourceConnectData: {} as SourceConnectData,
+ newCustomSource: {} as CustomSource,
+ currentServiceType: '',
+ githubOrganizations: [],
+ selectedGithubOrganizationsMap: {} as OrganizationsMap,
+ selectedGithubOrganizations: [],
+ };
+
+ const sourceConnectData = {
+ oauthUrl: 'http://foo',
+ serviceType: 'gmail',
+ };
+
+ const config = {
+ id: '123key',
+ serviceType: 'github',
+ githubOrganizations: ['foo', 'bar'],
+ };
+
+ const clearFlashMessagesSpy = jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages');
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ resetContext({});
+ AddSourceLogic.mount();
+ });
+
+ it('has expected default values', () => {
+ expect(AddSourceLogic.values).toEqual(defaultValues);
+ });
+
+ describe('actions', () => {
+ it('setSourceConfigData', () => {
+ AddSourceLogic.actions.setSourceConfigData(sourceConfigData);
+
+ expect(AddSourceLogic.values.sourceConfigData).toEqual(sourceConfigData);
+ expect(AddSourceLogic.values.dataLoading).toEqual(false);
+ expect(AddSourceLogic.values.buttonLoading).toEqual(false);
+ expect(AddSourceLogic.values.clientIdValue).toEqual(
+ sourceConfigData.configuredFields.clientId
+ );
+ expect(AddSourceLogic.values.clientSecretValue).toEqual(
+ sourceConfigData.configuredFields.clientSecret
+ );
+ expect(AddSourceLogic.values.baseUrlValue).toEqual(sourceConfigData.configuredFields.baseUrl);
+ });
+
+ it('setSourceConnectData', () => {
+ AddSourceLogic.actions.setSourceConnectData(sourceConnectData);
+
+ expect(AddSourceLogic.values.sourceConnectData).toEqual(sourceConnectData);
+ expect(AddSourceLogic.values.buttonLoading).toEqual(false);
+ });
+
+ it('setClientIdValue', () => {
+ AddSourceLogic.actions.setClientIdValue('id');
+
+ expect(AddSourceLogic.values.clientIdValue).toEqual('id');
+ });
+
+ it('setClientSecretValue', () => {
+ AddSourceLogic.actions.setClientSecretValue('secret');
+
+ expect(AddSourceLogic.values.clientSecretValue).toEqual('secret');
+ });
+
+ it('setBaseUrlValue', () => {
+ AddSourceLogic.actions.setBaseUrlValue('secret');
+
+ expect(AddSourceLogic.values.baseUrlValue).toEqual('secret');
+ });
+
+ it('setCustomSourceNameValue', () => {
+ AddSourceLogic.actions.setCustomSourceNameValue('name');
+
+ expect(AddSourceLogic.values.customSourceNameValue).toEqual('name');
+ });
+
+ it('setSourceLoginValue', () => {
+ AddSourceLogic.actions.setSourceLoginValue('login');
+
+ expect(AddSourceLogic.values.loginValue).toEqual('login');
+ });
+
+ it('setSourcePasswordValue', () => {
+ AddSourceLogic.actions.setSourcePasswordValue('password');
+
+ expect(AddSourceLogic.values.passwordValue).toEqual('password');
+ });
+
+ it('setSourceSubdomainValue', () => {
+ AddSourceLogic.actions.setSourceSubdomainValue('subdomain');
+
+ expect(AddSourceLogic.values.subdomainValue).toEqual('subdomain');
+ });
+
+ it('setSourceIndexPermissionsValue', () => {
+ AddSourceLogic.actions.setSourceIndexPermissionsValue(true);
+
+ expect(AddSourceLogic.values.indexPermissionsValue).toEqual(true);
+ });
+
+ it('setCustomSourceData', () => {
+ const newCustomSource = {
+ accessToken: 'foo',
+ key: 'bar',
+ name: 'source',
+ id: '123key',
+ };
+
+ AddSourceLogic.actions.setCustomSourceData(newCustomSource);
+
+ expect(AddSourceLogic.values.newCustomSource).toEqual(newCustomSource);
+ });
+
+ it('setPreContentSourceConfigData', () => {
+ AddSourceLogic.actions.setPreContentSourceConfigData(config);
+
+ expect(AddSourceLogic.values.dataLoading).toEqual(false);
+ expect(AddSourceLogic.values.sectionLoading).toEqual(false);
+ expect(AddSourceLogic.values.currentServiceType).toEqual(config.serviceType);
+ expect(AddSourceLogic.values.githubOrganizations).toEqual(config.githubOrganizations);
+ });
+
+ it('setSelectedGithubOrganizations', () => {
+ AddSourceLogic.actions.setSelectedGithubOrganizations('foo');
+
+ expect(AddSourceLogic.values.selectedGithubOrganizationsMap).toEqual({ foo: true });
+ });
+
+ it('setButtonNotLoading', () => {
+ AddSourceLogic.actions.setButtonNotLoading();
+
+ expect(AddSourceLogic.values.buttonLoading).toEqual(false);
+ });
+
+ it('resetSourceState', () => {
+ AddSourceLogic.actions.resetSourceState();
+
+ expect(AddSourceLogic.values.dataLoading).toEqual(false);
+ expect(AddSourceLogic.values.buttonLoading).toEqual(false);
+ expect(AddSourceLogic.values.clientIdValue).toEqual('');
+ expect(AddSourceLogic.values.clientSecretValue).toEqual('');
+ expect(AddSourceLogic.values.baseUrlValue).toEqual('');
+ expect(AddSourceLogic.values.loginValue).toEqual('');
+ expect(AddSourceLogic.values.passwordValue).toEqual('');
+ expect(AddSourceLogic.values.subdomainValue).toEqual('');
+ expect(AddSourceLogic.values.indexPermissionsValue).toEqual(false);
+ expect(AddSourceLogic.values.customSourceNameValue).toEqual('');
+ expect(AddSourceLogic.values.newCustomSource).toEqual({});
+ expect(AddSourceLogic.values.currentServiceType).toEqual('');
+ expect(AddSourceLogic.values.githubOrganizations).toEqual([]);
+ expect(AddSourceLogic.values.selectedGithubOrganizationsMap).toEqual({});
+ });
+
+ it('handles fallback states', () => {
+ const { publicKey, privateKey, consumerKey } = sourceConfigData.configuredFields;
+ AddSourceLogic.actions.setSourceConfigData({
+ ...sourceConfigData,
+ configuredFields: {
+ publicKey,
+ privateKey,
+ consumerKey,
+ },
+ });
+
+ expect(AddSourceLogic.values.clientIdValue).toEqual('');
+ expect(AddSourceLogic.values.clientSecretValue).toEqual('');
+ expect(AddSourceLogic.values.baseUrlValue).toEqual('');
+ });
+ });
+
+ describe('listeners', () => {
+ describe('organization context', () => {
+ describe('getSourceConfigData', () => {
+ it('calls API and sets values', async () => {
+ const setSourceConfigDataSpy = jest.spyOn(AddSourceLogic.actions, 'setSourceConfigData');
+ const promise = Promise.resolve(sourceConfigData);
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getSourceConfigData('github');
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/org/settings/connectors/github'
+ );
+ await promise;
+ expect(setSourceConfigDataSpy).toHaveBeenCalledWith(sourceConfigData);
+ });
+
+ it('handles error', async () => {
+ const promise = Promise.reject('this is an error');
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getSourceConfigData('github');
+ try {
+ await promise;
+ } catch {
+ // Do nothing
+ }
+ expect(flashAPIErrors).toHaveBeenCalledWith('this is an error');
+ });
+ });
+
+ describe('getSourceConnectData', () => {
+ const successCallback = jest.fn();
+
+ it('calls API and sets values', async () => {
+ const setButtonNotLoadingSpy = jest.spyOn(AddSourceLogic.actions, 'setButtonNotLoading');
+ const setSourceConnectDataSpy = jest.spyOn(
+ AddSourceLogic.actions,
+ 'setSourceConnectData'
+ );
+ const promise = Promise.resolve(sourceConnectData);
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getSourceConnectData('github', successCallback);
+
+ expect(clearFlashMessagesSpy).toHaveBeenCalled();
+ expect(AddSourceLogic.values.buttonLoading).toEqual(true);
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/org/sources/github/prepare'
+ );
+ await promise;
+ expect(setSourceConnectDataSpy).toHaveBeenCalledWith(sourceConnectData);
+ expect(successCallback).toHaveBeenCalledWith(sourceConnectData.oauthUrl);
+ expect(setButtonNotLoadingSpy).toHaveBeenCalled();
+ });
+
+ it('appends query params', () => {
+ AddSourceLogic.actions.setSourceSubdomainValue('subdomain');
+ AddSourceLogic.actions.setSourceIndexPermissionsValue(true);
+ AddSourceLogic.actions.getSourceConnectData('github', successCallback);
+
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/org/sources/github/prepare?subdomain=subdomain&index_permissions=true'
+ );
+ });
+
+ it('handles error', async () => {
+ const promise = Promise.reject('this is an error');
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getSourceConnectData('github', successCallback);
+ try {
+ await promise;
+ } catch {
+ // Do nothing
+ }
+ expect(flashAPIErrors).toHaveBeenCalledWith('this is an error');
+ });
+ });
+
+ describe('getSourceReConnectData', () => {
+ it('calls API and sets values', async () => {
+ const setSourceConnectDataSpy = jest.spyOn(
+ AddSourceLogic.actions,
+ 'setSourceConnectData'
+ );
+ const promise = Promise.resolve(sourceConnectData);
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getSourceReConnectData('github');
+
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/org/sources/github/reauth_prepare'
+ );
+ await promise;
+ expect(setSourceConnectDataSpy).toHaveBeenCalledWith(sourceConnectData);
+ });
+
+ it('handles error', async () => {
+ const promise = Promise.reject('this is an error');
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getSourceReConnectData('github');
+ try {
+ await promise;
+ } catch {
+ // Do nothing
+ }
+ expect(flashAPIErrors).toHaveBeenCalledWith('this is an error');
+ });
+ });
+
+ describe('getPreContentSourceConfigData', () => {
+ it('calls API and sets values', async () => {
+ const setPreContentSourceConfigDataSpy = jest.spyOn(
+ AddSourceLogic.actions,
+ 'setPreContentSourceConfigData'
+ );
+ const promise = Promise.resolve(config);
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getPreContentSourceConfigData('123');
+
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/org/pre_sources/123'
+ );
+ await promise;
+ expect(setPreContentSourceConfigDataSpy).toHaveBeenCalledWith(config);
+ });
+
+ it('handles error', async () => {
+ const promise = Promise.reject('this is an error');
+ (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.getPreContentSourceConfigData('123');
+ try {
+ await promise;
+ } catch {
+ // Do nothing
+ }
+ expect(flashAPIErrors).toHaveBeenCalledWith('this is an error');
+ });
+ });
+
+ describe('saveSourceConfig', () => {
+ let params: any;
+
+ beforeEach(() => {
+ AddSourceLogic.actions.setSourceConfigData(sourceConfigData);
+
+ params = {
+ base_url: AddSourceLogic.values.baseUrlValue,
+ client_id: AddSourceLogic.values.clientIdValue,
+ client_secret: AddSourceLogic.values.clientSecretValue,
+ service_type: sourceConfigData.serviceType,
+ private_key: sourceConfigData.configuredFields?.privateKey,
+ public_key: sourceConfigData.configuredFields?.publicKey,
+ consumer_key: sourceConfigData.configuredFields?.consumerKey,
+ };
+ });
+
+ it('calls API and sets values when updating', async () => {
+ const successCallback = jest.fn();
+ const setButtonNotLoadingSpy = jest.spyOn(AddSourceLogic.actions, 'setButtonNotLoading');
+ const setSourceConfigDataSpy = jest.spyOn(AddSourceLogic.actions, 'setSourceConfigData');
+ const promise = Promise.resolve({ sourceConfigData });
+ (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.saveSourceConfig(true, successCallback);
+
+ expect(clearFlashMessagesSpy).toHaveBeenCalled();
+ expect(AddSourceLogic.values.buttonLoading).toEqual(true);
+ expect(
+ HttpLogic.values.http.put
+ ).toHaveBeenCalledWith(
+ `/api/workplace_search/org/settings/connectors/${sourceConfigData.serviceType}`,
+ { body: JSON.stringify({ params }) }
+ );
+
+ await promise;
+ expect(successCallback).toHaveBeenCalled();
+ expect(setSourceConfigDataSpy).toHaveBeenCalledWith({ sourceConfigData });
+ expect(setButtonNotLoadingSpy).toHaveBeenCalled();
+ });
+
+ it('calls API when creating with empty attributes', () => {
+ AddSourceLogic.actions.setClientIdValue('');
+ AddSourceLogic.actions.setClientSecretValue('');
+ AddSourceLogic.actions.setBaseUrlValue('');
+ AddSourceLogic.actions.saveSourceConfig(false);
+
+ const createParams = {
+ service_type: sourceConfigData.serviceType,
+ private_key: sourceConfigData.configuredFields?.privateKey,
+ public_key: sourceConfigData.configuredFields?.publicKey,
+ consumer_key: sourceConfigData.configuredFields?.consumerKey,
+ };
+
+ expect(HttpLogic.values.http.post).toHaveBeenCalledWith(
+ '/api/workplace_search/org/settings/connectors',
+ {
+ body: JSON.stringify({ params: createParams }),
+ }
+ );
+ });
+
+ it('handles error', async () => {
+ const promise = Promise.reject('this is an error');
+ (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.saveSourceConfig(true);
+ try {
+ await promise;
+ } catch {
+ // Do nothing
+ }
+ expect(flashAPIErrors).toHaveBeenCalledWith('this is an error');
+ });
+ });
+
+ describe('createContentSource', () => {
+ const successCallback = jest.fn();
+ const errorCallback = jest.fn();
+
+ const serviceType = 'zendesk';
+ const name = 'name';
+ const login = 'login';
+ const password = 'password';
+ const indexPermissions = false;
+
+ let params: any;
+
+ beforeEach(() => {
+ AddSourceLogic.actions.setCustomSourceNameValue(name);
+ AddSourceLogic.actions.setSourceLoginValue(login);
+ AddSourceLogic.actions.setSourcePasswordValue(password);
+ AddSourceLogic.actions.setPreContentSourceConfigData(config);
+ AddSourceLogic.actions.setSourceIndexPermissionsValue(indexPermissions);
+ AddSourceLogic.actions.setSelectedGithubOrganizations('foo');
+
+ params = {
+ service_type: serviceType,
+ name,
+ login,
+ password,
+ organizations: ['foo'],
+ };
+ });
+
+ it('calls API and sets values', async () => {
+ const setButtonNotLoadingSpy = jest.spyOn(AddSourceLogic.actions, 'setButtonNotLoading');
+ const setCustomSourceDataSpy = jest.spyOn(AddSourceLogic.actions, 'setCustomSourceData');
+ const promise = Promise.resolve({ sourceConfigData });
+ (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback);
+
+ expect(clearFlashMessagesSpy).toHaveBeenCalled();
+ expect(AddSourceLogic.values.buttonLoading).toEqual(true);
+ expect(HttpLogic.values.http.post).toHaveBeenCalledWith(
+ '/api/workplace_search/org/create_source',
+ {
+ body: JSON.stringify({ ...params }),
+ }
+ );
+ await promise;
+ expect(setCustomSourceDataSpy).toHaveBeenCalledWith({ sourceConfigData });
+ expect(successCallback).toHaveBeenCalled();
+ expect(setButtonNotLoadingSpy).toHaveBeenCalled();
+ });
+
+ it('handles error', async () => {
+ const promise = Promise.reject('this is an error');
+ (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise);
+
+ AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback);
+ try {
+ await promise;
+ } catch {
+ // Do nothing
+ }
+ expect(errorCallback).toHaveBeenCalled();
+ expect(flashAPIErrors).toHaveBeenCalledWith('this is an error');
+ });
+ });
+ });
+
+ describe('account context routes', () => {
+ beforeEach(() => {
+ AppLogic.values.isOrganization = false;
+ });
+
+ it('getSourceConnectData', () => {
+ AddSourceLogic.actions.getSourceConnectData('github', jest.fn());
+
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/account/sources/github/prepare'
+ );
+ });
+
+ it('getSourceReConnectData', () => {
+ AddSourceLogic.actions.getSourceReConnectData('123');
+
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/account/sources/123/reauth_prepare'
+ );
+ });
+
+ it('getPreContentSourceConfigData', () => {
+ AddSourceLogic.actions.getPreContentSourceConfigData('123');
+
+ expect(HttpLogic.values.http.get).toHaveBeenCalledWith(
+ '/api/workplace_search/account/pre_sources/123'
+ );
+ });
+
+ it('createContentSource', () => {
+ AddSourceLogic.actions.createContentSource('github', jest.fn());
+
+ expect(HttpLogic.values.http.post).toHaveBeenCalledWith(
+ '/api/workplace_search/account/create_source',
+ {
+ body: JSON.stringify({ service_type: 'github' }),
+ }
+ );
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
index bea69a86edebb..c487b584d8ace 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { keys, pickBy, isEmpty } from 'lodash';
+import { keys, pickBy } from 'lodash';
import { kea, MakeLogicType } from 'kea';
@@ -55,7 +55,7 @@ export interface AddSourceActions {
setButtonNotLoading(): void;
}
-interface SourceConfigData {
+export interface SourceConfigData {
serviceType: string;
name: string;
configured: boolean;
@@ -73,12 +73,12 @@ interface SourceConfigData {
accountContextOnly?: boolean;
}
-interface SourceConnectData {
+export interface SourceConnectData {
oauthUrl: string;
serviceType: string;
}
-interface OrganizationsMap {
+export interface OrganizationsMap {
[key: string]: string | boolean;
}
@@ -160,7 +160,6 @@ export const AddSourceLogic = kea false,
setSourceConfigData: () => false,
resetSourceState: () => false,
setPreContentSourceConfigData: () => false,
@@ -182,7 +181,6 @@ export const AddSourceLogic = kea true,
- setSearchResults: () => false,
setPreContentSourceConfigData: () => false,
},
],
@@ -306,8 +304,8 @@ export const AddSourceLogic = kea
Date: Fri, 18 Dec 2020 11:35:15 -0500
Subject: [PATCH 03/40] [Security Solution] Fix Policy-License-Watcher payload
(#86185)
---
.../endpoint/lib/policy/license_watch.ts | 25 +++++++++++++++----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts
index cae3b9f33850a..2f0c3bf8fd5ba 100644
--- a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts
@@ -12,7 +12,11 @@ import {
SavedObjectsClientContract,
SavedObjectsServiceStart,
} from 'src/core/server';
-import { PackagePolicy, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../fleet/common';
+import {
+ PackagePolicy,
+ PACKAGE_POLICY_SAVED_OBJECT_TYPE,
+ UpdatePackagePolicy,
+} from '../../../../../fleet/common';
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
import { ILicense } from '../../../../../licensing/common/types';
import {
@@ -91,18 +95,29 @@ export class PolicyWatcher {
return;
}
response.items.forEach(async (policy) => {
- const policyConfig = policy.inputs[0].config?.policy.value;
+ const updatePolicy: UpdatePackagePolicy = {
+ name: policy.name,
+ description: policy.description,
+ namespace: policy.namespace,
+ enabled: policy.enabled,
+ policy_id: policy.policy_id,
+ output_id: policy.output_id,
+ package: policy.package,
+ inputs: policy.inputs,
+ version: policy.version,
+ };
+ const policyConfig = updatePolicy.inputs[0].config?.policy.value;
if (!isEndpointPolicyValidForLicense(policyConfig, license)) {
- policy.inputs[0].config!.policy.value = unsetPolicyFeaturesAboveLicenseLevel(
+ updatePolicy.inputs[0].config!.policy.value = unsetPolicyFeaturesAboveLicenseLevel(
policyConfig,
license
);
try {
- await this.policyService.update(this.soClient, policy.id, policy);
+ await this.policyService.update(this.soClient, policy.id, updatePolicy);
} catch (e) {
// try again for transient issues
try {
- await this.policyService.update(this.soClient, policy.id, policy);
+ await this.policyService.update(this.soClient, policy.id, updatePolicy);
} catch (ee) {
this.logger.warn(
`Unable to remove platinum features from policy ${policy.id}: ${ee.message}`
From 0155974591f9a385134fd916eca76c71705c79c8 Mon Sep 17 00:00:00 2001
From: "Christiane (Tina) Heiligers"
Date: Fri, 18 Dec 2020 10:53:34 -0700
Subject: [PATCH 04/40] Migrates elasticsearch client in the settings usage
collector (#86397)
---
.../xpack_legacy/server/routes/settings.test.ts | 10 ++++++++--
x-pack/plugins/xpack_legacy/server/routes/settings.ts | 7 +++----
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/x-pack/plugins/xpack_legacy/server/routes/settings.test.ts b/x-pack/plugins/xpack_legacy/server/routes/settings.test.ts
index 5d22e22ee0eb6..5d3a2c105c4a4 100644
--- a/x-pack/plugins/xpack_legacy/server/routes/settings.test.ts
+++ b/x-pack/plugins/xpack_legacy/server/routes/settings.test.ts
@@ -24,6 +24,12 @@ import { registerSettingsRoute } from './settings';
type HttpService = ReturnType;
type HttpSetup = UnwrapPromise>;
+export function mockGetClusterInfo(clusterInfo: any) {
+ const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
+ // @ts-ignore we only care about the response body
+ esClient.info.mockResolvedValue({ body: { ...clusterInfo } });
+ return esClient;
+}
describe('/api/settings', () => {
let server: HttpService;
let httpSetup: HttpSetup;
@@ -31,7 +37,7 @@ describe('/api/settings', () => {
let mockApiCaller: jest.Mocked;
beforeEach(async () => {
- mockApiCaller = jest.fn().mockResolvedValue({ cluster_uuid: 'yyy-yyyyy' });
+ mockApiCaller = jest.fn();
server = createHttpServer();
httpSetup = await server.setup({
context: contextServiceMock.createSetupContract({
@@ -43,7 +49,7 @@ describe('/api/settings', () => {
},
},
client: {
- asCurrentUser: elasticsearchServiceMock.createScopedClusterClient().asCurrentUser,
+ asCurrentUser: mockGetClusterInfo({ cluster_uuid: 'yyy-yyyyy' }),
},
},
savedObjects: {
diff --git a/x-pack/plugins/xpack_legacy/server/routes/settings.ts b/x-pack/plugins/xpack_legacy/server/routes/settings.ts
index 9a30ca30616b7..93dc6898f0c2e 100644
--- a/x-pack/plugins/xpack_legacy/server/routes/settings.ts
+++ b/x-pack/plugins/xpack_legacy/server/routes/settings.ts
@@ -58,9 +58,9 @@ export function registerSettingsRoute({
const settings =
(await settingsCollector.fetch(collectorFetchContext)) ??
settingsCollector.getEmailValueStructure(null);
- const { cluster_uuid: uuid } = await callAsCurrentUser('info', {
- filterPath: 'cluster_uuid',
- });
+
+ const { body } = await collectorFetchContext.esClient.info({ filter_path: 'cluster_uuid' });
+ const uuid: string = body.cluster_uuid;
const overallStatus = await overallStatus$.pipe(first()).toPromise();
@@ -76,7 +76,6 @@ export function registerSettingsRoute({
snapshot: SNAPSHOT_REGEX.test(config.kibanaVersion),
status: ServiceStatusToLegacyState[overallStatus.level.toString()],
};
-
return res.ok({
body: {
cluster_uuid: uuid,
From 6a517411ebcdba1499e210111868e5aa925b07d9 Mon Sep 17 00:00:00 2001
From: "Christiane (Tina) Heiligers"
Date: Fri, 18 Dec 2020 10:55:46 -0700
Subject: [PATCH 05/40] Migrates elasticsearch client in the
kibana_usage_collector (#86406)
---
.../kibana/get_saved_object_counts.test.ts | 23 ++++++++++++++-----
.../kibana/get_saved_object_counts.ts | 8 +++----
.../server/collectors/kibana/index.test.ts | 7 ++++--
.../kibana/kibana_usage_collector.ts | 4 ++--
4 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.test.ts b/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.test.ts
index a7681e1766427..64f1088dc3392 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.test.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.test.ts
@@ -16,14 +16,25 @@
* specific language governing permissions and limitations
* under the License.
*/
-
+import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks';
import { getSavedObjectsCounts } from './get_saved_object_counts';
+export function mockGetSavedObjectsCounts(params: any) {
+ const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
+ esClient.search.mockResolvedValue(
+ // @ts-ignore we only care about the response body
+ {
+ body: { ...params },
+ }
+ );
+ return esClient;
+}
+
describe('getSavedObjectsCounts', () => {
test('Get all the saved objects equal to 0 because no results were found', async () => {
- const callCluster = jest.fn(() => ({}));
+ const esClient = mockGetSavedObjectsCounts({});
- const results = await getSavedObjectsCounts(callCluster as any, '.kibana');
+ const results = await getSavedObjectsCounts(esClient, '.kibana');
expect(results).toStrictEqual({
dashboard: { total: 0 },
visualization: { total: 0 },
@@ -35,7 +46,7 @@ describe('getSavedObjectsCounts', () => {
});
test('Merge the zeros with the results', async () => {
- const callCluster = jest.fn(() => ({
+ const esClient = mockGetSavedObjectsCounts({
aggregations: {
types: {
buckets: [
@@ -46,9 +57,9 @@ describe('getSavedObjectsCounts', () => {
],
},
},
- }));
+ });
- const results = await getSavedObjectsCounts(callCluster as any, '.kibana');
+ const results = await getSavedObjectsCounts(esClient, '.kibana');
expect(results).toStrictEqual({
dashboard: { total: 1 },
visualization: { total: 0 },
diff --git a/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts b/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts
index e88d90fe5b24b..65cc3643a88cb 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts
@@ -27,7 +27,7 @@
*/
import { snakeCase } from 'lodash';
-import { LegacyAPICaller } from 'kibana/server';
+import { ElasticsearchClient } from 'src/core/server';
const TYPES = [
'dashboard',
@@ -48,7 +48,7 @@ export interface KibanaSavedObjectCounts {
}
export async function getSavedObjectsCounts(
- callCluster: LegacyAPICaller,
+ esClient: ElasticsearchClient,
kibanaIndex: string // Typically '.kibana'. We might need a way to obtain it from the SavedObjects client (or the SavedObjects client to provide a way to run aggregations?)
): Promise {
const savedObjectCountSearchParams = {
@@ -67,9 +67,9 @@ export async function getSavedObjectsCounts(
},
},
};
- const resp = await callCluster('search', savedObjectCountSearchParams);
+ const { body } = await esClient.search(savedObjectCountSearchParams);
const buckets: Array<{ key: string; doc_count: number }> =
- resp.aggregations?.types?.buckets || [];
+ body.aggregations?.types?.buckets || [];
// Initialise the object with all zeros for all the types
const allZeros: KibanaSavedObjectCounts = TYPES.reduce(
diff --git a/src/plugins/kibana_usage_collection/server/collectors/kibana/index.test.ts b/src/plugins/kibana_usage_collection/server/collectors/kibana/index.test.ts
index 83cac1d456a3a..dee9ca4d32c5f 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/kibana/index.test.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/kibana/index.test.ts
@@ -20,12 +20,13 @@
import {
loggingSystemMock,
pluginInitializerContextConfigMock,
+ elasticsearchServiceMock,
} from '../../../../../core/server/mocks';
import {
Collector,
+ createCollectorFetchContextMock,
createUsageCollectionSetupMock,
} from '../../../../usage_collection/server/usage_collection.mock';
-import { createCollectorFetchContextMock } from '../../../../usage_collection/server/mocks';
import { registerKibanaUsageCollector } from './';
const logger = loggingSystemMock.createLogger();
@@ -43,7 +44,9 @@ describe('telemetry_kibana', () => {
const getMockFetchClients = (hits?: unknown[]) => {
const fetchParamsMock = createCollectorFetchContextMock();
- fetchParamsMock.callCluster.mockResolvedValue({ hits: { hits } });
+ const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
+ esClient.search.mockResolvedValue({ body: { hits: { hits } } } as any);
+ fetchParamsMock.esClient = esClient;
return fetchParamsMock;
};
diff --git a/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts
index 6c2e0a2c926ad..5dd39d172e1c2 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts
@@ -43,13 +43,13 @@ export function getKibanaUsageCollector(
graph_workspace: { total: { type: 'long' } },
timelion_sheet: { total: { type: 'long' } },
},
- async fetch({ callCluster }) {
+ async fetch({ esClient }) {
const {
kibana: { index },
} = await legacyConfig$.pipe(take(1)).toPromise();
return {
index,
- ...(await getSavedObjectsCounts(callCluster, index)),
+ ...(await getSavedObjectsCounts(esClient, index)),
};
},
});
From fc7ae0e1a682748c67bbe81fd8de76b148742dc2 Mon Sep 17 00:00:00 2001
From: Angela Chuang <6295984+angorayc@users.noreply.github.com>
Date: Fri, 18 Dec 2020 18:02:19 +0000
Subject: [PATCH 06/40] [Security Solution] Fix 'disable usage data here.' link
(#86452)
* fix 'disable usage data here.' link
* update snapshot
* update link
* update mocks
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
src/plugins/telemetry/common/constants.ts | 2 +-
.../opted_in_notice_banner.test.tsx.snap | 2 +-
.../components/opted_in_notice_banner.test.tsx | 12 ++++++++++--
.../public/components/opted_in_notice_banner.tsx | 8 ++++++--
src/plugins/telemetry/public/mocks.ts | 1 +
src/plugins/telemetry/public/plugin.ts | 1 +
.../render_opted_in_notice_banner.test.ts | 4 ++++
.../render_opted_in_notice_banner.tsx | 5 +++--
.../telemetry_notifications.ts | 6 +++++-
9 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts
index fc77332c18fc9..2fa8b32f68291 100644
--- a/src/plugins/telemetry/common/constants.ts
+++ b/src/plugins/telemetry/common/constants.ts
@@ -49,7 +49,7 @@ export const LOCALSTORAGE_KEY = 'telemetry.data';
/**
* Link to Advanced Settings.
*/
-export const PATH_TO_ADVANCED_SETTINGS = 'management/kibana/settings';
+export const PATH_TO_ADVANCED_SETTINGS = '/app/management/kibana/settings';
/**
* Link to the Elastic Telemetry privacy statement.
diff --git a/src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap b/src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap
index 62998da73d6f9..897e3b2761c74 100644
--- a/src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap
+++ b/src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap
@@ -10,7 +10,7 @@ exports[`OptInDetailsComponent renders as expected 1`] = `
values={
Object {
"disableLink":
{
it('renders as expected', () => {
- expect(shallowWithIntl( {}} />)).toMatchSnapshot();
+ expect(
+ shallowWithIntl( {}} http={mockHttp} />)
+ ).toMatchSnapshot();
});
it('fires the "onSeenBanner" prop when a link is clicked', () => {
const onLinkClick = jest.fn();
- const component = shallowWithIntl( );
+ const component = shallowWithIntl(
+
+ );
const button = component.findWhere((n) => n.type() === EuiButton);
diff --git a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx
index 090893964c881..46ae17171203c 100644
--- a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx
+++ b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx
@@ -24,14 +24,18 @@ import { EuiButton, EuiLink, EuiCallOut, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { PATH_TO_ADVANCED_SETTINGS, PRIVACY_STATEMENT_URL } from '../../common/constants';
+import { HttpSetup } from '../../../../core/public';
interface Props {
+ http: HttpSetup;
onSeenBanner: () => any;
}
export class OptedInNoticeBanner extends React.PureComponent {
render() {
- const { onSeenBanner } = this.props;
+ const { onSeenBanner, http } = this.props;
+ const basePath = http.basePath.get();
+
const bannerTitle = i18n.translate('telemetry.telemetryOptedInNoticeTitle', {
defaultMessage: 'Help us improve the Elastic Stack',
});
@@ -56,7 +60,7 @@ export class OptedInNoticeBanner extends React.PureComponent {
),
disableLink: (
-
+
{
it('adds a banner to banners with priority of 10000', () => {
const bannerID = 'brucer-wayne';
const overlays = overlayServiceMock.createStartContract();
+ const mockHttp = httpServiceMock.createStartContract();
overlays.banners.add.mockReturnValue(bannerID);
const returnedBannerId = renderOptedInNoticeBanner({
+ http: mockHttp,
onSeen: jest.fn(),
overlays,
});
diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx
index e63e46af6e8ca..e1feea4b6cbe1 100644
--- a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx
+++ b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx
@@ -23,11 +23,12 @@ import { OptedInNoticeBanner } from '../../components/opted_in_notice_banner';
import { toMountPoint } from '../../../../kibana_react/public';
interface RenderBannerConfig {
+ http: CoreStart['http'];
overlays: CoreStart['overlays'];
onSeen: () => void;
}
-export function renderOptedInNoticeBanner({ onSeen, overlays }: RenderBannerConfig) {
- const mount = toMountPoint( );
+export function renderOptedInNoticeBanner({ onSeen, overlays, http }: RenderBannerConfig) {
+ const mount = toMountPoint( );
const bannerId = overlays.banners.add(mount, 10000);
return bannerId;
diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts
index fc44a4db7cf5e..6ebbfcfb91336 100644
--- a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts
+++ b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts
@@ -23,18 +23,21 @@ import { renderOptInBanner } from './render_opt_in_banner';
import { TelemetryService } from '../telemetry_service';
interface TelemetryNotificationsConstructor {
+ http: CoreStart['http'];
overlays: CoreStart['overlays'];
telemetryService: TelemetryService;
}
export class TelemetryNotifications {
+ private readonly http: CoreStart['http'];
private readonly overlays: CoreStart['overlays'];
private readonly telemetryService: TelemetryService;
private optedInNoticeBannerId?: string;
private optInBannerId?: string;
- constructor({ overlays, telemetryService }: TelemetryNotificationsConstructor) {
+ constructor({ http, overlays, telemetryService }: TelemetryNotificationsConstructor) {
this.telemetryService = telemetryService;
+ this.http = http;
this.overlays = overlays;
}
@@ -46,6 +49,7 @@ export class TelemetryNotifications {
public renderOptedInNoticeBanner = (): void => {
const bannerId = renderOptedInNoticeBanner({
+ http: this.http,
onSeen: this.setOptedInNoticeSeen,
overlays: this.overlays,
});
From d73af3282f6cf9c9f8520999982c5085db4a7cb4 Mon Sep 17 00:00:00 2001
From: Constance
Date: Fri, 18 Dec 2020 10:16:27 -0800
Subject: [PATCH 07/40] [Enterprise Search] Basic DocumentCreation creation
mode modal views (#86056)
* Add ApiCodeExample modal component
- Previously lived in EngineOverview / Onboarding
* Add basic PasteJsonText component
* Add basic UploadJsonFile component
* [Refactor] Have all modal components manage their own ModalHeader & ModalFooters
- Per feedback from Casey
+ Update DocumentCreationModal to use switch
* Set basic empty/disabled validation on ModalFooter continue buttons
* Update x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx
Co-authored-by: Jason Stoltzfus
* [PR feedback] Typescript improvements
* [PR feedback] Remove need for hasFile reducer
- by storing either 1 file or null
- which gets around the stored FileList reference not triggering a rerender/change
Co-authored-by: Jason Stoltzfus
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../document_creation/constants.tsx | 46 ++++++-
.../api_code_example.test.tsx | 75 ++++++++++
.../api_code_example.tsx | 128 ++++++++++++++++++
.../creation_mode_components/index.ts | 10 ++
.../paste_json_text.scss | 9 ++
.../paste_json_text.test.tsx | 80 +++++++++++
.../paste_json_text.tsx | 101 ++++++++++++++
.../show_creation_modes.test.tsx | 37 +++++
.../show_creation_modes.tsx | 45 ++++++
.../upload_json_file.test.tsx | 85 ++++++++++++
.../upload_json_file.tsx | 98 ++++++++++++++
.../document_creation_logic.test.ts | 33 +++++
.../document_creation_logic.ts | 20 +++
.../document_creation_modal.test.tsx | 45 +++---
.../document_creation_modal.tsx | 77 ++++++-----
15 files changed, 830 insertions(+), 59 deletions(-)
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.scss
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx
index 5406a90a75a35..c4237da0d0e80 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx
@@ -4,4 +4,48 @@
* you may not use this file except in compliance with the Elastic License.
*/
-// TODO: This will be used shortly in an upcoming PR
+import { i18n } from '@kbn/i18n';
+
+export const MODAL_CANCEL_BUTTON = i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.modalCancel',
+ { defaultMessage: 'Cancel' }
+);
+export const MODAL_CONTINUE_BUTTON = i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.modalContinue',
+ { defaultMessage: 'Continue' }
+);
+
+// This is indented the way it is to work with ApiCodeExample.
+// Use dedent() when calling this alone
+export const DOCUMENTS_API_JSON_EXAMPLE = `[
+ {
+ "id": "park_rocky-mountain",
+ "title": "Rocky Mountain",
+ "description": "Bisected north to south by the Continental Divide, this portion of the Rockies has ecosystems varying from over 150 riparian lakes to montane and subalpine forests to treeless alpine tundra. Wildlife including mule deer, bighorn sheep, black bears, and cougars inhabit its igneous mountains and glacial valleys. Longs Peak, a classic Colorado fourteener, and the scenic Bear Lake are popular destinations, as well as the historic Trail Ridge Road, which reaches an elevation of more than 12,000 feet (3,700 m).",
+ "nps_link": "https://www.nps.gov/romo/index.htm",
+ "states": [
+ "Colorado"
+ ],
+ "visitors": 4517585,
+ "world_heritage_site": false,
+ "location": "40.4,-105.58",
+ "acres": 265795.2,
+ "square_km": 1075.6,
+ "date_established": "1915-01-26T06:00:00Z"
+ },
+ {
+ "id": "park_saguaro",
+ "title": "Saguaro",
+ "description": "Split into the separate Rincon Mountain and Tucson Mountain districts, this park is evidence that the dry Sonoran Desert is still home to a great variety of life spanning six biotic communities. Beyond the namesake giant saguaro cacti, there are barrel cacti, chollas, and prickly pears, as well as lesser long-nosed bats, spotted owls, and javelinas.",
+ "nps_link": "https://www.nps.gov/sagu/index.htm",
+ "states": [
+ "Arizona"
+ ],
+ "visitors": 820426,
+ "world_heritage_site": false,
+ "location": "32.25,-110.5",
+ "acres": 91715.72,
+ "square_km": 371.2,
+ "date_established": "1994-10-14T05:00:00Z"
+ }
+ ]`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx
new file mode 100644
index 0000000000000..2dd46419528c1
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx
@@ -0,0 +1,75 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import '../../../../__mocks__/enterprise_search_url.mock';
+import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock';
+
+import React from 'react';
+import { shallow, ShallowWrapper } from 'enzyme';
+import { EuiCode, EuiCodeBlock, EuiButtonEmpty } from '@elastic/eui';
+
+import { ApiCodeExample, ModalHeader, ModalBody, ModalFooter } from './api_code_example';
+
+describe('ApiCodeExample', () => {
+ const values = {
+ engineName: 'test-engine',
+ engine: { apiKey: 'test-key' },
+ };
+ const actions = {
+ closeDocumentCreation: jest.fn(),
+ };
+
+ beforeAll(() => {
+ setMockValues(values);
+ setMockActions(actions);
+ });
+
+ it('renders', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(ModalHeader)).toHaveLength(1);
+ expect(wrapper.find(ModalBody)).toHaveLength(1);
+ expect(wrapper.find(ModalFooter)).toHaveLength(1);
+ });
+
+ describe('ModalHeader', () => {
+ it('renders', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find('h2').text()).toEqual('Indexing by API');
+ });
+ });
+
+ describe('ModalBody', () => {
+ let wrapper: ShallowWrapper;
+
+ beforeAll(() => {
+ wrapper = shallow( );
+ });
+
+ it('renders with the full remote Enterprise Search API URL', () => {
+ expect(wrapper.find(EuiCode).dive().dive().text()).toEqual(
+ 'http://localhost:3002/api/as/v1/engines/test-engine/documents'
+ );
+ expect(wrapper.find(EuiCodeBlock).dive().dive().text()).toEqual(
+ expect.stringContaining('http://localhost:3002/api/as/v1/engines/test-engine/documents')
+ );
+ });
+
+ it('renders with the API key', () => {
+ expect(wrapper.find(EuiCodeBlock).dive().dive().text()).toEqual(
+ expect.stringContaining('test-key')
+ );
+ });
+ });
+
+ describe('ModalFooter', () => {
+ it('closes the modal', () => {
+ const wrapper = shallow( );
+
+ wrapper.find(EuiButtonEmpty).simulate('click');
+ expect(actions.closeDocumentCreation).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx
new file mode 100644
index 0000000000000..1dd57ffe8bc01
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx
@@ -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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import dedent from 'dedent';
+import React from 'react';
+import { useValues, useActions } from 'kea';
+
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiButtonEmpty,
+ EuiText,
+ EuiLink,
+ EuiSpacer,
+ EuiPanel,
+ EuiBadge,
+ EuiCode,
+ EuiCodeBlock,
+} from '@elastic/eui';
+
+import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url';
+import { EngineLogic } from '../../engine';
+import { EngineDetails } from '../../engine/types';
+
+import { DOCS_PREFIX } from '../../../routes';
+import { DOCUMENTS_API_JSON_EXAMPLE, MODAL_CANCEL_BUTTON } from '../constants';
+import { DocumentCreationLogic } from '../';
+
+export const ApiCodeExample: React.FC = () => (
+ <>
+
+
+
+ >
+);
+
+export const ModalHeader: React.FC = () => {
+ return (
+
+
+
+ {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.api.title', {
+ defaultMessage: 'Indexing by API',
+ })}
+
+
+
+ );
+};
+
+export const ModalBody: React.FC = () => {
+ const { engineName, engine } = useValues(EngineLogic);
+ const { apiKey } = engine as EngineDetails;
+
+ const documentsApiUrl = getEnterpriseSearchUrl(`/api/as/v1/engines/${engineName}/documents`);
+
+ return (
+
+
+
+
+ documents API
+
+ ),
+ clientLibrariesLink: (
+
+ client libraries
+
+ ),
+ }}
+ />
+
+
+ {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.api.example', {
+ defaultMessage:
+ 'To see the API in action, you can experiment with the example request below using a command line or a client library.',
+ })}
+
+
+
+
+ POST
+ {documentsApiUrl}
+
+
+ {dedent(`
+ curl -X POST '${documentsApiUrl}'
+ -H 'Content-Type: application/json'
+ -H 'Authorization: Bearer ${apiKey}'
+ -d '${DOCUMENTS_API_JSON_EXAMPLE}'
+ # Returns
+ # [
+ # {
+ # "id": "park_rocky-mountain",
+ # "errors": []
+ # },
+ # {
+ # "id": "park_saguaro",
+ # "errors": []
+ # }
+ # ]
+ `)}
+
+
+ );
+};
+
+export const ModalFooter: React.FC = () => {
+ const { closeDocumentCreation } = useActions(DocumentCreationLogic);
+
+ return (
+
+ {MODAL_CANCEL_BUTTON}
+
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts
new file mode 100644
index 0000000000000..b9a6f2b3e750f
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts
@@ -0,0 +1,10 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { ShowCreationModes } from './show_creation_modes';
+export { ApiCodeExample } from './api_code_example';
+export { PasteJsonText } from './paste_json_text';
+export { UploadJsonFile } from './upload_json_file';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.scss
new file mode 100644
index 0000000000000..cca179e8c0608
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.scss
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+.pasteJsonTextArea {
+ font-family: $euiCodeFontFamily;
+}
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx
new file mode 100644
index 0000000000000..ede1529c049d7
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx
@@ -0,0 +1,80 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock';
+import { rerender } from '../../../../__mocks__';
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { EuiTextArea, EuiButtonEmpty, EuiButton } from '@elastic/eui';
+
+import { PasteJsonText, ModalHeader, ModalBody, ModalFooter } from './paste_json_text';
+
+describe('PasteJsonText', () => {
+ const values = {
+ textInput: 'hello world',
+ configuredLimits: {
+ engine: {
+ maxDocumentByteSize: 102400,
+ },
+ },
+ };
+ const actions = {
+ setTextInput: jest.fn(),
+ closeDocumentCreation: jest.fn(),
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ setMockValues(values);
+ setMockActions(actions);
+ });
+
+ it('renders', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(ModalHeader)).toHaveLength(1);
+ expect(wrapper.find(ModalBody)).toHaveLength(1);
+ expect(wrapper.find(ModalFooter)).toHaveLength(1);
+ });
+
+ describe('ModalHeader', () => {
+ it('renders', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find('h2').text()).toEqual('Create documents');
+ });
+ });
+
+ describe('ModalBody', () => {
+ it('renders and updates the textarea value', () => {
+ setMockValues({ ...values, textInput: 'lorem ipsum' });
+ const wrapper = shallow( );
+ const textarea = wrapper.find(EuiTextArea);
+
+ expect(textarea.prop('value')).toEqual('lorem ipsum');
+
+ textarea.simulate('change', { target: { value: 'dolor sit amet' } });
+ expect(actions.setTextInput).toHaveBeenCalledWith('dolor sit amet');
+ });
+ });
+
+ describe('ModalFooter', () => {
+ it('closes the modal', () => {
+ const wrapper = shallow( );
+
+ wrapper.find(EuiButtonEmpty).simulate('click');
+ expect(actions.closeDocumentCreation).toHaveBeenCalled();
+ });
+
+ it('disables/enables the Continue button based on whether text has been entered', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(false);
+
+ setMockValues({ ...values, textInput: '' });
+ rerender(wrapper);
+ expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true);
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx
new file mode 100644
index 0000000000000..614704701222b
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx
@@ -0,0 +1,101 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { useValues, useActions } from 'kea';
+
+import { i18n } from '@kbn/i18n';
+import {
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiButton,
+ EuiButtonEmpty,
+ EuiTextArea,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+
+import { AppLogic } from '../../../app_logic';
+
+import { MODAL_CANCEL_BUTTON, MODAL_CONTINUE_BUTTON } from '../constants';
+import { DocumentCreationLogic } from '../';
+
+import './paste_json_text.scss';
+
+export const PasteJsonText: React.FC = () => (
+ <>
+
+
+
+ >
+);
+
+export const ModalHeader: React.FC = () => {
+ return (
+
+
+
+ {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.title', {
+ defaultMessage: 'Create documents',
+ })}
+
+
+
+ );
+};
+
+export const ModalBody: React.FC = () => {
+ const { configuredLimits } = useValues(AppLogic);
+ const maxDocumentByteSize = configuredLimits?.engine?.maxDocumentByteSize;
+
+ const { textInput } = useValues(DocumentCreationLogic);
+ const { setTextInput } = useActions(DocumentCreationLogic);
+
+ return (
+
+
+
+ {i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.description',
+ {
+ defaultMessage:
+ 'Paste an array of JSON documents. Ensure the JSON is valid and that each document object is less than {maxDocumentByteSize} bytes.',
+ values: { maxDocumentByteSize },
+ }
+ )}
+
+
+
+ setTextInput(e.target.value)}
+ aria-label={i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.label',
+ { defaultMessage: 'Paste JSON here' }
+ )}
+ className="pasteJsonTextArea"
+ fullWidth
+ rows={12}
+ />
+
+ );
+};
+
+export const ModalFooter: React.FC = () => {
+ const { textInput } = useValues(DocumentCreationLogic);
+ const { closeDocumentCreation } = useActions(DocumentCreationLogic);
+
+ return (
+
+ {MODAL_CANCEL_BUTTON}
+
+ {MODAL_CONTINUE_BUTTON}
+
+
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx
new file mode 100644
index 0000000000000..eadcf6df473e5
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { setMockActions } from '../../../../__mocks__/kea.mock';
+
+import React from 'react';
+import { shallow, ShallowWrapper } from 'enzyme';
+import { EuiButtonEmpty } from '@elastic/eui';
+
+import { DocumentCreationButtons } from '../';
+import { ShowCreationModes } from './';
+
+describe('ShowCreationModes', () => {
+ const actions = {
+ closeDocumentCreation: jest.fn(),
+ };
+ let wrapper: ShallowWrapper;
+
+ beforeAll(() => {
+ jest.clearAllMocks();
+ setMockActions(actions);
+ wrapper = shallow( );
+ });
+
+ it('renders', () => {
+ expect(wrapper.find('h2').text()).toEqual('Add new documents');
+ expect(wrapper.find(DocumentCreationButtons)).toHaveLength(1);
+ });
+
+ it('closes the modal', () => {
+ wrapper.find(EuiButtonEmpty).simulate('click');
+ expect(actions.closeDocumentCreation).toHaveBeenCalled();
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx
new file mode 100644
index 0000000000000..1f7c4db83ab06
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { useActions } from 'kea';
+
+import { i18n } from '@kbn/i18n';
+import {
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiButtonEmpty,
+} from '@elastic/eui';
+
+import { MODAL_CANCEL_BUTTON } from '../constants';
+import { DocumentCreationLogic, DocumentCreationButtons } from '../';
+
+export const ShowCreationModes: React.FC = () => {
+ const { closeDocumentCreation } = useActions(DocumentCreationLogic);
+
+ return (
+ <>
+
+
+
+ {i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.showCreationModes.title',
+ { defaultMessage: 'Add new documents' }
+ )}
+
+
+
+
+
+
+
+ {MODAL_CANCEL_BUTTON}
+
+ >
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx
new file mode 100644
index 0000000000000..dae085617cad8
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock';
+import { rerender } from '../../../../__mocks__';
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { EuiFilePicker, EuiButtonEmpty, EuiButton } from '@elastic/eui';
+
+import { UploadJsonFile, ModalHeader, ModalBody, ModalFooter } from './upload_json_file';
+
+describe('UploadJsonFile', () => {
+ const mockFile = new File(['mock'], 'mock.json', { type: 'application/json' });
+ const values = {
+ fileInput: null,
+ configuredLimits: {
+ engine: {
+ maxDocumentByteSize: 102400,
+ },
+ },
+ };
+ const actions = {
+ setFileInput: jest.fn(),
+ closeDocumentCreation: jest.fn(),
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ setMockValues(values);
+ setMockActions(actions);
+ });
+
+ it('renders', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(ModalHeader)).toHaveLength(1);
+ expect(wrapper.find(ModalBody)).toHaveLength(1);
+ expect(wrapper.find(ModalFooter)).toHaveLength(1);
+ });
+
+ describe('ModalHeader', () => {
+ it('renders', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find('h2').text()).toEqual('Drag and drop .json');
+ });
+ });
+
+ describe('ModalBody', () => {
+ it('updates fileInput when files are added & removed', () => {
+ const wrapper = shallow( );
+
+ wrapper.find(EuiFilePicker).simulate('change', [mockFile]);
+ expect(actions.setFileInput).toHaveBeenCalledWith(mockFile);
+
+ wrapper.find(EuiFilePicker).simulate('change', []);
+ expect(actions.setFileInput).toHaveBeenCalledWith(null);
+ });
+ });
+
+ describe('ModalFooter', () => {
+ it('closes the modal', () => {
+ const wrapper = shallow( );
+
+ wrapper.find(EuiButtonEmpty).simulate('click');
+ expect(actions.closeDocumentCreation).toHaveBeenCalled();
+ });
+
+ it('disables/enables the Continue button based on whether files have been uploaded', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true);
+
+ setMockValues({ ...values, fineInput: mockFile });
+ rerender(wrapper);
+ expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true);
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx
new file mode 100644
index 0000000000000..d4c005d5cfa2b
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx
@@ -0,0 +1,98 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { useValues, useActions } from 'kea';
+
+import { i18n } from '@kbn/i18n';
+import {
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiButton,
+ EuiButtonEmpty,
+ EuiFilePicker,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+
+import { AppLogic } from '../../../app_logic';
+
+import { MODAL_CANCEL_BUTTON, MODAL_CONTINUE_BUTTON } from '../constants';
+import { DocumentCreationLogic } from '../';
+
+export const UploadJsonFile: React.FC = () => (
+ <>
+
+
+
+ >
+);
+
+export const ModalHeader: React.FC = () => {
+ return (
+
+
+
+ {i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.title',
+ { defaultMessage: 'Drag and drop .json' }
+ )}
+
+
+
+ );
+};
+
+export const ModalBody: React.FC = () => {
+ const { configuredLimits } = useValues(AppLogic);
+ const maxDocumentByteSize = configuredLimits?.engine?.maxDocumentByteSize;
+
+ const { setFileInput } = useActions(DocumentCreationLogic);
+
+ return (
+
+
+
+ {i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.label',
+ {
+ defaultMessage:
+ 'If you have a .json file, drag and drop or upload it. Ensure the JSON is valid and that each document object is less than {maxDocumentByteSize} bytes.',
+ values: { maxDocumentByteSize },
+ }
+ )}
+
+
+
+ setFileInput(files?.length ? files[0] : null)}
+ accept="application/json"
+ fullWidth
+ />
+
+ );
+};
+
+export const ModalFooter: React.FC = () => {
+ const { fileInput } = useValues(DocumentCreationLogic);
+ const { closeDocumentCreation } = useActions(DocumentCreationLogic);
+
+ return (
+
+ {MODAL_CANCEL_BUTTON}
+
+ {MODAL_CONTINUE_BUTTON}
+
+
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts
index ff38ab5add367..1145d7853cb1a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts
@@ -5,7 +5,9 @@
*/
import { resetContext } from 'kea';
+import dedent from 'dedent';
+import { DOCUMENTS_API_JSON_EXAMPLE } from './constants';
import { DocumentCreationStep } from './types';
import { DocumentCreationLogic } from './';
@@ -14,7 +16,10 @@ describe('DocumentCreationLogic', () => {
isDocumentCreationOpen: false,
creationMode: 'text',
creationStep: DocumentCreationStep.AddDocuments,
+ textInput: dedent(DOCUMENTS_API_JSON_EXAMPLE),
+ fileInput: null,
};
+ const mockFile = new File(['mockFile'], 'mockFile.json');
const mount = () => {
resetContext({});
@@ -130,5 +135,33 @@ describe('DocumentCreationLogic', () => {
});
});
});
+
+ describe('setTextInput', () => {
+ describe('textInput', () => {
+ it('should be set to the provided value', () => {
+ mount();
+ DocumentCreationLogic.actions.setTextInput('hello world');
+
+ expect(DocumentCreationLogic.values).toEqual({
+ ...DEFAULT_VALUES,
+ textInput: 'hello world',
+ });
+ });
+ });
+ });
+
+ describe('setFileInput', () => {
+ describe('fileInput', () => {
+ it('should be set to the provided value', () => {
+ mount();
+ DocumentCreationLogic.actions.setFileInput(mockFile);
+
+ expect(DocumentCreationLogic.values).toEqual({
+ ...DEFAULT_VALUES,
+ fileInput: mockFile,
+ });
+ });
+ });
+ });
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts
index 26f7a1f3d50ec..a5e015391d8fd 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts
@@ -5,13 +5,17 @@
*/
import { kea, MakeLogicType } from 'kea';
+import dedent from 'dedent';
+import { DOCUMENTS_API_JSON_EXAMPLE } from './constants';
import { DocumentCreationMode, DocumentCreationStep } from './types';
interface DocumentCreationValues {
isDocumentCreationOpen: boolean;
creationMode: DocumentCreationMode;
creationStep: DocumentCreationStep;
+ textInput: string;
+ fileInput: File | null;
}
interface DocumentCreationActions {
@@ -19,6 +23,8 @@ interface DocumentCreationActions {
openDocumentCreation(creationMode: DocumentCreationMode): { creationMode: DocumentCreationMode };
closeDocumentCreation(): void;
setCreationStep(creationStep: DocumentCreationStep): { creationStep: DocumentCreationStep };
+ setTextInput(textInput: string): { textInput: string };
+ setFileInput(fileInput: File | null): { fileInput: File | null };
}
export const DocumentCreationLogic = kea<
@@ -30,6 +36,8 @@ export const DocumentCreationLogic = kea<
openDocumentCreation: (creationMode) => ({ creationMode }),
closeDocumentCreation: () => null,
setCreationStep: (creationStep) => ({ creationStep }),
+ setTextInput: (textInput) => ({ textInput }),
+ setFileInput: (fileInput) => ({ fileInput }),
}),
reducers: () => ({
isDocumentCreationOpen: [
@@ -54,5 +62,17 @@ export const DocumentCreationLogic = kea<
setCreationStep: (_, { creationStep }) => creationStep,
},
],
+ textInput: [
+ dedent(DOCUMENTS_API_JSON_EXAMPLE),
+ {
+ setTextInput: (_, { textInput }) => textInput,
+ },
+ ],
+ fileInput: [
+ null,
+ {
+ setFileInput: (_, { fileInput }) => fileInput,
+ },
+ ],
}),
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.test.tsx
index a00aed96a6fbc..a0bca62dc7419 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.test.tsx
@@ -8,10 +8,17 @@ import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock';
import React from 'react';
import { shallow } from 'enzyme';
-import { EuiModal, EuiModalBody } from '@elastic/eui';
-
+import { EuiModal } from '@elastic/eui';
+
+import {
+ ShowCreationModes,
+ ApiCodeExample,
+ PasteJsonText,
+ UploadJsonFile,
+} from './creation_mode_components';
import { DocumentCreationStep } from './types';
-import { DocumentCreationModal, DocumentCreationButtons } from './';
+
+import { DocumentCreationModal, ModalContent } from './document_creation_modal';
describe('DocumentCreationModal', () => {
const values = {
@@ -44,58 +51,58 @@ describe('DocumentCreationModal', () => {
expect(wrapper.isEmptyRender()).toBe(true);
});
- describe('modal content', () => {
- it('renders document creation mode buttons', () => {
+ describe('ModalContent', () => {
+ it('renders ShowCreationModes', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowCreationModes });
- const wrapper = shallow( );
+ const wrapper = shallow( );
- expect(wrapper.find(DocumentCreationButtons)).toHaveLength(1);
+ expect(wrapper.find(ShowCreationModes)).toHaveLength(1);
});
describe('creation modes', () => {
it('renders ApiCodeExample', () => {
setMockValues({ ...values, creationMode: 'api' });
- const wrapper = shallow( );
+ const wrapper = shallow( );
- expect(wrapper.find(EuiModalBody).dive().text()).toBe('ApiCodeExample'); // TODO: actual component
+ expect(wrapper.find(ApiCodeExample)).toHaveLength(1);
});
it('renders PasteJsonText', () => {
setMockValues({ ...values, creationMode: 'text' });
- const wrapper = shallow( );
+ const wrapper = shallow( );
- expect(wrapper.find(EuiModalBody).dive().text()).toBe('PasteJsonText'); // TODO: actual component
+ expect(wrapper.find(PasteJsonText)).toHaveLength(1);
});
it('renders UploadJsonFile', () => {
setMockValues({ ...values, creationMode: 'file' });
- const wrapper = shallow( );
+ const wrapper = shallow( );
- expect(wrapper.find(EuiModalBody).dive().text()).toBe('UploadJsonFile'); // TODO: actual component
+ expect(wrapper.find(UploadJsonFile)).toHaveLength(1);
});
});
describe('creation steps', () => {
it('renders an error page', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowError });
- const wrapper = shallow( );
+ const wrapper = shallow( );
- expect(wrapper.find(EuiModalBody).dive().text()).toBe('DocumentCreationError'); // TODO: actual component
+ expect(wrapper.text()).toBe('DocumentCreationError'); // TODO: actual component
});
it('renders an error summary', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowErrorSummary });
- const wrapper = shallow( );
+ const wrapper = shallow( );
- expect(wrapper.find(EuiModalBody).dive().text()).toBe('DocumentCreationSummary'); // TODO: actual component
+ expect(wrapper.text()).toBe('DocumentCreationSummary'); // TODO: actual component
});
it('renders a success summary', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowSuccessSummary });
- const wrapper = shallow( );
+ const wrapper = shallow( );
// TODO: Figure out if the error and success summary should remain the same vs different components
- expect(wrapper.find(EuiModalBody).dive().text()).toBe('DocumentCreationSummary'); // TODO: actual component
+ expect(wrapper.text()).toBe('DocumentCreationSummary'); // TODO: actual component
});
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.tsx
index 95ce5456ef9a8..e6662a7c30407 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.tsx
@@ -7,52 +7,51 @@
import React from 'react';
import { useValues, useActions } from 'kea';
-import { i18n } from '@kbn/i18n';
-import {
- EuiOverlayMask,
- EuiModal,
- EuiModalHeader,
- EuiModalHeaderTitle,
- EuiModalBody,
- EuiModalFooter,
-} from '@elastic/eui';
-
-import { DocumentCreationLogic, DocumentCreationButtons } from './';
+import { EuiOverlayMask, EuiModal } from '@elastic/eui';
+
+import { DocumentCreationLogic } from './';
import { DocumentCreationStep } from './types';
+import {
+ ShowCreationModes,
+ ApiCodeExample,
+ PasteJsonText,
+ UploadJsonFile,
+} from './creation_mode_components';
+
export const DocumentCreationModal: React.FC = () => {
const { closeDocumentCreation } = useActions(DocumentCreationLogic);
- const { isDocumentCreationOpen, creationMode, creationStep } = useValues(DocumentCreationLogic);
+ const { isDocumentCreationOpen } = useValues(DocumentCreationLogic);
- if (!isDocumentCreationOpen) return null;
-
- return (
+ return isDocumentCreationOpen ? (
-
-
- {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.modalTitle', {
- defaultMessage: 'Document Import',
- })}
-
-
-
- {creationStep === DocumentCreationStep.ShowError && <>DocumentCreationError>}
- {creationStep === DocumentCreationStep.ShowCreationModes && }
- {creationStep === DocumentCreationStep.AddDocuments && creationMode === 'api' && (
- <>ApiCodeExample>
- )}
- {creationStep === DocumentCreationStep.AddDocuments && creationMode === 'text' && (
- <>PasteJsonText>
- )}
- {creationStep === DocumentCreationStep.AddDocuments && creationMode === 'file' && (
- <>UploadJsonFile>
- )}
- {creationStep === DocumentCreationStep.ShowErrorSummary && <>DocumentCreationSummary>}
- {creationStep === DocumentCreationStep.ShowSuccessSummary && <>DocumentCreationSummary>}
-
-
+
- );
+ ) : null;
+};
+
+export const ModalContent: React.FC = () => {
+ const { creationStep, creationMode } = useValues(DocumentCreationLogic);
+
+ switch (creationStep) {
+ case DocumentCreationStep.ShowCreationModes:
+ return ;
+ case DocumentCreationStep.AddDocuments:
+ switch (creationMode) {
+ case 'api':
+ return ;
+ case 'text':
+ return ;
+ case 'file':
+ return ;
+ }
+ case DocumentCreationStep.ShowError:
+ return <>DocumentCreationError>;
+ case DocumentCreationStep.ShowErrorSummary:
+ return <>DocumentCreationSummary>;
+ case DocumentCreationStep.ShowSuccessSummary:
+ return <>DocumentCreationSummary>;
+ }
};
From e71610630734d6b55df9731f2ee5a2876eed5729 Mon Sep 17 00:00:00 2001
From: Dan Panzarella
Date: Fri, 18 Dec 2020 13:41:05 -0500
Subject: [PATCH 08/40] [Security Solution] Correct Policy Config to current
license level on fetch (#85206)
---
.../common/license/license.ts | 2 +-
.../components/current_license/index.tsx | 29 ++++++++++++
.../policy/store/policy_details/action.ts | 9 +++-
.../policy/store/policy_details/index.test.ts | 44 +++++++++++++++++++
.../policy/store/policy_details/middleware.ts | 1 -
.../policy/store/policy_details/reducer.ts | 8 ++++
.../policy/store/policy_details/selectors.ts | 24 +++++++++-
.../public/management/pages/policy/types.ts | 3 ++
.../with_security_context.tsx | 5 ++-
.../public/management/routes.tsx | 11 +++--
10 files changed, 126 insertions(+), 10 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/common/components/current_license/index.tsx
diff --git a/x-pack/plugins/security_solution/common/license/license.ts b/x-pack/plugins/security_solution/common/license/license.ts
index 2d424ab9c960a..9c093016f4a38 100644
--- a/x-pack/plugins/security_solution/common/license/license.ts
+++ b/x-pack/plugins/security_solution/common/license/license.ts
@@ -51,5 +51,5 @@ export class LicenseService {
}
export const isAtLeast = (license: ILicense | null, level: LicenseType): boolean => {
- return license !== null && license.isAvailable && license.isActive && license.hasAtLeast(level);
+ return !!license && license.isAvailable && license.isActive && license.hasAtLeast(level);
};
diff --git a/x-pack/plugins/security_solution/public/common/components/current_license/index.tsx b/x-pack/plugins/security_solution/public/common/components/current_license/index.tsx
new file mode 100644
index 0000000000000..27d34f5cf418f
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/current_license/index.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React, { FC, memo, useEffect } from 'react';
+import { useDispatch } from 'react-redux';
+import { Dispatch } from 'redux';
+import { licenseService } from '../../hooks/use_license';
+import { AppAction } from '../../store/actions';
+import { ILicense } from '../../../../../licensing/common/types';
+
+export const CurrentLicense: FC = memo(({ children }) => {
+ const dispatch = useDispatch>();
+ useEffect(() => {
+ const subscription = licenseService
+ .getLicenseInformation$()
+ ?.subscribe((licenseInformation: ILicense) => {
+ dispatch({
+ type: 'licenseChanged',
+ payload: licenseInformation,
+ });
+ });
+ return () => subscription?.unsubscribe();
+ }, [dispatch]);
+ return <>{children}>;
+});
+
+CurrentLicense.displayName = 'CurrentLicense';
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts
index bda408cd00e75..573442de807ae 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { ILicense } from '../../../../../../../licensing/common/types';
import { GetAgentStatusResponse } from '../../../../../../../fleet/common/types/rest_spec';
import { PolicyData, UIPolicyConfig } from '../../../../../../common/endpoint/types';
import { ServerApiError } from '../../../../../common/types';
@@ -62,6 +63,11 @@ interface UserClickedPolicyDetailsSaveButton {
type: 'userClickedPolicyDetailsSaveButton';
}
+interface LicenseChanged {
+ type: 'licenseChanged';
+ payload: ILicense;
+}
+
export type PolicyDetailsAction =
| ServerReturnedPolicyDetailsData
| UserClickedPolicyDetailsSaveButton
@@ -70,4 +76,5 @@ export type PolicyDetailsAction =
| ServerReturnedUpdatedPolicyDetailsData
| ServerFailedToReturnPolicyDetailsData
| UserChangedPolicyConfig
- | UserChangedAntivirusRegistration;
+ | UserChangedAntivirusRegistration
+ | LicenseChanged;
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts
index 69c2afbd01960..70ffc1f8a9fc4 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts
@@ -20,6 +20,7 @@ import {
} from '../../../../../common/mock/endpoint';
import { HttpFetchOptions } from 'kibana/public';
import { cloneDeep } from 'lodash';
+import { licenseMock } from '../../../../../../../licensing/common/licensing.mock';
describe('policy details: ', () => {
let store: Store;
@@ -151,6 +152,49 @@ describe('policy details: ', () => {
expect(config!.linux.events.file).toEqual(true);
});
});
+
+ describe('when the policy config has paid features enabled', () => {
+ const CustomMessage = 'Some Popup message change';
+ const Basic = licenseMock.createLicense({ license: { type: 'basic', mode: 'basic' } });
+ const Platinum = licenseMock.createLicense({
+ license: { type: 'platinum', mode: 'platinum' },
+ });
+
+ beforeEach(() => {
+ const config = policyConfig(getState());
+ if (!config) {
+ throw new Error();
+ }
+
+ // have a paid-policy field existing in the store from a previous time
+ const newPayload1 = cloneDeep(config);
+ newPayload1.windows.popup.malware.message = CustomMessage;
+ dispatch({
+ type: 'userChangedPolicyConfig',
+ payload: { policyConfig: newPayload1 },
+ });
+ });
+
+ it('preserves paid fields when license level allows', () => {
+ dispatch({
+ type: 'licenseChanged',
+ payload: Platinum,
+ });
+ const config = policyConfig(getState());
+
+ expect(config.windows.popup.malware.message).toEqual(CustomMessage);
+ });
+
+ it('reverts paid fields to default when license level does not allow', () => {
+ dispatch({
+ type: 'licenseChanged',
+ payload: Basic,
+ });
+ const config = policyConfig(getState());
+
+ expect(config.windows.popup.malware.message).not.toEqual(CustomMessage);
+ });
+ });
});
describe('when saving policy data', () => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts
index f039324b3af64..2f9f0d6723749 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts
@@ -26,7 +26,6 @@ export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory {
const http = coreStart.http;
-
return ({ getState, dispatch }) => (next) => async (action) => {
next(action);
const state = getState();
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts
index bcdc7ba2089c6..a6e94d3715ca3 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts
@@ -45,6 +45,7 @@ export const initialPolicyDetailsState: () => Immutable = ()
total: 0,
other: 0,
},
+ license: undefined,
});
export const policyDetailsReducer: ImmutableReducer = (
@@ -93,6 +94,13 @@ export const policyDetailsReducer: ImmutableReducer = {
...state,
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts
index 77e975a46d37b..c52bef9a23b25 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts
@@ -6,6 +6,8 @@
import { createSelector } from 'reselect';
import { matchPath } from 'react-router-dom';
+import { ILicense } from '../../../../../../../licensing/common/types';
+import { unsetPolicyFeaturesAboveLicenseLevel } from '../../../../../../common/license/policy_config';
import { PolicyDetailsState } from '../../types';
import {
Immutable,
@@ -20,6 +22,24 @@ import { ManagementRoutePolicyDetailsParams } from '../../../../types';
/** Returns the policy details */
export const policyDetails = (state: Immutable) => state.policyItem;
+/** Returns current active license */
+export const licenseState = (state: Immutable) => state.license;
+
+export const licensedPolicy: (
+ state: Immutable
+) => Immutable | undefined = createSelector(
+ policyDetails,
+ licenseState,
+ (policyData, license) => {
+ if (policyData) {
+ unsetPolicyFeaturesAboveLicenseLevel(
+ policyData?.inputs[0]?.config.policy.value,
+ license as ILicense
+ );
+ }
+ return policyData;
+ }
+);
/**
* Given a Policy Data (package policy) object, return back a new object with only the field
@@ -75,7 +95,7 @@ export const getPolicyDataForUpdate = (
*/
export const policyDetailsForUpdate: (
state: Immutable
-) => Immutable | undefined = createSelector(policyDetails, (policy) => {
+) => Immutable | undefined = createSelector(licensedPolicy, (policy) => {
if (policy) {
return getPolicyDataForUpdate(policy);
}
@@ -111,7 +131,7 @@ const defaultFullPolicy: Immutable = policyConfigFactory();
* Note: this will return a default full policy if the `policyItem` is `undefined`
*/
export const fullPolicy: (s: Immutable) => PolicyConfig = createSelector(
- policyDetails,
+ licensedPolicy,
(policyData) => {
return policyData?.inputs[0]?.config?.policy?.value ?? defaultFullPolicy;
}
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts
index 3926ad2220e35..889bcc15d8df0 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { ILicense } from '../../../../../licensing/common/types';
import {
AppLocation,
Immutable,
@@ -66,6 +67,8 @@ export interface PolicyDetailsState {
success: boolean;
error?: ServerApiError;
};
+ /** current license */
+ license?: ILicense;
}
/**
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx
index f65dbaf1087d8..118ebdf56db90 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx
@@ -8,6 +8,7 @@ import React, { ComponentType, memo } from 'react';
import { CoreStart } from 'kibana/public';
import { combineReducers, createStore, compose, applyMiddleware } from 'redux';
import { Provider as ReduxStoreProvider } from 'react-redux';
+import { CurrentLicense } from '../../../../../common/components/current_license';
import { StartPlugins } from '../../../../../types';
import { managementReducer } from '../../../../store/reducer';
import { managementMiddlewareFactory } from '../../../../store/middleware';
@@ -57,7 +58,9 @@ export const withSecurityContext = ({
return (
-
+
+
+
);
});
diff --git a/x-pack/plugins/security_solution/public/management/routes.tsx b/x-pack/plugins/security_solution/public/management/routes.tsx
index 209d7dd6dbcde..bc24b9ca51980 100644
--- a/x-pack/plugins/security_solution/public/management/routes.tsx
+++ b/x-pack/plugins/security_solution/public/management/routes.tsx
@@ -8,13 +8,16 @@ import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { ManagementContainer } from './pages';
import { NotFoundPage } from '../app/404';
+import { CurrentLicense } from '../common/components/current_license';
/**
* Returns the React Router Routes for the management area
*/
export const ManagementRoutes = () => (
-
-
- } />
-
+
+
+
+ } />
+
+
);
From 3379763965cec86d6a907bd311111939c831fc15 Mon Sep 17 00:00:00 2001
From: Scotty Bollinger
Date: Fri, 18 Dec 2020 12:54:59 -0600
Subject: [PATCH 09/40] [Workplace Search] Refactor AddSource component state
and add tests (#86482)
* Rename variables
This will make reviewing later commits easier
* Use internal routing instead of history.push
Also updates path to Loading
* Move state from component to logic
* Add tests
---
.../components/add_source/add_source.test.tsx | 164 ++++++++++++++++++
.../components/add_source/add_source.tsx | 89 ++++------
.../add_source/add_source_logic.test.ts | 52 ++++++
.../components/add_source/add_source_logic.ts | 67 +++++++
4 files changed, 312 insertions(+), 60 deletions(-)
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx
new file mode 100644
index 0000000000000..a45094ac55ba0
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx
@@ -0,0 +1,164 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import '../../../../../__mocks__/kea.mock';
+import '../../../../../__mocks__/shallow_useeffect.mock';
+
+import { setMockActions, setMockValues } from '../../../../../__mocks__';
+import { sourceConfigData } from '../../../../__mocks__/content_sources.mock';
+
+jest.mock('../../../../../shared/kibana', () => ({
+ KibanaLogic: { values: { navigateToUrl: jest.fn() } },
+}));
+import { KibanaLogic } from '../../../../../shared/kibana';
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { Loading } from '../../../../../shared/loading';
+
+import { AddSource } from './add_source';
+import { AddSourceSteps } from './add_source_logic';
+import { ConfigCompleted } from './config_completed';
+import { ConfigurationIntro } from './configuration_intro';
+import { ConfigureCustom } from './configure_custom';
+import { ConfigureOauth } from './configure_oauth';
+import { ConnectInstance } from './connect_instance';
+import { ReAuthenticate } from './re_authenticate';
+import { SaveConfig } from './save_config';
+import { SaveCustom } from './save_custom';
+
+describe('AddSourceList', () => {
+ const initializeAddSource = jest.fn();
+ const setAddSourceStep = jest.fn();
+ const saveSourceConfig = jest.fn((_, setConfigCompletedStep) => {
+ setConfigCompletedStep();
+ });
+ const createContentSource = jest.fn((_, formSubmitSuccess) => {
+ formSubmitSuccess();
+ });
+ const resetSourcesState = jest.fn();
+
+ const mockValues = {
+ addSourceCurrentStep: AddSourceSteps.ConfigIntroStep,
+ sourceConfigData,
+ dataLoading: false,
+ newCustomSource: {},
+ isOrganization: true,
+ };
+
+ beforeEach(() => {
+ setMockActions({
+ initializeAddSource,
+ setAddSourceStep,
+ saveSourceConfig,
+ createContentSource,
+ resetSourcesState,
+ });
+ setMockValues(mockValues);
+ });
+
+ it('renders default state', () => {
+ const wrapper = shallow( );
+ wrapper.find(ConfigurationIntro).prop('advanceStep')();
+
+ expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep);
+ });
+
+ it('handles loading state', () => {
+ setMockValues({ ...mockValues, dataLoading: true });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(Loading)).toHaveLength(1);
+ });
+
+ it('renders Config Completed step', () => {
+ setMockValues({
+ ...mockValues,
+ addSourceCurrentStep: AddSourceSteps.ConfigCompletedStep,
+ });
+ const wrapper = shallow( );
+ wrapper.find(ConfigCompleted).prop('advanceStep')();
+
+ expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(
+ '/sources/add/confluence_cloud/connect'
+ );
+ expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep);
+ });
+
+ it('renders Save Config step', () => {
+ setMockValues({
+ ...mockValues,
+ addSourceCurrentStep: AddSourceSteps.SaveConfigStep,
+ });
+ const wrapper = shallow( );
+ const saveConfig = wrapper.find(SaveConfig);
+ saveConfig.prop('advanceStep')();
+ saveConfig.prop('goBackStep')!();
+
+ expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ConfigIntroStep);
+ expect(saveSourceConfig).toHaveBeenCalled();
+ });
+
+ it('renders Connect Instance step', () => {
+ setMockValues({
+ ...mockValues,
+ sourceConfigData,
+ addSourceCurrentStep: AddSourceSteps.ConnectInstanceStep,
+ });
+ const wrapper = shallow( );
+ wrapper.find(ConnectInstance).prop('onFormCreated')('foo');
+
+ expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(
+ '/sources/add/confluence_cloud/connect'
+ );
+ });
+
+ it('renders Configure Custom step', () => {
+ setMockValues({
+ ...mockValues,
+ addSourceCurrentStep: AddSourceSteps.ConfigureCustomStep,
+ });
+ const wrapper = shallow( );
+ wrapper.find(ConfigureCustom).prop('advanceStep')();
+
+ expect(createContentSource).toHaveBeenCalled();
+ });
+
+ it('renders Configure Oauth step', () => {
+ setMockValues({
+ ...mockValues,
+ addSourceCurrentStep: AddSourceSteps.ConfigureOauthStep,
+ });
+ const wrapper = shallow( );
+
+ wrapper.find(ConfigureOauth).prop('onFormCreated')('foo');
+
+ expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(
+ '/sources/add/confluence_cloud/connect'
+ );
+ });
+
+ it('renders Save Custom step', () => {
+ setMockValues({
+ ...mockValues,
+ addSourceCurrentStep: AddSourceSteps.SaveCustomStep,
+ });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(SaveCustom)).toHaveLength(1);
+ });
+
+ it('renders ReAuthenticate step', () => {
+ setMockValues({
+ ...mockValues,
+ addSourceCurrentStep: AddSourceSteps.ReAuthenticateStep,
+ });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(ReAuthenticate)).toHaveLength(1);
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx
index eca363da89433..b3f02c831d977 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx
@@ -4,17 +4,16 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useEffect, useState } from 'react';
+import React, { useEffect } from 'react';
-import { History } from 'history';
import { useActions, useValues } from 'kea';
-import { useHistory } from 'react-router-dom';
import { AppLogic } from '../../../../app_logic';
-import { Loading } from '../../../../../../applications/shared/loading';
+import { KibanaLogic } from '../../../../../shared/kibana';
+import { Loading } from '../../../../../shared/loading';
import { CUSTOM_SERVICE_TYPE } from '../../../../constants';
import { staticSourceData } from '../../source_data';
-import { AddSourceLogic } from './add_source_logic';
+import { AddSourceLogic, AddSourceProps, AddSourceSteps } from './add_source_logic';
import { SourceDataItem } from '../../../../types';
import { SOURCE_ADDED_PATH, getSourcesPath } from '../../../../routes';
@@ -28,38 +27,16 @@ import { ReAuthenticate } from './re_authenticate';
import { SaveConfig } from './save_config';
import { SaveCustom } from './save_custom';
-enum Steps {
- ConfigIntroStep = 'Config Intro',
- SaveConfigStep = 'Save Config',
- ConfigCompletedStep = 'Config Completed',
- ConnectInstanceStep = 'Connect Instance',
- ConfigureCustomStep = 'Configure Custom',
- ConfigureOauthStep = 'Configure Oauth',
- SaveCustomStep = 'Save Custom',
- ReAuthenticateStep = 'ReAuthenticate',
-}
-
-interface AddSourceProps {
- sourceIndex: number;
- connect?: boolean;
- configure?: boolean;
- reAuthenticate?: boolean;
-}
-
-export const AddSource: React.FC = ({
- sourceIndex,
- connect,
- configure,
- reAuthenticate,
-}) => {
- const history = useHistory() as History;
+export const AddSource: React.FC = (props) => {
const {
- getSourceConfigData,
+ initializeAddSource,
+ setAddSourceStep,
saveSourceConfig,
createContentSource,
resetSourceState,
} = useActions(AddSourceLogic);
const {
+ addSourceCurrentStep,
sourceConfigData: {
name,
categories,
@@ -79,54 +56,44 @@ export const AddSource: React.FC = ({
sourceDescription,
connectStepDescription,
addPath,
- } = staticSourceData[sourceIndex] as SourceDataItem;
+ } = staticSourceData[props.sourceIndex] as SourceDataItem;
const { isOrganization } = useValues(AppLogic);
useEffect(() => {
- getSourceConfigData(serviceType);
+ initializeAddSource(props);
return resetSourceState;
}, []);
- const isCustom = serviceType === CUSTOM_SERVICE_TYPE;
-
- const getFirstStep = () => {
- if (isCustom) return Steps.ConfigureCustomStep;
- if (connect) return Steps.ConnectInstanceStep;
- if (configure) return Steps.ConfigureOauthStep;
- if (reAuthenticate) return Steps.ReAuthenticateStep;
- return Steps.ConfigIntroStep;
- };
-
- const [currentStep, setStep] = useState(getFirstStep());
-
if (dataLoading) return ;
- const goToConfigurationIntro = () => setStep(Steps.ConfigIntroStep);
- const goToSaveConfig = () => setStep(Steps.SaveConfigStep);
- const setConfigCompletedStep = () => setStep(Steps.ConfigCompletedStep);
+ const goToConfigurationIntro = () => setAddSourceStep(AddSourceSteps.ConfigIntroStep);
+ const goToSaveConfig = () => setAddSourceStep(AddSourceSteps.SaveConfigStep);
+ const setConfigCompletedStep = () => setAddSourceStep(AddSourceSteps.ConfigCompletedStep);
const goToConfigCompleted = () => saveSourceConfig(false, setConfigCompletedStep);
const goToConnectInstance = () => {
- setStep(Steps.ConnectInstanceStep);
- history.push(`${getSourcesPath(addPath, isOrganization)}/connect`);
+ setAddSourceStep(AddSourceSteps.ConnectInstanceStep);
+ KibanaLogic.values.navigateToUrl(`${getSourcesPath(addPath, isOrganization)}/connect`);
};
- const saveCustomSuccess = () => setStep(Steps.SaveCustomStep);
+ const saveCustomSuccess = () => setAddSourceStep(AddSourceSteps.SaveCustomStep);
const goToSaveCustom = () => createContentSource(CUSTOM_SERVICE_TYPE, saveCustomSuccess);
const goToFormSourceCreated = (sourceName: string) => {
- history.push(`${getSourcesPath(SOURCE_ADDED_PATH, isOrganization)}/?name=${sourceName}`);
+ KibanaLogic.values.navigateToUrl(
+ `${getSourcesPath(SOURCE_ADDED_PATH, isOrganization)}/?name=${sourceName}`
+ );
};
const header = ;
return (
<>
- {currentStep === Steps.ConfigIntroStep && (
+ {addSourceCurrentStep === AddSourceSteps.ConfigIntroStep && (
)}
- {currentStep === Steps.SaveConfigStep && (
+ {addSourceCurrentStep === AddSourceSteps.SaveConfigStep && (
= ({
header={header}
/>
)}
- {currentStep === Steps.ConfigCompletedStep && (
+ {addSourceCurrentStep === AddSourceSteps.ConfigCompletedStep && (
= ({
header={header}
/>
)}
- {currentStep === Steps.ConnectInstanceStep && (
+ {addSourceCurrentStep === AddSourceSteps.ConnectInstanceStep && (
= ({
header={header}
/>
)}
- {currentStep === Steps.ConfigureCustomStep && (
+ {addSourceCurrentStep === AddSourceSteps.ConfigureCustomStep && (
)}
- {currentStep === Steps.ConfigureOauthStep && (
+ {addSourceCurrentStep === AddSourceSteps.ConfigureOauthStep && (
)}
- {currentStep === Steps.SaveCustomStep && (
+ {addSourceCurrentStep === AddSourceSteps.SaveCustomStep && (
= ({
header={header}
/>
)}
- {currentStep === Steps.ReAuthenticateStep && }
+ {addSourceCurrentStep === AddSourceSteps.ReAuthenticateStep && (
+
+ )}
>
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
index ba38b46aa0552..084c3d1fb9c4f 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
@@ -32,6 +32,7 @@ import { sourceConfigData } from '../../../../__mocks__/content_sources.mock';
import {
AddSourceLogic,
+ AddSourceSteps,
SourceConfigData,
SourceConnectData,
OrganizationsMap,
@@ -39,6 +40,8 @@ import {
describe('AddSourceLogic', () => {
const defaultValues = {
+ addSourceCurrentStep: AddSourceSteps.ConfigIntroStep,
+ addSourceProps: {},
dataLoading: true,
sectionLoading: true,
buttonLoading: false,
@@ -70,6 +73,8 @@ describe('AddSourceLogic', () => {
githubOrganizations: ['foo', 'bar'],
};
+ const CUSTOM_SERVICE_TYPE_INDEX = 17;
+
const clearFlashMessagesSpy = jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages');
beforeEach(() => {
@@ -224,6 +229,53 @@ describe('AddSourceLogic', () => {
});
describe('listeners', () => {
+ it('initializeAddSource', () => {
+ const addSourceProps = { sourceIndex: 1 };
+ const getSourceConfigDataSpy = jest.spyOn(AddSourceLogic.actions, 'getSourceConfigData');
+ const setAddSourcePropsSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceProps');
+ const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep');
+
+ AddSourceLogic.actions.initializeAddSource(addSourceProps);
+
+ expect(setAddSourcePropsSpy).toHaveBeenCalledWith({ addSourceProps });
+ expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConfigIntroStep);
+ expect(getSourceConfigDataSpy).toHaveBeenCalledWith('confluence_cloud');
+ });
+
+ describe('getFirstStep', () => {
+ it('sets custom as first step', () => {
+ const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep');
+ const addSourceProps = { sourceIndex: CUSTOM_SERVICE_TYPE_INDEX };
+ AddSourceLogic.actions.initializeAddSource(addSourceProps);
+
+ expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConfigureCustomStep);
+ });
+
+ it('sets connect as first step', () => {
+ const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep');
+ const addSourceProps = { sourceIndex: 1, connect: true };
+ AddSourceLogic.actions.initializeAddSource(addSourceProps);
+
+ expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep);
+ });
+
+ it('sets configure as first step', () => {
+ const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep');
+ const addSourceProps = { sourceIndex: 1, configure: true };
+ AddSourceLogic.actions.initializeAddSource(addSourceProps);
+
+ expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConfigureOauthStep);
+ });
+
+ it('sets reAuthenticate as first step', () => {
+ const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep');
+ const addSourceProps = { sourceIndex: 1, reAuthenticate: true };
+ AddSourceLogic.actions.initializeAddSource(addSourceProps);
+
+ expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ReAuthenticateStep);
+ });
+ });
+
describe('organization context', () => {
describe('getSourceConfigData', () => {
it('calls API and sets values', async () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
index c487b584d8ace..ec5cf541c2316 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
@@ -18,10 +18,40 @@ import {
FlashMessagesLogic,
} from '../../../../../shared/flash_messages';
+import { staticSourceData } from '../../source_data';
+import { CUSTOM_SERVICE_TYPE } from '../../../../constants';
+
import { AppLogic } from '../../../../app_logic';
import { CustomSource } from '../../../../types';
+export interface AddSourceProps {
+ sourceIndex: number;
+ connect?: boolean;
+ configure?: boolean;
+ reAuthenticate?: boolean;
+}
+
+export enum AddSourceSteps {
+ ConfigIntroStep = 'Config Intro',
+ SaveConfigStep = 'Save Config',
+ ConfigCompletedStep = 'Config Completed',
+ ConnectInstanceStep = 'Connect Instance',
+ ConfigureCustomStep = 'Configure Custom',
+ ConfigureOauthStep = 'Configure Oauth',
+ SaveCustomStep = 'Save Custom',
+ ReAuthenticateStep = 'ReAuthenticate',
+}
+
export interface AddSourceActions {
+ initializeAddSource: (addSourceProps: AddSourceProps) => { addSourceProps: AddSourceProps };
+ setAddSourceProps: ({
+ addSourceProps,
+ }: {
+ addSourceProps: AddSourceProps;
+ }) => {
+ addSourceProps: AddSourceProps;
+ };
+ setAddSourceStep(addSourceCurrentStep: AddSourceSteps): AddSourceSteps;
setSourceConfigData(sourceConfigData: SourceConfigData): SourceConfigData;
setSourceConnectData(sourceConnectData: SourceConnectData): SourceConnectData;
setClientIdValue(clientIdValue: string): string;
@@ -83,6 +113,8 @@ export interface OrganizationsMap {
}
interface AddSourceValues {
+ addSourceProps: AddSourceProps;
+ addSourceCurrentStep: AddSourceSteps;
dataLoading: boolean;
sectionLoading: boolean;
buttonLoading: boolean;
@@ -112,6 +144,11 @@ interface PreContentSourceResponse {
export const AddSourceLogic = kea>({
path: ['enterprise_search', 'workplace_search', 'add_source_logic'],
actions: {
+ initializeAddSource: (addSourceProps: AddSourceProps) => ({ addSourceProps }),
+ setAddSourceProps: ({ addSourceProps }: { addSourceProps: AddSourceProps }) => ({
+ addSourceProps,
+ }),
+ setAddSourceStep: (addSourceCurrentStep: AddSourceSteps) => addSourceCurrentStep,
setSourceConfigData: (sourceConfigData: SourceConfigData) => sourceConfigData,
setSourceConnectData: (sourceConnectData: SourceConnectData) => sourceConnectData,
setClientIdValue: (clientIdValue: string) => clientIdValue,
@@ -145,6 +182,18 @@ export const AddSourceLogic = kea false,
},
reducers: {
+ addSourceProps: [
+ {} as AddSourceProps,
+ {
+ setAddSourceProps: (_, { addSourceProps }) => addSourceProps,
+ },
+ ],
+ addSourceCurrentStep: [
+ AddSourceSteps.ConfigIntroStep,
+ {
+ setAddSourceStep: (_, addSourceCurrentStep) => addSourceCurrentStep,
+ },
+ ],
sourceConfigData: [
{} as SourceConfigData,
{
@@ -282,6 +331,12 @@ export const AddSourceLogic = kea ({
+ initializeAddSource: ({ addSourceProps }) => {
+ const { serviceType } = staticSourceData[addSourceProps.sourceIndex];
+ actions.setAddSourceProps({ addSourceProps });
+ actions.setAddSourceStep(getFirstStep(addSourceProps));
+ actions.getSourceConfigData(serviceType);
+ },
getSourceConfigData: async ({ serviceType }) => {
const route = `/api/workplace_search/org/settings/connectors/${serviceType}`;
@@ -435,3 +490,15 @@ export const AddSourceLogic = kea {
+ const { sourceIndex, connect, configure, reAuthenticate } = props;
+ const { serviceType } = staticSourceData[sourceIndex];
+ const isCustom = serviceType === CUSTOM_SERVICE_TYPE;
+
+ if (isCustom) return AddSourceSteps.ConfigureCustomStep;
+ if (connect) return AddSourceSteps.ConnectInstanceStep;
+ if (configure) return AddSourceSteps.ConfigureOauthStep;
+ if (reAuthenticate) return AddSourceSteps.ReAuthenticateStep;
+ return AddSourceSteps.ConfigIntroStep;
+};
From b484071096f6e45de3efbbf1b31738a3182091a9 Mon Sep 17 00:00:00 2001
From: Candace Park <56409205+parkiino@users.noreply.github.com>
Date: Fri, 18 Dec 2020 11:07:26 -0800
Subject: [PATCH 10/40] [Security Solution][Endpoint][Admin] Disables malware
checkbox when switch is off and can now save in detect mode (#86402)
---
.../view/policy_forms/protections/malware.tsx | 25 +++++++++++++------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx
index d611c4102e8f8..8e631e497e57b 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx
@@ -55,16 +55,19 @@ const ProtectionRadio = React.memo(({ id, label }: { id: ProtectionModes; label:
const radioButtonId = useMemo(() => htmlIdGenerator()(), []);
// currently just taking windows.malware, but both windows.malware and mac.malware should be the same value
const selected = policyDetailsConfig && policyDetailsConfig.windows.malware.mode;
+ const isPlatinumPlus = useLicense().isPlatinumPlus();
const handleRadioChange = useCallback(() => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of OSes) {
newPayload[os][protection].mode = id;
- if (id === ProtectionModes.prevent) {
- newPayload[os].popup[protection].enabled = true;
- } else {
- newPayload[os].popup[protection].enabled = false;
+ if (isPlatinumPlus) {
+ if (id === ProtectionModes.prevent) {
+ newPayload[os].popup[protection].enabled = true;
+ } else {
+ newPayload[os].popup[protection].enabled = false;
+ }
}
}
dispatch({
@@ -72,7 +75,7 @@ const ProtectionRadio = React.memo(({ id, label }: { id: ProtectionModes; label:
payload: { policyConfig: newPayload },
});
}
- }, [dispatch, id, policyDetailsConfig]);
+ }, [dispatch, id, policyDetailsConfig, isPlatinumPlus]);
/**
* Passing an arbitrary id because EuiRadio
@@ -158,12 +161,16 @@ export const MalwareProtections = React.memo(() => {
if (event.target.checked === false) {
for (const os of OSes) {
newPayload[os][protection].mode = ProtectionModes.off;
- newPayload[os].popup[protection].enabled = event.target.checked;
+ if (isPlatinumPlus) {
+ newPayload[os].popup[protection].enabled = event.target.checked;
+ }
}
} else {
for (const os of OSes) {
newPayload[os][protection].mode = ProtectionModes.prevent;
- newPayload[os].popup[protection].enabled = event.target.checked;
+ if (isPlatinumPlus) {
+ newPayload[os].popup[protection].enabled = event.target.checked;
+ }
}
}
dispatch({
@@ -172,7 +179,7 @@ export const MalwareProtections = React.memo(() => {
});
}
},
- [dispatch, policyDetailsConfig]
+ [dispatch, policyDetailsConfig, isPlatinumPlus]
);
const handleUserNotificationCheckbox = useCallback(
@@ -243,6 +250,7 @@ export const MalwareProtections = React.memo(() => {
id="xpack.securitySolution.endpoint.policyDetail.malware.userNotification"
onChange={handleUserNotificationCheckbox}
checked={userNotificationSelected}
+ disabled={selected === ProtectionModes.off}
label={i18n.translate(
'xpack.securitySolution.endpoint.policyDetail.malware.notifyUser',
{
@@ -305,6 +313,7 @@ export const MalwareProtections = React.memo(() => {
);
}, [
radios,
+ selected,
isPlatinumPlus,
handleUserNotificationCheckbox,
userNotificationSelected,
From 73068e755c115f7a14101ad2d2b54ad98d072a69 Mon Sep 17 00:00:00 2001
From: Candace Park <56409205+parkiino@users.noreply.github.com>
Date: Fri, 18 Dec 2020 11:45:29 -0800
Subject: [PATCH 11/40] [Security Solution][Endpoint][Admin] Remove spaces in
custom malware message brackets (#86393)
---
.../common/endpoint/models/policy_config.ts | 2 +-
.../apps/endpoint/policy_details.ts | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts
index 22037c021701f..14941b019421b 100644
--- a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts
@@ -72,4 +72,4 @@ export const factory = (): PolicyConfig => {
/**
* Reflects what string the Endpoint will use when message field is default/empty
*/
-export const DefaultMalwareMessage = 'Elastic Security { action } { filename }';
+export const DefaultMalwareMessage = 'Elastic Security {action} {filename}';
diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts
index 1a5c99294c281..e344d4c3c27e4 100644
--- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts
+++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts
@@ -222,7 +222,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
popup: {
malware: {
enabled: true,
- message: 'Elastic Security { action } { filename }',
+ message: 'Elastic Security {action} {filename}',
},
},
},
@@ -241,7 +241,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
popup: {
malware: {
enabled: true,
- message: 'Elastic Security { action } { filename }',
+ message: 'Elastic Security {action} {filename}',
},
},
antivirus_registration: {
@@ -366,7 +366,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
popup: {
malware: {
enabled: true,
- message: 'Elastic Security { action } { filename }',
+ message: 'Elastic Security {action} {filename}',
},
},
},
@@ -385,7 +385,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
popup: {
malware: {
enabled: true,
- message: 'Elastic Security { action } { filename }',
+ message: 'Elastic Security {action} {filename}',
},
},
antivirus_registration: {
@@ -503,7 +503,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
popup: {
malware: {
enabled: true,
- message: 'Elastic Security { action } { filename }',
+ message: 'Elastic Security {action} {filename}',
},
},
},
@@ -522,7 +522,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
popup: {
malware: {
enabled: true,
- message: 'Elastic Security { action } { filename }',
+ message: 'Elastic Security {action} {filename}',
},
},
antivirus_registration: {
From c33835e87cab3f1ad9f66a32520b9080541334ca Mon Sep 17 00:00:00 2001
From: Jonathan Budzenski
Date: Fri, 18 Dec 2020 14:02:21 -0600
Subject: [PATCH 12/40] [docs] Add kibana-encryption-keys (#84577)
Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../security/encryption-keys/index.asciidoc | 44 +++++++++++++++++++
docs/user/security/index.asciidoc | 1 +
2 files changed, 45 insertions(+)
create mode 100644 docs/user/security/encryption-keys/index.asciidoc
diff --git a/docs/user/security/encryption-keys/index.asciidoc b/docs/user/security/encryption-keys/index.asciidoc
new file mode 100644
index 0000000000000..58c0c0bb775ca
--- /dev/null
+++ b/docs/user/security/encryption-keys/index.asciidoc
@@ -0,0 +1,44 @@
+[[kibana-encryption-keys]]
+=== Set up encryptions keys to protect sensitive information
+
+The `kibana-encryption-keys` command helps you set up encryption keys that {kib} uses
+to protect sensitive information.
+
+[discrete]
+=== Synopsis
+
+[source,shell]
+--------------------------------------------------
+bin/kibana-encryption-keys generate
+[-i, --interactive] [-q, --quiet]
+[-f, --force] [-h, --help]
+--------------------------------------------------
+
+[discrete]
+=== Description
+
+{kib} uses encryption keys in several areas, ranging from encrypting data
+in {kib} associated indices to storing session information. By defining these
+encryption keys in your configuration, you'll ensure consistent operations
+across restarts.
+
+[discrete]
+[[encryption-key-parameters]]
+=== Parameters
+
+`generate`:: Randomly generates passwords to the console.
+
+`-i, --interactive`:: Prompts you for which encryption keys to set and optionally
+where to save a sample configuration file.
+
+`-q, --quiet`:: Outputs the encryption keys without helper information.
+
+`-f, --force`:: Shows help information.
+
+[discrete]
+=== Examples
+
+[source,shell]
+--------------------------------------------------
+bin/kibana-encryption-keys generate
+--------------------------------------------------
diff --git a/docs/user/security/index.asciidoc b/docs/user/security/index.asciidoc
index f84e9de87c734..6a5c4a83aa3ad 100644
--- a/docs/user/security/index.asciidoc
+++ b/docs/user/security/index.asciidoc
@@ -45,5 +45,6 @@ cause Kibana's authorization to behave unexpectedly.
include::authorization/index.asciidoc[]
include::authorization/kibana-privileges.asciidoc[]
include::api-keys/index.asciidoc[]
+include::encryption-keys/index.asciidoc[]
include::role-mappings/index.asciidoc[]
include::rbac_tutorial.asciidoc[]
From 8ce9b474d671dab83591254f973e3cb506aca511 Mon Sep 17 00:00:00 2001
From: Devon Thomson
Date: Fri, 18 Dec 2020 15:07:36 -0500
Subject: [PATCH 13/40] [Time to Visualize] Fix Dashboard OnAppLeave (#86193)
Added isTransferInProgress to embeddable_state_transfer for apps to determine whether or not to show onAppLeave confirm
---
...c.embeddablestatetransfer._constructor_.md | 3 +-
...dablestatetransfer.istransferinprogress.md | 11 +++++++
...beddable-public.embeddablestatetransfer.md | 3 +-
.../public/application/dashboard_app.tsx | 17 +++++++---
.../embeddable_state_transfer.test.ts | 31 +++++++++++++++++--
.../embeddable_state_transfer.ts | 8 +++++
src/plugins/embeddable/public/plugin.tsx | 8 ++++-
src/plugins/embeddable/public/public.api.md | 4 ++-
test/common/services/security/test_user.ts | 6 ++--
.../dashboard/create_and_add_embeddables.ts | 2 +-
.../apps/dashboard/dashboard_time_picker.ts | 6 ++++
.../apps/dashboard/panel_context_menu.ts | 2 +-
.../apps/dashboard/panel_replacing.ts | 2 ++
test/functional/page_objects/header_page.ts | 17 ++++++++--
.../services/dashboard/visualizations.ts | 3 +-
15 files changed, 102 insertions(+), 21 deletions(-)
create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.istransferinprogress.md
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md
index 276499b435e1f..77e9c2d00b2dd 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md
@@ -9,7 +9,7 @@ Constructs a new instance of the `EmbeddableStateTransfer` class
Signature:
```typescript
-constructor(navigateToApp: ApplicationStart['navigateToApp'], appList?: ReadonlyMap | undefined, customStorage?: Storage);
+constructor(navigateToApp: ApplicationStart['navigateToApp'], currentAppId$: ApplicationStart['currentAppId$'], appList?: ReadonlyMap | undefined, customStorage?: Storage);
```
## Parameters
@@ -17,6 +17,7 @@ constructor(navigateToApp: ApplicationStart['navigateToApp'], appList?: Readonly
| Parameter | Type | Description |
| --- | --- | --- |
| navigateToApp | ApplicationStart['navigateToApp']
| |
+| currentAppId$ | ApplicationStart['currentAppId$']
| |
| appList | ReadonlyMap<string, PublicAppInfo> | undefined
| |
| customStorage | Storage
| |
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.istransferinprogress.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.istransferinprogress.md
new file mode 100644
index 0000000000000..f00d015f316d2
--- /dev/null
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.istransferinprogress.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [isTransferInProgress](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.istransferinprogress.md)
+
+## EmbeddableStateTransfer.isTransferInProgress property
+
+Signature:
+
+```typescript
+isTransferInProgress: boolean;
+```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md
index 3676b744b8cc9..76b6708b93bd1 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md
@@ -16,13 +16,14 @@ export declare class EmbeddableStateTransfer
| Constructor | Modifiers | Description |
| --- | --- | --- |
-| [(constructor)(navigateToApp, appList, customStorage)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md) | | Constructs a new instance of the EmbeddableStateTransfer
class |
+| [(constructor)(navigateToApp, currentAppId$, appList, customStorage)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md) | | Constructs a new instance of the EmbeddableStateTransfer
class |
## Properties
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [getAppNameFromId](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getappnamefromid.md) | | (appId: string) => string | undefined
| Fetches an internationalized app title when given an appId. |
+| [isTransferInProgress](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.istransferinprogress.md) | | boolean
| |
## Methods
diff --git a/src/plugins/dashboard/public/application/dashboard_app.tsx b/src/plugins/dashboard/public/application/dashboard_app.tsx
index 8eff48251b371..845d64c16500d 100644
--- a/src/plugins/dashboard/public/application/dashboard_app.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_app.tsx
@@ -45,6 +45,7 @@ import { removeQueryParam } from '../services/kibana_utils';
import { IndexPattern } from '../services/data';
import { EmbeddableRenderer } from '../services/embeddable';
import { DashboardContainerInput } from '.';
+import { leaveConfirmStrings } from '../dashboard_strings';
export interface DashboardAppProps {
history: History;
@@ -64,8 +65,9 @@ export function DashboardApp({
core,
onAppLeave,
uiSettings,
- indexPatterns: indexPatternService,
+ embeddable,
dashboardCapabilities,
+ indexPatterns: indexPatternService,
} = useKibana().services;
const [lastReloadTime, setLastReloadTime] = useState(0);
@@ -196,9 +198,14 @@ export function DashboardApp({
return;
}
onAppLeave((actions) => {
- if (dashboardStateManager?.getIsDirty()) {
- // TODO: Finish App leave handler with overrides when redirecting to an editor.
- // return actions.confirm(leaveConfirmStrings.leaveSubtitle, leaveConfirmStrings.leaveTitle);
+ if (
+ dashboardStateManager?.getIsDirty() &&
+ !embeddable.getStateTransfer().isTransferInProgress
+ ) {
+ return actions.confirm(
+ leaveConfirmStrings.getLeaveSubtitle(),
+ leaveConfirmStrings.getLeaveTitle()
+ );
}
return actions.default();
});
@@ -206,7 +213,7 @@ export function DashboardApp({
// reset on app leave handler so leaving from the listing page doesn't trigger a confirmation
onAppLeave((actions) => actions.default());
};
- }, [dashboardStateManager, dashboardContainer, onAppLeave]);
+ }, [dashboardStateManager, dashboardContainer, onAppLeave, embeddable]);
// Refresh the dashboard container when lastReloadTime changes
useEffect(() => {
diff --git a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts
index cbaeddf472d52..be034d125dcee 100644
--- a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts
+++ b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts
@@ -23,6 +23,7 @@ import { EmbeddableStateTransfer } from '.';
import { ApplicationStart, PublicAppInfo } from '../../../../../core/public';
import { EMBEDDABLE_EDITOR_STATE_KEY, EMBEDDABLE_PACKAGE_STATE_KEY } from './types';
import { EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY } from './embeddable_state_transfer';
+import { Subject } from 'rxjs';
const createStorage = (): Storage => {
const createMockStore = () => {
@@ -46,16 +47,24 @@ const createStorage = (): Storage => {
describe('embeddable state transfer', () => {
let application: jest.Mocked;
let stateTransfer: EmbeddableStateTransfer;
+ let currentAppId$: Subject;
let store: Storage;
const destinationApp = 'superUltraVisualize';
const originatingApp = 'superUltraTestDashboard';
beforeEach(() => {
+ currentAppId$ = new Subject();
+ currentAppId$.next(originatingApp);
const core = coreMock.createStart();
application = core.application;
store = createStorage();
- stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, undefined, store);
+ stateTransfer = new EmbeddableStateTransfer(
+ application.navigateToApp,
+ currentAppId$,
+ undefined,
+ store
+ );
});
it('cannot fetch app name when given no app list', async () => {
@@ -67,7 +76,7 @@ describe('embeddable state transfer', () => {
['testId', { title: 'State Transfer Test App Hello' } as PublicAppInfo],
['testId2', { title: 'State Transfer Test App Goodbye' } as PublicAppInfo],
]);
- stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, appsList);
+ stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, currentAppId$, appsList);
expect(stateTransfer.getAppNameFromId('kibanana')).toBeUndefined();
});
@@ -76,7 +85,7 @@ describe('embeddable state transfer', () => {
['testId', { title: 'State Transfer Test App Hello' } as PublicAppInfo],
['testId2', { title: 'State Transfer Test App Goodbye' } as PublicAppInfo],
]);
- stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, appsList);
+ stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, currentAppId$, appsList);
expect(stateTransfer.getAppNameFromId('testId')).toBe('State Transfer Test App Hello');
expect(stateTransfer.getAppNameFromId('testId2')).toBe('State Transfer Test App Goodbye');
});
@@ -107,6 +116,13 @@ describe('embeddable state transfer', () => {
});
});
+ it('sets isTransferInProgress to true when sending an outgoing editor state', async () => {
+ await stateTransfer.navigateToEditor(destinationApp, { state: { originatingApp } });
+ expect(stateTransfer.isTransferInProgress).toEqual(true);
+ currentAppId$.next(destinationApp);
+ expect(stateTransfer.isTransferInProgress).toEqual(false);
+ });
+
it('can send an outgoing embeddable package state', async () => {
await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, {
state: { type: 'coolestType', input: { savedObjectId: '150' } },
@@ -135,6 +151,15 @@ describe('embeddable state transfer', () => {
});
});
+ it('sets isTransferInProgress to true when sending an outgoing embeddable package state', async () => {
+ await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, {
+ state: { type: 'coolestType', input: { savedObjectId: '150' } },
+ });
+ expect(stateTransfer.isTransferInProgress).toEqual(true);
+ currentAppId$.next(destinationApp);
+ expect(stateTransfer.isTransferInProgress).toEqual(false);
+ });
+
it('can fetch an incoming editor state', async () => {
store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, {
[EMBEDDABLE_EDITOR_STATE_KEY]: { originatingApp: 'superUltraTestDashboard' },
diff --git a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts
index 0b34bea810520..92900059668db 100644
--- a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts
+++ b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts
@@ -38,14 +38,20 @@ export const EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY = 'EMBEDDABLE_STATE_TRANSFER'
* @public
*/
export class EmbeddableStateTransfer {
+ public isTransferInProgress: boolean;
private storage: Storage;
constructor(
private navigateToApp: ApplicationStart['navigateToApp'],
+ currentAppId$: ApplicationStart['currentAppId$'],
private appList?: ReadonlyMap | undefined,
customStorage?: Storage
) {
this.storage = customStorage ? customStorage : new Storage(sessionStorage);
+ this.isTransferInProgress = false;
+ currentAppId$.subscribe(() => {
+ this.isTransferInProgress = false;
+ });
}
/**
@@ -105,6 +111,7 @@ export class EmbeddableStateTransfer {
state: EmbeddableEditorState;
}
): Promise {
+ this.isTransferInProgress = true;
await this.navigateToWithState(appId, EMBEDDABLE_EDITOR_STATE_KEY, {
...options,
appendToExistingState: true,
@@ -119,6 +126,7 @@ export class EmbeddableStateTransfer {
appId: string,
options?: { path?: string; state: EmbeddablePackageState }
): Promise {
+ this.isTransferInProgress = true;
await this.navigateToWithState(appId, EMBEDDABLE_PACKAGE_STATE_KEY, {
...options,
appendToExistingState: true,
diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx
index 5118a1a8818c0..6f43d87bdcd53 100644
--- a/src/plugins/embeddable/public/plugin.tsx
+++ b/src/plugins/embeddable/public/plugin.tsx
@@ -161,6 +161,7 @@ export class EmbeddablePublicPlugin implements Plugin
storage
- ? new EmbeddableStateTransfer(core.application.navigateToApp, this.appList, storage)
+ ? new EmbeddableStateTransfer(
+ core.application.navigateToApp,
+ core.application.currentAppId$,
+ this.appList,
+ storage
+ )
: this.stateTransferService,
EmbeddablePanel: getEmbeddablePanelHoc(),
telemetry: getTelemetryFunction(commonContract),
diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md
index 03818fccda0bc..7563b66e58ae9 100644
--- a/src/plugins/embeddable/public/public.api.md
+++ b/src/plugins/embeddable/public/public.api.md
@@ -628,12 +628,14 @@ export interface EmbeddableStartDependencies {
export class EmbeddableStateTransfer {
// Warning: (ae-forgotten-export) The symbol "ApplicationStart" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "PublicAppInfo" needs to be exported by the entry point index.d.ts
- constructor(navigateToApp: ApplicationStart['navigateToApp'], appList?: ReadonlyMap | undefined, customStorage?: Storage);
+ constructor(navigateToApp: ApplicationStart['navigateToApp'], currentAppId$: ApplicationStart['currentAppId$'], appList?: ReadonlyMap | undefined, customStorage?: Storage);
// (undocumented)
clearEditorState(): void;
getAppNameFromId: (appId: string) => string | undefined;
getIncomingEditorState(removeAfterFetch?: boolean): EmbeddableEditorState | undefined;
getIncomingEmbeddablePackage(removeAfterFetch?: boolean): EmbeddablePackageState | undefined;
+ // (undocumented)
+ isTransferInProgress: boolean;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ApplicationStart"
navigateToEditor(appId: string, options?: {
path?: string;
diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts
index 7183943591c88..25a36fed9c9c5 100644
--- a/test/common/services/security/test_user.ts
+++ b/test/common/services/security/test_user.ts
@@ -87,11 +87,11 @@ export async function createTestUserService(
});
if (browser && testSubjects && shouldRefreshBrowser) {
- // accept alert if it pops up
- const alert = await browser.getAlert();
- await alert?.accept();
if (await testSubjects.exists('kibanaChrome', { allowHidden: true })) {
await browser.refresh();
+ // accept alert if it pops up
+ const alert = await browser.getAlert();
+ await alert?.accept();
await testSubjects.find('kibanaChrome', config.get('timeouts.find') * 10);
}
}
diff --git a/test/functional/apps/dashboard/create_and_add_embeddables.ts b/test/functional/apps/dashboard/create_and_add_embeddables.ts
index d8b8a6f91fe31..605ea26b76c6f 100644
--- a/test/functional/apps/dashboard/create_and_add_embeddables.ts
+++ b/test/functional/apps/dashboard/create_and_add_embeddables.ts
@@ -81,7 +81,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('saves the saved visualization url to the app link', async () => {
- await PageObjects.header.clickVisualize();
+ await PageObjects.header.clickVisualize(true);
const currentUrl = await browser.getCurrentUrl();
expect(currentUrl).to.contain(VisualizeConstants.EDIT_PATH);
});
diff --git a/test/functional/apps/dashboard/dashboard_time_picker.ts b/test/functional/apps/dashboard/dashboard_time_picker.ts
index 274a4355e26e2..c759edd638260 100644
--- a/test/functional/apps/dashboard/dashboard_time_picker.ts
+++ b/test/functional/apps/dashboard/dashboard_time_picker.ts
@@ -40,6 +40,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
after(async () => {
await kibanaServer.uiSettings.replace({});
await browser.refresh();
+ const alert = await browser.getAlert();
+ await alert?.accept();
});
it('Visualization updated when time picker changes', async () => {
@@ -88,6 +90,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
`)`;
log.debug('go to url' + `${kibanaBaseUrl}#${urlQuery}`);
await browser.get(`${kibanaBaseUrl}#${urlQuery}`, true);
+ const alert = await browser.getAlert();
+ await alert?.accept();
await PageObjects.header.waitUntilLoadingHasFinished();
const time = await PageObjects.timePicker.getTimeConfig();
const refresh = await PageObjects.timePicker.getRefreshConfig();
@@ -99,6 +103,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('Timepicker respects dateFormat from UI settings', async () => {
await kibanaServer.uiSettings.replace({ dateFormat: 'YYYY-MM-DD HH:mm:ss.SSS' });
await browser.refresh();
+ const alert = await browser.getAlert();
+ await alert?.accept();
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]);
diff --git a/test/functional/apps/dashboard/panel_context_menu.ts b/test/functional/apps/dashboard/panel_context_menu.ts
index 0b9e873f46151..bd6756835af31 100644
--- a/test/functional/apps/dashboard/panel_context_menu.ts
+++ b/test/functional/apps/dashboard/panel_context_menu.ts
@@ -110,7 +110,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const searchName = 'my search';
before(async () => {
- await PageObjects.header.clickDiscover();
+ await PageObjects.header.clickDiscover(true);
await PageObjects.discover.clickNewSearchButton();
await dashboardVisualizations.createSavedSearch({ name: searchName, fields: ['bytes'] });
await PageObjects.header.waitUntilLoadingHasFinished();
diff --git a/test/functional/apps/dashboard/panel_replacing.ts b/test/functional/apps/dashboard/panel_replacing.ts
index 6bf3dbbe47b1d..c9ecf42dbc8e8 100644
--- a/test/functional/apps/dashboard/panel_replacing.ts
+++ b/test/functional/apps/dashboard/panel_replacing.ts
@@ -70,6 +70,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('replaced panel persisted correctly when dashboard is hard refreshed', async () => {
const currentUrl = await browser.getCurrentUrl();
await browser.get(currentUrl, true);
+ const alert = await browser.getAlert();
+ await alert?.accept();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
const panelTitles = await PageObjects.dashboard.getPanelTitles();
diff --git a/test/functional/page_objects/header_page.ts b/test/functional/page_objects/header_page.ts
index d69a01ab6bb26..5a892bb4f6ca3 100644
--- a/test/functional/page_objects/header_page.ts
+++ b/test/functional/page_objects/header_page.ts
@@ -31,14 +31,16 @@ export function HeaderPageProvider({ getService, getPageObjects }: FtrProviderCo
const defaultFindTimeout = config.get('timeouts.find');
class HeaderPage {
- public async clickDiscover() {
+ public async clickDiscover(ignoreAppLeaveWarning = false) {
await appsMenu.clickLink('Discover', { category: 'kibana' });
+ await this.onAppLeaveWarning(ignoreAppLeaveWarning);
await PageObjects.common.waitForTopNavToBeVisible();
await this.awaitGlobalLoadingIndicatorHidden();
}
- public async clickVisualize() {
+ public async clickVisualize(ignoreAppLeaveWarning = false) {
await appsMenu.clickLink('Visualize', { category: 'kibana' });
+ await this.onAppLeaveWarning(ignoreAppLeaveWarning);
await this.awaitGlobalLoadingIndicatorHidden();
await retry.waitFor('first breadcrumb to be "Visualize"', async () => {
const firstBreadcrumb = await globalNav.getFirstBreadcrumb();
@@ -95,6 +97,17 @@ export function HeaderPageProvider({ getService, getPageObjects }: FtrProviderCo
log.debug('awaitKibanaChrome');
await testSubjects.find('kibanaChrome', defaultFindTimeout * 10);
}
+
+ public async onAppLeaveWarning(ignoreWarning = false) {
+ await retry.try(async () => {
+ const warning = await testSubjects.exists('confirmModalTitleText');
+ if (warning) {
+ await testSubjects.click(
+ ignoreWarning ? 'confirmModalConfirmButton' : 'confirmModalCancelButton'
+ );
+ }
+ });
+ }
}
return new HeaderPage();
diff --git a/test/functional/services/dashboard/visualizations.ts b/test/functional/services/dashboard/visualizations.ts
index b35ef1e8f2f9a..22e1769145f88 100644
--- a/test/functional/services/dashboard/visualizations.ts
+++ b/test/functional/services/dashboard/visualizations.ts
@@ -58,8 +58,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
fields?: string[];
}) {
log.debug(`createSavedSearch(${name})`);
- await PageObjects.header.clickDiscover();
-
+ await PageObjects.header.clickDiscover(true);
await PageObjects.timePicker.setHistoricalDataRange();
if (query) {
From b5197a36d47007d78f5bdb223580d341236cc4d4 Mon Sep 17 00:00:00 2001
From: ymao1
Date: Fri, 18 Dec 2020 15:26:24 -0500
Subject: [PATCH 14/40] [Alerting UI] Centered loading spinners (#86186)
* Creating shared loading spinner component
* Using section loading component where it makes sense
* Fixing tests
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../components/center_justified_spinner.tsx | 22 ++++++++++++++
.../application/components/health_check.tsx | 13 ++++++--
.../lib/suspended_component_with_props.tsx | 12 ++------
.../action_connector_form.tsx | 15 +++++-----
.../action_type_form.tsx | 11 +------
.../action_type_menu.tsx | 21 ++++++++++---
.../components/actions_connectors_list.tsx | 10 ++-----
.../components/alert_details_route.test.tsx | 4 +--
.../components/alert_details_route.tsx | 11 ++-----
.../components/alert_instances_route.test.tsx | 4 +--
.../components/alert_instances_route.tsx | 11 ++-----
.../sections/alert_form/alert_form.tsx | 30 +++++++++++--------
.../alerts_list/components/alerts_list.tsx | 8 ++---
13 files changed, 90 insertions(+), 82 deletions(-)
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/center_justified_spinner.tsx
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/center_justified_spinner.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/center_justified_spinner.tsx
new file mode 100644
index 0000000000000..2dec2dcb001d7
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/center_justified_spinner.tsx
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
+import { EuiLoadingSpinnerSize } from '@elastic/eui/src/components/loading/loading_spinner';
+
+interface Props {
+ size?: EuiLoadingSpinnerSize;
+}
+
+export const CenterJustifiedSpinner: React.FunctionComponent = ({ size }) => (
+
+
+
+
+
+);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx
index 0f20ade8187fd..66f7c1d36dfb2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx
@@ -9,7 +9,7 @@ import { Option, none, some, fold } from 'fp-ts/lib/Option';
import { pipe } from 'fp-ts/lib/pipeable';
import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiLink, EuiLoadingSpinner } from '@elastic/eui';
+import { EuiLink, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiEmptyPrompt, EuiCode } from '@elastic/eui';
@@ -19,6 +19,7 @@ import { health } from '../lib/alert_api';
import './health_check.scss';
import { useHealthContext } from '../context/health_context';
import { useKibana } from '../../common/lib/kibana';
+import { CenterJustifiedSpinner } from './center_justified_spinner';
interface Props {
inFlyout?: boolean;
@@ -47,7 +48,15 @@ export const HealthCheck: React.FunctionComponent = ({
return pipe(
alertingHealth,
fold(
- () => (waitForCheck ? : {children} ),
+ () =>
+ waitForCheck ? (
+
+
+
+
+ ) : (
+ {children}
+ ),
(healthCheck) => {
return healthCheck?.isSufficientlySecure && healthCheck?.hasPermanentEncryptionKey ? (
{children}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx
index 563353793f991..98c20c5abcc2d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx
@@ -4,23 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Suspense } from 'react';
-import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiLoadingSpinnerSize } from '@elastic/eui/src/components/loading/loading_spinner';
+import { CenterJustifiedSpinner } from '../components/center_justified_spinner';
export function suspendedComponentWithProps(
ComponentToSuspend: React.ComponentType,
size?: EuiLoadingSpinnerSize
) {
return (props: T) => (
-
-
-
-
-
- }
- >
+ }>
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx
index 7d8949421126c..a83194d67a759 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx
@@ -12,9 +12,6 @@ import {
EuiSpacer,
EuiFieldText,
EuiFormRow,
- EuiLoadingSpinner,
- EuiFlexGroup,
- EuiFlexItem,
EuiErrorBoundary,
EuiTitle,
} from '@elastic/eui';
@@ -29,6 +26,7 @@ import {
} from '../../../types';
import { hasSaveActionsCapability } from '../../lib/capabilities';
import { useKibana } from '../../../common/lib/kibana';
+import { SectionLoading } from '../../components/section_loading';
export function validateBaseProperties(actionObject: ActionConnector) {
const validationResult = { errors: {} };
@@ -181,11 +179,12 @@ export const ActionConnectorForm = ({
-
-
-
-
+
+
+
}
>
{ParamsFieldsComponent ? (
-
-
-
-
-
- }
- >
+
void;
@@ -31,6 +33,7 @@ export const ActionTypeMenu = ({
http,
notifications: { toasts },
} = useKibana().services;
+ const [loadingActionTypes, setLoadingActionTypes] = useState(false);
const [actionTypesIndex, setActionTypesIndex] = useState(undefined);
useEffect(() => {
@@ -43,11 +46,14 @@ export const ActionTypeMenu = ({
*
* TODO: Remove when cases connector is available across Kibana. Issue: https://github.com/elastic/kibana/issues/82502.
* */
- const availableActionTypes =
- actionTypes ??
- (await loadActionTypes({ http })).filter(
+ let availableActionTypes = actionTypes;
+ if (!availableActionTypes) {
+ setLoadingActionTypes(true);
+ availableActionTypes = (await loadActionTypes({ http })).filter(
(actionType) => !DEFAULT_HIDDEN_ACTION_TYPES.includes(actionType.id)
);
+ setLoadingActionTypes(false);
+ }
const index: ActionTypeIndex = {};
for (const actionTypeItem of availableActionTypes) {
index[actionTypeItem.id] = actionTypeItem;
@@ -117,7 +123,14 @@ export const ActionTypeMenu = ({
);
});
- return (
+ return loadingActionTypes ? (
+
+
+
+ ) : (
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
index 2df75436f5f96..bf6786d0d4e4c 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
@@ -10,7 +10,6 @@ import {
EuiSpacer,
EuiButton,
EuiLink,
- EuiLoadingSpinner,
EuiIconTip,
EuiFlexGroup,
EuiFlexItem,
@@ -40,6 +39,7 @@ import { ActionConnector, ActionConnectorTableItem, ActionTypeIndex } from '../.
import { EmptyConnectorsPrompt } from '../../../components/prompts/empty_connectors_prompt';
import { useKibana } from '../../../../common/lib/kibana';
import { DEFAULT_HIDDEN_ACTION_TYPES } from '../../../../';
+import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
export const ActionsConnectorsList: React.FunctionComponent = () => {
const {
@@ -355,13 +355,7 @@ export const ActionsConnectorsList: React.FunctionComponent = () => {
/>
{/* Render the view based on if there's data or if they can save */}
- {(isLoadingActions || isLoadingActionTypes) && (
-
-
-
-
-
- )}
+ {(isLoadingActions || isLoadingActionTypes) && }
{actionConnectorTableItems.length !== 0 && table}
{actionConnectorTableItems.length === 0 &&
canSave &&
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx
index 48360647e24ee..7a12c43427a91 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx
@@ -10,7 +10,7 @@ import { createMemoryHistory, createLocation } from 'history';
import { ToastsApi } from 'kibana/public';
import { AlertDetailsRoute, getAlertData } from './alert_details_route';
import { Alert } from '../../../../types';
-import { EuiLoadingSpinner } from '@elastic/eui';
+import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
jest.mock('../../../../common/lib/kibana');
describe('alert_details_route', () => {
@@ -20,7 +20,7 @@ describe('alert_details_route', () => {
expect(
shallow(
- ).containsMatchingElement( )
+ ).containsMatchingElement( )
).toBeTruthy();
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx
index fc3e05fbfaed0..ae729dd4f0095 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx
@@ -7,7 +7,6 @@
import { i18n } from '@kbn/i18n';
import React, { useState, useEffect } from 'react';
import { RouteComponentProps } from 'react-router-dom';
-import { EuiLoadingSpinner } from '@elastic/eui';
import { ToastsApi } from 'kibana/public';
import { Alert, AlertType, ActionType } from '../../../../types';
import { AlertDetailsWithApi as AlertDetails } from './alert_details';
@@ -21,6 +20,7 @@ import {
withActionOperations,
} from '../../common/components/with_actions_api_operations';
import { useKibana } from '../../../../common/lib/kibana';
+import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
type AlertDetailsRouteProps = RouteComponentProps<{
alertId: string;
@@ -66,14 +66,7 @@ export const AlertDetailsRoute: React.FunctionComponent
requestRefresh={async () => requestRefresh(Date.now())}
/>
) : (
-
-
-
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx
index e3fe9cd86356a..dfaed32ff72ae 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx
@@ -9,7 +9,7 @@ import { shallow } from 'enzyme';
import { ToastsApi } from 'kibana/public';
import { AlertInstancesRoute, getAlertInstanceSummary } from './alert_instances_route';
import { Alert, AlertInstanceSummary, AlertType } from '../../../../types';
-import { EuiLoadingSpinner } from '@elastic/eui';
+import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
jest.mock('../../../../common/lib/kibana');
const fakeNow = new Date('2020-02-09T23:15:41.941Z');
@@ -23,7 +23,7 @@ describe('alert_instance_summary_route', () => {
expect(
shallow(
- ).containsMatchingElement( )
+ ).containsMatchingElement( )
).toBeTruthy();
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx
index e1e0866d886a3..a122d59959156 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx
@@ -7,7 +7,6 @@
import { i18n } from '@kbn/i18n';
import { ToastsApi } from 'kibana/public';
import React, { useState, useEffect } from 'react';
-import { EuiLoadingSpinner } from '@elastic/eui';
import { Alert, AlertInstanceSummary, AlertType } from '../../../../types';
import {
ComponentOpts as AlertApis,
@@ -15,6 +14,7 @@ import {
} from '../../common/components/with_bulk_alert_api_operations';
import { AlertInstancesWithApi as AlertInstances } from './alert_instances';
import { useKibana } from '../../../../common/lib/kibana';
+import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
type WithAlertInstanceSummaryProps = {
alert: Alert;
@@ -52,14 +52,7 @@ export const AlertInstancesRoute: React.FunctionComponent
) : (
-
-
-
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
index 3210d53841993..cf3d0bf1544c2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
@@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { Fragment, useState, useEffect, Suspense, useCallback } from 'react';
+import React, { Fragment, useState, useEffect, useCallback, Suspense } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -23,7 +23,6 @@ import {
EuiIconTip,
EuiButtonIcon,
EuiHorizontalRule,
- EuiLoadingSpinner,
EuiEmptyPrompt,
EuiListGroupItem,
EuiListGroup,
@@ -71,6 +70,7 @@ import { AlertNotifyWhen } from './alert_notify_when';
import { checkAlertTypeEnabled } from '../../lib/check_alert_type_enabled';
import { alertTypeCompare, alertTypeGroupCompare } from '../../lib/alert_type_compare';
import { VIEW_LICENSE_OPTIONS_LINK } from '../../../common/constants';
+import { SectionLoading } from '../../components/section_loading';
const ENTER_KEY = 13;
@@ -535,7 +535,16 @@ export const AlertForm = ({
alert.alertTypeId &&
selectedAlertType ? (
- }>
+
+
+
+ }
+ >
) : (
-
+
+
+
)}
);
};
-const CenterJustifiedSpinner = () => (
-
-
-
-
-
-);
-
const NoAuthorizedAlertTypes = ({ operation }: { operation: string }) => (
{
{loadedItems.length || isFilterApplied ? (
table
) : alertTypesState.isLoading || alertsState.isLoading ? (
-
-
-
-
-
+
) : authorizedToCreateAnyAlerts ? (
setAlertFlyoutVisibility(true)} />
) : (
From 319a4070d7b913af3894358a65897171884c09df Mon Sep 17 00:00:00 2001
From: Devon Thomson
Date: Fri, 18 Dec 2020 15:59:24 -0500
Subject: [PATCH 15/40] Fixed duplication of create new modal (#86489)
---
.../public/application/top_nav/dashboard_top_nav.tsx | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
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 937e6737d2716..915f245fbcd19 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
@@ -27,6 +27,7 @@ import { useKibana } from '../../services/kibana_react';
import { IndexPattern, SavedQuery, TimefilterContract } from '../../services/data';
import {
EmbeddableFactoryNotFoundError,
+ EmbeddableInput,
isErrorEmbeddable,
openAddPanelFlyout,
ViewMode,
@@ -135,10 +136,7 @@ export function DashboardTopNav({
if (!factory) {
throw new EmbeddableFactoryNotFoundError(type);
}
- const explicitInput = await factory.getExplicitInput();
- if (dashboardContainer) {
- await dashboardContainer.addNewEmbeddable(type, explicitInput);
- }
+ await factory.create({} as EmbeddableInput, dashboardContainer);
}, [dashboardContainer, embeddable]);
const onChangeViewMode = useCallback(
From 8e717205021677bb3fb4a40a0efc40385a8b8692 Mon Sep 17 00:00:00 2001
From: Constance
Date: Fri, 18 Dec 2020 13:25:35 -0800
Subject: [PATCH 16/40] [App Search] Sample Engines should have access to the
Crawler (#86502)
* Remove logic preventing Crawler from being used on sample engines
* Remove check around crawler button in DocumentCreationButtons
- primarily a UI thing - ideally we always want to show 4 buttons
---
.../document_creation_buttons.test.tsx | 24 +-------
.../document_creation_buttons.tsx | 57 ++++++++-----------
.../components/engine/engine_nav.test.tsx | 6 --
.../components/engine/engine_nav.tsx | 2 +-
4 files changed, 29 insertions(+), 60 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx
index 8bd473c003eb1..c76cc4b45fc19 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx
@@ -16,8 +16,6 @@ import { DocumentCreationButtons } from './';
describe('DocumentCreationButtons', () => {
const values = {
engineName: 'test-engine',
- isSampleEngine: false,
- myRole: { canViewEngineCrawler: true },
};
const actions = {
openDocumentCreation: jest.fn(),
@@ -56,25 +54,9 @@ describe('DocumentCreationButtons', () => {
expect(actions.openDocumentCreation).toHaveBeenCalledWith('api');
});
- describe('crawler card', () => {
- it('renders the crawler button with a link to the crawler page', () => {
- const wrapper = shallow( );
-
- expect(wrapper.find(EuiCardTo).prop('to')).toEqual('/engines/test-engine/crawler');
- });
-
- it('does not render the crawler button if the user does not have access', () => {
- setMockValues({ ...values, myRole: { canViewEngineCrawler: false } });
- const wrapper = shallow( );
-
- expect(wrapper.find(EuiCardTo)).toHaveLength(0);
- });
-
- it('does not render the crawler button for the sample engine', () => {
- setMockValues({ ...values, isSampleEngine: true });
- const wrapper = shallow( );
+ it('renders the crawler button with a link to the crawler page', () => {
+ const wrapper = shallow( );
- expect(wrapper.find(EuiCardTo)).toHaveLength(0);
- });
+ expect(wrapper.find(EuiCardTo).prop('to')).toEqual('/engines/test-engine/crawler');
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx
index edeae5205b646..ce7cae5678338 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx
@@ -22,7 +22,6 @@ import {
import { EuiCardTo } from '../../../shared/react_router_helpers';
import { DOCS_PREFIX, getEngineRoute, ENGINE_CRAWLER_PATH } from '../../routes';
-import { AppLogic } from '../../app_logic';
import { EngineLogic } from '../engine';
import { DocumentCreationLogic } from './';
@@ -34,11 +33,7 @@ interface Props {
export const DocumentCreationButtons: React.FC = ({ disabled = false }) => {
const { openDocumentCreation } = useActions(DocumentCreationLogic);
- const { engineName, isSampleEngine } = useValues(EngineLogic);
- const {
- myRole: { canViewEngineCrawler },
- } = useValues(AppLogic);
- const showCrawlerLink = canViewEngineCrawler && !isSampleEngine;
+ const { engineName } = useValues(EngineLogic);
const crawlerLink = getEngineRoute(engineName) + ENGINE_CRAWLER_PATH;
return (
@@ -61,7 +56,7 @@ export const DocumentCreationButtons: React.FC = ({ disabled = false }) =
-
+
= ({ disabled = false }) =
isDisabled={disabled}
/>
- {showCrawlerLink && (
-
- }
- betaBadgeLabel={i18n.translate(
- 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTitle',
- { defaultMessage: 'Beta' }
- )}
- betaBadgeTooltipContent={i18n.translate(
- 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTooltip',
- {
- defaultMessage:
- 'The Elastic Crawler is not GA. Please help us by reporting any bugs.',
- }
- )}
- to={crawlerLink}
- isDisabled={disabled}
- />
-
- )}
+
+ }
+ betaBadgeLabel={i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTitle',
+ { defaultMessage: 'Beta' }
+ )}
+ betaBadgeTooltipContent={i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTooltip',
+ {
+ defaultMessage:
+ 'The Elastic Crawler is not GA. Please help us by reporting any bugs.',
+ }
+ )}
+ to={crawlerLink}
+ isDisabled={disabled}
+ />
+
>
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx
index 0d2ce654d4a0a..2e419168f2e1b 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx
@@ -102,12 +102,6 @@ describe('EngineNav', () => {
const wrapper = shallow( );
expect(wrapper.find('[data-test-subj="EngineCrawlerLink"]')).toHaveLength(0);
});
-
- it('does not render for sample engine', () => {
- setMockValues({ ...values, myRole, isSampleEngine: true });
- const wrapper = shallow( );
- expect(wrapper.find('[data-test-subj="EngineCrawlerLink"]')).toHaveLength(0);
- });
});
describe('meta engine source engines link', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx
index 35389bbe4b3ba..0fed7cd0fc8fc 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx
@@ -153,7 +153,7 @@ export const EngineNav: React.FC = () => {
)}
- {canViewEngineCrawler && !isMetaEngine && !isSampleEngine && (
+ {canViewEngineCrawler && !isMetaEngine && (
Date: Fri, 18 Dec 2020 13:31:21 -0800
Subject: [PATCH 17/40] [App Search] Convert DocumentCreationModal to
DocumentCreationFlyout (#86508)
* Convert DocumentCreationModal to a Flyout
- Per discussion w/ Davey - it handles longer/detailed content better
* Update instances referencing DocumentCreationFlyout
* Update flyout children
- modal->flyout
- add hasBorder, set EuiTitle sizes, add flexgroup to footer buttons
---
.../document_creation/constants.tsx | 10 ++--
.../api_code_example.test.tsx | 22 ++++----
.../api_code_example.tsx | 46 ++++++++-------
.../paste_json_text.test.tsx | 22 ++++----
.../paste_json_text.tsx | 56 +++++++++++--------
.../show_creation_modes.test.tsx | 2 +-
.../show_creation_modes.tsx | 30 +++++-----
.../upload_json_file.test.tsx | 24 ++++----
.../upload_json_file.tsx | 56 +++++++++++--------
.../document_creation_buttons.test.tsx | 2 +-
....tsx => document_creation_flyout.test.tsx} | 32 +++++------
...modal.tsx => document_creation_flyout.tsx} | 17 +++---
.../document_creation_logic.ts | 2 +-
.../components/document_creation/index.ts | 2 +-
.../document_creation_button.test.tsx | 4 +-
.../documents/document_creation_button.tsx | 4 +-
.../engine_overview_empty.test.tsx | 4 +-
.../engine_overview/engine_overview_empty.tsx | 4 +-
18 files changed, 181 insertions(+), 158 deletions(-)
rename x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/{document_creation_modal.test.tsx => document_creation_flyout.test.tsx} (77%)
rename x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/{document_creation_modal.tsx => document_creation_flyout.tsx} (78%)
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx
index c4237da0d0e80..27c3410767d8a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx
@@ -6,12 +6,14 @@
import { i18n } from '@kbn/i18n';
-export const MODAL_CANCEL_BUTTON = i18n.translate(
- 'xpack.enterpriseSearch.appSearch.documentCreation.modalCancel',
+export const FLYOUT_ARIA_LABEL_ID = 'documentCreationFlyoutHeadingId';
+
+export const FLYOUT_CANCEL_BUTTON = i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.flyoutCancel',
{ defaultMessage: 'Cancel' }
);
-export const MODAL_CONTINUE_BUTTON = i18n.translate(
- 'xpack.enterpriseSearch.appSearch.documentCreation.modalContinue',
+export const FLYOUT_CONTINUE_BUTTON = i18n.translate(
+ 'xpack.enterpriseSearch.appSearch.documentCreation.flyoutContinue',
{ defaultMessage: 'Continue' }
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx
index 2dd46419528c1..ddce27789b82c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.test.tsx
@@ -11,7 +11,7 @@ import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { EuiCode, EuiCodeBlock, EuiButtonEmpty } from '@elastic/eui';
-import { ApiCodeExample, ModalHeader, ModalBody, ModalFooter } from './api_code_example';
+import { ApiCodeExample, FlyoutHeader, FlyoutBody, FlyoutFooter } from './api_code_example';
describe('ApiCodeExample', () => {
const values = {
@@ -29,23 +29,23 @@ describe('ApiCodeExample', () => {
it('renders', () => {
const wrapper = shallow( );
- expect(wrapper.find(ModalHeader)).toHaveLength(1);
- expect(wrapper.find(ModalBody)).toHaveLength(1);
- expect(wrapper.find(ModalFooter)).toHaveLength(1);
+ expect(wrapper.find(FlyoutHeader)).toHaveLength(1);
+ expect(wrapper.find(FlyoutBody)).toHaveLength(1);
+ expect(wrapper.find(FlyoutFooter)).toHaveLength(1);
});
- describe('ModalHeader', () => {
+ describe('FlyoutHeader', () => {
it('renders', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find('h2').text()).toEqual('Indexing by API');
});
});
- describe('ModalBody', () => {
+ describe('FlyoutBody', () => {
let wrapper: ShallowWrapper;
beforeAll(() => {
- wrapper = shallow( );
+ wrapper = shallow( );
});
it('renders with the full remote Enterprise Search API URL', () => {
@@ -64,9 +64,9 @@ describe('ApiCodeExample', () => {
});
});
- describe('ModalFooter', () => {
- it('closes the modal', () => {
- const wrapper = shallow( );
+ describe('FlyoutFooter', () => {
+ it('closes the flyout', () => {
+ const wrapper = shallow( );
wrapper.find(EuiButtonEmpty).simulate('click');
expect(actions.closeDocumentCreation).toHaveBeenCalled();
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx
index 1dd57ffe8bc01..9ebe404659ca2 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx
@@ -11,10 +11,10 @@ import { useValues, useActions } from 'kea';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
- EuiModalHeader,
- EuiModalHeaderTitle,
- EuiModalBody,
- EuiModalFooter,
+ EuiFlyoutHeader,
+ EuiTitle,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
EuiButtonEmpty,
EuiText,
EuiLink,
@@ -30,39 +30,43 @@ import { EngineLogic } from '../../engine';
import { EngineDetails } from '../../engine/types';
import { DOCS_PREFIX } from '../../../routes';
-import { DOCUMENTS_API_JSON_EXAMPLE, MODAL_CANCEL_BUTTON } from '../constants';
+import {
+ DOCUMENTS_API_JSON_EXAMPLE,
+ FLYOUT_ARIA_LABEL_ID,
+ FLYOUT_CANCEL_BUTTON,
+} from '../constants';
import { DocumentCreationLogic } from '../';
export const ApiCodeExample: React.FC = () => (
<>
-
-
-
+
+
+
>
);
-export const ModalHeader: React.FC = () => {
+export const FlyoutHeader: React.FC = () => {
return (
-
-
-
+
+
+
{i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.api.title', {
defaultMessage: 'Indexing by API',
})}
-
-
+
+
);
};
-export const ModalBody: React.FC = () => {
+export const FlyoutBody: React.FC = () => {
const { engineName, engine } = useValues(EngineLogic);
const { apiKey } = engine as EngineDetails;
const documentsApiUrl = getEnterpriseSearchUrl(`/api/as/v1/engines/${engineName}/documents`);
return (
-
+
{
# ]
`)}
-
+
);
};
-export const ModalFooter: React.FC = () => {
+export const FlyoutFooter: React.FC = () => {
const { closeDocumentCreation } = useActions(DocumentCreationLogic);
return (
-
- {MODAL_CANCEL_BUTTON}
-
+
+ {FLYOUT_CANCEL_BUTTON}
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx
index ede1529c049d7..50e4d473e5f78 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx
@@ -11,7 +11,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { EuiTextArea, EuiButtonEmpty, EuiButton } from '@elastic/eui';
-import { PasteJsonText, ModalHeader, ModalBody, ModalFooter } from './paste_json_text';
+import { PasteJsonText, FlyoutHeader, FlyoutBody, FlyoutFooter } from './paste_json_text';
describe('PasteJsonText', () => {
const values = {
@@ -35,22 +35,22 @@ describe('PasteJsonText', () => {
it('renders', () => {
const wrapper = shallow( );
- expect(wrapper.find(ModalHeader)).toHaveLength(1);
- expect(wrapper.find(ModalBody)).toHaveLength(1);
- expect(wrapper.find(ModalFooter)).toHaveLength(1);
+ expect(wrapper.find(FlyoutHeader)).toHaveLength(1);
+ expect(wrapper.find(FlyoutBody)).toHaveLength(1);
+ expect(wrapper.find(FlyoutFooter)).toHaveLength(1);
});
- describe('ModalHeader', () => {
+ describe('FlyoutHeader', () => {
it('renders', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find('h2').text()).toEqual('Create documents');
});
});
- describe('ModalBody', () => {
+ describe('FlyoutBody', () => {
it('renders and updates the textarea value', () => {
setMockValues({ ...values, textInput: 'lorem ipsum' });
- const wrapper = shallow( );
+ const wrapper = shallow( );
const textarea = wrapper.find(EuiTextArea);
expect(textarea.prop('value')).toEqual('lorem ipsum');
@@ -60,16 +60,16 @@ describe('PasteJsonText', () => {
});
});
- describe('ModalFooter', () => {
+ describe('FlyoutFooter', () => {
it('closes the modal', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
wrapper.find(EuiButtonEmpty).simulate('click');
expect(actions.closeDocumentCreation).toHaveBeenCalled();
});
it('disables/enables the Continue button based on whether text has been entered', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(false);
setMockValues({ ...values, textInput: '' });
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx
index 614704701222b..ad83e0eb1a286 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx
@@ -9,10 +9,12 @@ import { useValues, useActions } from 'kea';
import { i18n } from '@kbn/i18n';
import {
- EuiModalHeader,
- EuiModalHeaderTitle,
- EuiModalBody,
- EuiModalFooter,
+ EuiFlyoutHeader,
+ EuiTitle,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiFlexGroup,
+ EuiFlexItem,
EuiButton,
EuiButtonEmpty,
EuiTextArea,
@@ -22,34 +24,34 @@ import {
import { AppLogic } from '../../../app_logic';
-import { MODAL_CANCEL_BUTTON, MODAL_CONTINUE_BUTTON } from '../constants';
+import { FLYOUT_ARIA_LABEL_ID, FLYOUT_CANCEL_BUTTON, FLYOUT_CONTINUE_BUTTON } from '../constants';
import { DocumentCreationLogic } from '../';
import './paste_json_text.scss';
export const PasteJsonText: React.FC = () => (
<>
-
-
-
+
+
+
>
);
-export const ModalHeader: React.FC = () => {
+export const FlyoutHeader: React.FC = () => {
return (
-
-
-
+
+
+
{i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.title', {
defaultMessage: 'Create documents',
})}
-
-
+
+
);
};
-export const ModalBody: React.FC = () => {
+export const FlyoutBody: React.FC = () => {
const { configuredLimits } = useValues(AppLogic);
const maxDocumentByteSize = configuredLimits?.engine?.maxDocumentByteSize;
@@ -57,7 +59,7 @@ export const ModalBody: React.FC = () => {
const { setTextInput } = useActions(DocumentCreationLogic);
return (
-
+
{i18n.translate(
@@ -82,20 +84,26 @@ export const ModalBody: React.FC = () => {
fullWidth
rows={12}
/>
-
+
);
};
-export const ModalFooter: React.FC = () => {
+export const FlyoutFooter: React.FC = () => {
const { textInput } = useValues(DocumentCreationLogic);
const { closeDocumentCreation } = useActions(DocumentCreationLogic);
return (
-
- {MODAL_CANCEL_BUTTON}
-
- {MODAL_CONTINUE_BUTTON}
-
-
+
+
+
+ {FLYOUT_CANCEL_BUTTON}
+
+
+
+ {FLYOUT_CONTINUE_BUTTON}
+
+
+
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx
index eadcf6df473e5..d02545625345d 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.test.tsx
@@ -30,7 +30,7 @@ describe('ShowCreationModes', () => {
expect(wrapper.find(DocumentCreationButtons)).toHaveLength(1);
});
- it('closes the modal', () => {
+ it('closes the flyout', () => {
wrapper.find(EuiButtonEmpty).simulate('click');
expect(actions.closeDocumentCreation).toHaveBeenCalled();
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx
index 1f7c4db83ab06..f923661a57bcc 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx
@@ -9,14 +9,14 @@ import { useActions } from 'kea';
import { i18n } from '@kbn/i18n';
import {
- EuiModalHeader,
- EuiModalHeaderTitle,
- EuiModalBody,
- EuiModalFooter,
+ EuiFlyoutHeader,
+ EuiTitle,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
EuiButtonEmpty,
} from '@elastic/eui';
-import { MODAL_CANCEL_BUTTON } from '../constants';
+import { FLYOUT_ARIA_LABEL_ID, FLYOUT_CANCEL_BUTTON } from '../constants';
import { DocumentCreationLogic, DocumentCreationButtons } from '../';
export const ShowCreationModes: React.FC = () => {
@@ -24,22 +24,22 @@ export const ShowCreationModes: React.FC = () => {
return (
<>
-
-
-
+
+
+
{i18n.translate(
'xpack.enterpriseSearch.appSearch.documentCreation.showCreationModes.title',
{ defaultMessage: 'Add new documents' }
)}
-
-
-
+
+
+
-
-
- {MODAL_CANCEL_BUTTON}
-
+
+
+ {FLYOUT_CANCEL_BUTTON}
+
>
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx
index dae085617cad8..72a245df817ba 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx
@@ -16,7 +16,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { EuiFilePicker, EuiButtonEmpty, EuiButton } from '@elastic/eui';
-import { UploadJsonFile, ModalHeader, ModalBody, ModalFooter } from './upload_json_file';
+import { UploadJsonFile, FlyoutHeader, FlyoutBody, FlyoutFooter } from './upload_json_file';
describe('UploadJsonFile', () => {
const mockFile = new File(['mock'], 'mock.json', { type: 'application/json' });
@@ -41,21 +41,21 @@ describe('UploadJsonFile', () => {
it('renders', () => {
const wrapper = shallow( );
- expect(wrapper.find(ModalHeader)).toHaveLength(1);
- expect(wrapper.find(ModalBody)).toHaveLength(1);
- expect(wrapper.find(ModalFooter)).toHaveLength(1);
+ expect(wrapper.find(FlyoutHeader)).toHaveLength(1);
+ expect(wrapper.find(FlyoutBody)).toHaveLength(1);
+ expect(wrapper.find(FlyoutFooter)).toHaveLength(1);
});
- describe('ModalHeader', () => {
+ describe('FlyoutHeader', () => {
it('renders', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find('h2').text()).toEqual('Drag and drop .json');
});
});
- describe('ModalBody', () => {
+ describe('FlyoutBody', () => {
it('updates fileInput when files are added & removed', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
wrapper.find(EuiFilePicker).simulate('change', [mockFile]);
expect(actions.setFileInput).toHaveBeenCalledWith(mockFile);
@@ -65,16 +65,16 @@ describe('UploadJsonFile', () => {
});
});
- describe('ModalFooter', () => {
- it('closes the modal', () => {
- const wrapper = shallow( );
+ describe('FlyoutFooter', () => {
+ it('closes the flyout', () => {
+ const wrapper = shallow( );
wrapper.find(EuiButtonEmpty).simulate('click');
expect(actions.closeDocumentCreation).toHaveBeenCalled();
});
it('disables/enables the Continue button based on whether files have been uploaded', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true);
setMockValues({ ...values, fineInput: mockFile });
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx
index d4c005d5cfa2b..6c5b1de79c320 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx
@@ -14,10 +14,12 @@ import { useValues, useActions } from 'kea';
import { i18n } from '@kbn/i18n';
import {
- EuiModalHeader,
- EuiModalHeaderTitle,
- EuiModalBody,
- EuiModalFooter,
+ EuiFlyoutHeader,
+ EuiTitle,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiFlexGroup,
+ EuiFlexItem,
EuiButton,
EuiButtonEmpty,
EuiFilePicker,
@@ -27,40 +29,40 @@ import {
import { AppLogic } from '../../../app_logic';
-import { MODAL_CANCEL_BUTTON, MODAL_CONTINUE_BUTTON } from '../constants';
+import { FLYOUT_ARIA_LABEL_ID, FLYOUT_CANCEL_BUTTON, FLYOUT_CONTINUE_BUTTON } from '../constants';
import { DocumentCreationLogic } from '../';
export const UploadJsonFile: React.FC = () => (
<>
-
-
-
+
+
+
>
);
-export const ModalHeader: React.FC = () => {
+export const FlyoutHeader: React.FC = () => {
return (
-
-
-
+
+
+
{i18n.translate(
'xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.title',
{ defaultMessage: 'Drag and drop .json' }
)}
-
-
+
+
);
};
-export const ModalBody: React.FC = () => {
+export const FlyoutBody: React.FC = () => {
const { configuredLimits } = useValues(AppLogic);
const maxDocumentByteSize = configuredLimits?.engine?.maxDocumentByteSize;
const { setFileInput } = useActions(DocumentCreationLogic);
return (
-
+
{i18n.translate(
@@ -79,20 +81,26 @@ export const ModalBody: React.FC = () => {
accept="application/json"
fullWidth
/>
-
+
);
};
-export const ModalFooter: React.FC = () => {
+export const FlyoutFooter: React.FC = () => {
const { fileInput } = useValues(DocumentCreationLogic);
const { closeDocumentCreation } = useActions(DocumentCreationLogic);
return (
-
- {MODAL_CANCEL_BUTTON}
-
- {MODAL_CONTINUE_BUTTON}
-
-
+
+
+
+ {FLYOUT_CANCEL_BUTTON}
+
+
+
+ {FLYOUT_CONTINUE_BUTTON}
+
+
+
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx
index c76cc4b45fc19..93aff04b3f7c0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx
@@ -41,7 +41,7 @@ describe('DocumentCreationButtons', () => {
expect(wrapper.find(EuiCardTo).prop('isDisabled')).toEqual(true);
});
- it('opens the DocumentCreationModal on click', () => {
+ it('opens the DocumentCreationFlyout on click', () => {
const wrapper = shallow( );
wrapper.find(EuiCard).at(0).simulate('click');
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx
similarity index 77%
rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.test.tsx
rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx
index a0bca62dc7419..f2799cde41e97 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx
@@ -8,7 +8,7 @@ import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock';
import React from 'react';
import { shallow } from 'enzyme';
-import { EuiModal } from '@elastic/eui';
+import { EuiFlyout } from '@elastic/eui';
import {
ShowCreationModes,
@@ -18,9 +18,9 @@ import {
} from './creation_mode_components';
import { DocumentCreationStep } from './types';
-import { DocumentCreationModal, ModalContent } from './document_creation_modal';
+import { DocumentCreationFlyout, FlyoutContent } from './document_creation_flyout';
-describe('DocumentCreationModal', () => {
+describe('DocumentCreationFlyout', () => {
const values = {
isDocumentCreationOpen: true,
creationMode: 'text',
@@ -36,25 +36,25 @@ describe('DocumentCreationModal', () => {
setMockActions(actions);
});
- it('renders a closeable modal', () => {
- const wrapper = shallow( );
- expect(wrapper.find(EuiModal)).toHaveLength(1);
+ it('renders a closeable flyout', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(EuiFlyout)).toHaveLength(1);
- wrapper.find(EuiModal).prop('onClose')();
+ wrapper.find(EuiFlyout).prop('onClose')();
expect(actions.closeDocumentCreation).toHaveBeenCalled();
});
it('does not render if isDocumentCreationOpen is false', () => {
setMockValues({ ...values, isDocumentCreationOpen: false });
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.isEmptyRender()).toBe(true);
});
- describe('ModalContent', () => {
+ describe('FlyoutContent', () => {
it('renders ShowCreationModes', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowCreationModes });
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find(ShowCreationModes)).toHaveLength(1);
});
@@ -62,21 +62,21 @@ describe('DocumentCreationModal', () => {
describe('creation modes', () => {
it('renders ApiCodeExample', () => {
setMockValues({ ...values, creationMode: 'api' });
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find(ApiCodeExample)).toHaveLength(1);
});
it('renders PasteJsonText', () => {
setMockValues({ ...values, creationMode: 'text' });
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find(PasteJsonText)).toHaveLength(1);
});
it('renders UploadJsonFile', () => {
setMockValues({ ...values, creationMode: 'file' });
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.find(UploadJsonFile)).toHaveLength(1);
});
@@ -85,21 +85,21 @@ describe('DocumentCreationModal', () => {
describe('creation steps', () => {
it('renders an error page', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowError });
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.text()).toBe('DocumentCreationError'); // TODO: actual component
});
it('renders an error summary', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowErrorSummary });
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper.text()).toBe('DocumentCreationSummary'); // TODO: actual component
});
it('renders a success summary', () => {
setMockValues({ ...values, creationStep: DocumentCreationStep.ShowSuccessSummary });
- const wrapper = shallow( );
+ const wrapper = shallow( );
// TODO: Figure out if the error and success summary should remain the same vs different components
expect(wrapper.text()).toBe('DocumentCreationSummary'); // TODO: actual component
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx
similarity index 78%
rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.tsx
rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx
index e6662a7c30407..ca52d14befb38 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_modal.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx
@@ -7,10 +7,11 @@
import React from 'react';
import { useValues, useActions } from 'kea';
-import { EuiOverlayMask, EuiModal } from '@elastic/eui';
+import { EuiPortal, EuiFlyout } from '@elastic/eui';
import { DocumentCreationLogic } from './';
import { DocumentCreationStep } from './types';
+import { FLYOUT_ARIA_LABEL_ID } from './constants';
import {
ShowCreationModes,
@@ -19,20 +20,20 @@ import {
UploadJsonFile,
} from './creation_mode_components';
-export const DocumentCreationModal: React.FC = () => {
+export const DocumentCreationFlyout: React.FC = () => {
const { closeDocumentCreation } = useActions(DocumentCreationLogic);
const { isDocumentCreationOpen } = useValues(DocumentCreationLogic);
return isDocumentCreationOpen ? (
-
-
-
-
-
+
+
+
+
+
) : null;
};
-export const ModalContent: React.FC = () => {
+export const FlyoutContent: React.FC = () => {
const { creationStep, creationMode } = useValues(DocumentCreationLogic);
switch (creationStep) {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts
index a5e015391d8fd..5b85e7f2ab54e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts
@@ -30,7 +30,7 @@ interface DocumentCreationActions {
export const DocumentCreationLogic = kea<
MakeLogicType
>({
- path: ['enterprise_search', 'app_search', 'document_creation_modal_logic'],
+ path: ['enterprise_search', 'app_search', 'document_creation_logic'],
actions: () => ({
showCreationModes: () => null,
openDocumentCreation: (creationMode) => ({ creationMode }),
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts
index 0f4eaaeda0e1a..d443b02393c05 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts
@@ -5,5 +5,5 @@
*/
export { DocumentCreationButtons } from './document_creation_buttons';
-export { DocumentCreationModal } from './document_creation_modal';
+export { DocumentCreationFlyout } from './document_creation_flyout';
export { DocumentCreationLogic } from './document_creation_logic';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.test.tsx
index 17e6e2538f044..52fa0d03a9719 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.test.tsx
@@ -10,7 +10,7 @@ import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { EuiButton } from '@elastic/eui';
-import { DocumentCreationModal } from '../document_creation';
+import { DocumentCreationFlyout } from '../document_creation';
import { DocumentCreationButton } from './document_creation_button';
describe('DocumentCreationButton', () => {
@@ -24,7 +24,7 @@ describe('DocumentCreationButton', () => {
it('renders', () => {
expect(wrapper.find(EuiButton).length).toEqual(1);
- expect(wrapper.find(DocumentCreationModal).length).toEqual(1);
+ expect(wrapper.find(DocumentCreationFlyout).length).toEqual(1);
});
it('opens the document creation modes modal on click', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.tsx
index 6d211cf45ca9f..3e4039bafcac7 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.tsx
@@ -10,7 +10,7 @@ import { useActions } from 'kea';
import { i18n } from '@kbn/i18n';
import { EuiButton } from '@elastic/eui';
-import { DocumentCreationLogic, DocumentCreationModal } from '../document_creation';
+import { DocumentCreationLogic, DocumentCreationFlyout } from '../document_creation';
export const DocumentCreationButton: React.FC = () => {
const { showCreationModes } = useActions(DocumentCreationLogic);
@@ -27,7 +27,7 @@ export const DocumentCreationButton: React.FC = () => {
defaultMessage: 'Index documents',
})}
-
+
>
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx
index ad7874c01655b..6c46c849c79bc 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx
@@ -10,7 +10,7 @@ import { EuiButton } from '@elastic/eui';
import { CURRENT_MAJOR_VERSION } from '../../../../../common/version';
-import { DocumentCreationButtons, DocumentCreationModal } from '../document_creation';
+import { DocumentCreationButtons, DocumentCreationFlyout } from '../document_creation';
import { EmptyEngineOverview } from './engine_overview_empty';
describe('EmptyEngineOverview', () => {
@@ -32,6 +32,6 @@ describe('EmptyEngineOverview', () => {
it('renders document creation components', () => {
expect(wrapper.find(DocumentCreationButtons)).toHaveLength(1);
- expect(wrapper.find(DocumentCreationModal)).toHaveLength(1);
+ expect(wrapper.find(DocumentCreationFlyout)).toHaveLength(1);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx
index d65ca4868d282..83dd396e5e080 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx
@@ -16,7 +16,7 @@ import {
} from '@elastic/eui';
import { DOCS_PREFIX } from '../../routes';
-import { DocumentCreationButtons, DocumentCreationModal } from '../document_creation';
+import { DocumentCreationButtons, DocumentCreationFlyout } from '../document_creation';
export const EmptyEngineOverview: React.FC = () => {
return (
@@ -42,7 +42,7 @@ export const EmptyEngineOverview: React.FC = () => {
-
+
>
);
From 4bf2de7b0d84b002b103bdbdc0097dedd55d4799 Mon Sep 17 00:00:00 2001
From: Matthias Wilhelm
Date: Fri, 18 Dec 2020 22:59:59 +0100
Subject: [PATCH 18/40] [Discover] Change default sort handling (#85561)
---
.../public/application/angular/discover.js | 8 ++-
.../doc_table/lib/get_default_sort.test.ts | 43 ++++++++++++++++
.../angular/doc_table/lib/get_default_sort.ts | 7 ++-
.../lib/get_sort_for_search_source.test.ts | 51 +++++++++++++++++++
.../lib/get_sort_for_search_source.ts | 12 +++--
.../embeddable/search_embeddable.ts | 17 ++++++-
.../helpers/get_sharing_data.test.ts | 14 ++++-
.../functional/apps/discover/_shared_links.ts | 2 +-
8 files changed, 140 insertions(+), 14 deletions(-)
create mode 100644 src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts
create mode 100644 src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts
diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js
index 2c3b8fd9606a9..99497d61c716e 100644
--- a/src/plugins/discover/public/application/angular/discover.js
+++ b/src/plugins/discover/public/application/angular/discover.js
@@ -65,11 +65,13 @@ import {
MODIFY_COLUMNS_ON_SWITCH,
SAMPLE_SIZE_SETTING,
SEARCH_ON_PAGE_LOAD_SETTING,
+ SORT_DEFAULT_ORDER_SETTING,
} from '../../../common';
import { loadIndexPattern, resolveIndexPattern } from '../helpers/resolve_index_pattern';
import { getTopNavLinks } from '../components/top_nav/get_top_nav_links';
import { updateSearchSource } from '../helpers/update_search_source';
import { calcFieldCounts } from '../helpers/calc_field_counts';
+import { getDefaultSort } from './doc_table/lib/get_default_sort';
const services = getServices();
@@ -410,9 +412,13 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab
function getStateDefaults() {
const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery();
+ const sort = getSortArray(savedSearch.sort, $scope.indexPattern);
+
return {
query,
- sort: getSortArray(savedSearch.sort, $scope.indexPattern),
+ sort: !sort.length
+ ? getDefaultSort($scope.indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING, 'desc'))
+ : sort,
columns:
savedSearch.columns.length > 0
? savedSearch.columns
diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts
new file mode 100644
index 0000000000000..9ad19653a6c12
--- /dev/null
+++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts
@@ -0,0 +1,43 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { getDefaultSort } from './get_default_sort';
+// @ts-ignore
+import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
+import { IndexPattern } from '../../../../kibana_services';
+
+describe('getDefaultSort function', function () {
+ let indexPattern: IndexPattern;
+ beforeEach(() => {
+ indexPattern = FixturesStubbedLogstashIndexPatternProvider() as IndexPattern;
+ });
+ test('should be a function', function () {
+ expect(typeof getDefaultSort === 'function').toBeTruthy();
+ });
+
+ test('should return default sort for an index pattern with timeFieldName', function () {
+ expect(getDefaultSort(indexPattern, 'desc')).toEqual([['time', 'desc']]);
+ expect(getDefaultSort(indexPattern, 'asc')).toEqual([['time', 'asc']]);
+ });
+
+ test('should return default sort for an index pattern without timeFieldName', function () {
+ delete indexPattern.timeFieldName;
+ expect(getDefaultSort(indexPattern, 'desc')).toEqual([]);
+ expect(getDefaultSort(indexPattern, 'asc')).toEqual([]);
+ });
+});
diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.ts
index 634e3cfec3a0b..c1e4da0bab54d 100644
--- a/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.ts
+++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.ts
@@ -17,7 +17,6 @@
* under the License.
*/
import { IndexPattern } from '../../../../kibana_services';
-// @ts-ignore
import { isSortable } from './get_sort';
import { SortOrder } from '../components/table_header/helpers';
@@ -26,12 +25,12 @@ import { SortOrder } from '../components/table_header/helpers';
* the default sort is returned depending of the index pattern
*/
export function getDefaultSort(
- indexPattern: IndexPattern,
+ indexPattern: IndexPattern | undefined,
defaultSortOrder: string = 'desc'
): SortOrder[] {
- if (indexPattern.timeFieldName && isSortable(indexPattern.timeFieldName, indexPattern)) {
+ if (indexPattern?.timeFieldName && isSortable(indexPattern.timeFieldName, indexPattern)) {
return [[indexPattern.timeFieldName, defaultSortOrder]];
} else {
- return [['_score', defaultSortOrder]];
+ return [];
}
}
diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts
new file mode 100644
index 0000000000000..1dbd31897d307
--- /dev/null
+++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts
@@ -0,0 +1,51 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { getSortForSearchSource } from './get_sort_for_search_source';
+// @ts-ignore
+import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
+import { IndexPattern } from '../../../../kibana_services';
+import { SortOrder } from '../components/table_header/helpers';
+
+describe('getSortForSearchSource function', function () {
+ let indexPattern: IndexPattern;
+ beforeEach(() => {
+ indexPattern = FixturesStubbedLogstashIndexPatternProvider() as IndexPattern;
+ });
+ test('should be a function', function () {
+ expect(typeof getSortForSearchSource === 'function').toBeTruthy();
+ });
+
+ test('should return an object to use for searchSource when columns are given', function () {
+ const cols = [['bytes', 'desc']] as SortOrder[];
+ expect(getSortForSearchSource(cols, indexPattern)).toEqual([{ bytes: 'desc' }]);
+ expect(getSortForSearchSource(cols, indexPattern, 'asc')).toEqual([{ bytes: 'desc' }]);
+ delete indexPattern.timeFieldName;
+ expect(getSortForSearchSource(cols, indexPattern)).toEqual([{ bytes: 'desc' }]);
+ expect(getSortForSearchSource(cols, indexPattern, 'asc')).toEqual([{ bytes: 'desc' }]);
+ });
+
+ test('should return an object to use for searchSource when no columns are given', function () {
+ const cols = [] as SortOrder[];
+ expect(getSortForSearchSource(cols, indexPattern)).toEqual([{ _doc: 'desc' }]);
+ expect(getSortForSearchSource(cols, indexPattern, 'asc')).toEqual([{ _doc: 'asc' }]);
+ delete indexPattern.timeFieldName;
+ expect(getSortForSearchSource(cols, indexPattern)).toEqual([{ _score: 'desc' }]);
+ expect(getSortForSearchSource(cols, indexPattern, 'asc')).toEqual([{ _score: 'asc' }]);
+ });
+});
diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.ts
index 6721f7a03584c..1244a0e229cdb 100644
--- a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.ts
+++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.ts
@@ -19,7 +19,6 @@
import { EsQuerySortValue, IndexPattern } from '../../../../kibana_services';
import { SortOrder } from '../components/table_header/helpers';
import { getSort } from './get_sort';
-import { getDefaultSort } from './get_default_sort';
/**
* Prepares sort for search source, that's sending the request to ES
@@ -33,10 +32,13 @@ export function getSortForSearchSource(
indexPattern?: IndexPattern,
defaultDirection: string = 'desc'
): EsQuerySortValue[] {
- if (!sort || !indexPattern) {
- return [];
- } else if (Array.isArray(sort) && sort.length === 0) {
- sort = getDefaultSort(indexPattern, defaultDirection);
+ if (!sort || !indexPattern || (Array.isArray(sort) && sort.length === 0)) {
+ if (indexPattern?.timeFieldName) {
+ // sorting by index order
+ return [{ _doc: defaultDirection } as EsQuerySortValue];
+ } else {
+ return [{ _score: defaultDirection } as EsQuerySortValue];
+ }
}
const { timeFieldName } = indexPattern;
return getSort(sort, indexPattern).map((sortPair: Record) => {
diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
index b143afd1988e6..ff408ba431ed9 100644
--- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts
+++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
@@ -48,6 +48,7 @@ import {
import { SEARCH_EMBEDDABLE_TYPE } from './constants';
import { SavedSearch } from '../..';
import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common';
+import { getDefaultSort } from '../angular/doc_table/lib/get_default_sort';
interface SearchScope extends ng.IScope {
columns?: string[];
@@ -200,6 +201,13 @@ export class SearchEmbeddable
const { searchSource } = this.savedSearch;
const indexPattern = (searchScope.indexPattern = searchSource.getField('index'))!;
+ if (!this.savedSearch.sort || !this.savedSearch.sort.length) {
+ this.savedSearch.sort = getDefaultSort(
+ indexPattern,
+ getServices().uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc')
+ );
+ }
+
const timeRangeSearchSource = searchSource.create();
timeRangeSearchSource.setField('filter', () => {
if (!this.searchScope || !this.input.timeRange) return;
@@ -341,7 +349,14 @@ export class SearchEmbeddable
// If there is column or sort data on the panel, that means the original columns or sort settings have
// been overridden in a dashboard.
searchScope.columns = this.input.columns || this.savedSearch.columns;
- searchScope.sort = this.input.sort || this.savedSearch.sort;
+ const savedSearchSort =
+ this.savedSearch.sort && this.savedSearch.sort.length
+ ? this.savedSearch.sort
+ : getDefaultSort(
+ this.searchScope?.indexPattern,
+ getServices().uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc')
+ );
+ searchScope.sort = this.input.sort || savedSearchSort;
searchScope.sharedItemTitle = this.panelTitle;
if (forceFetch || isFetchRequired) {
diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts
index b2aa3a05d7eb0..4dec1f75ba322 100644
--- a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts
+++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts
@@ -21,6 +21,7 @@ import { getSharingData } from './get_sharing_data';
import { IUiSettingsClient } from 'kibana/public';
import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks';
import { indexPatternMock } from '../../__mocks__/index_pattern';
+import { SORT_DEFAULT_ORDER_SETTING } from '../../../common';
describe('getSharingData', () => {
test('returns valid data for sharing', async () => {
@@ -29,7 +30,10 @@ describe('getSharingData', () => {
searchSourceMock,
{ columns: [] },
({
- get: () => {
+ get: (key: string) => {
+ if (key === SORT_DEFAULT_ORDER_SETTING) {
+ return 'desc';
+ }
return false;
},
} as unknown) as IUiSettingsClient,
@@ -57,7 +61,13 @@ describe('getSharingData', () => {
},
},
"script_fields": Object {},
- "sort": Array [],
+ "sort": Array [
+ Object {
+ "_score": Object {
+ "order": "desc",
+ },
+ },
+ ],
"stored_fields": undefined,
},
"index": "the-index-pattern-title",
diff --git a/test/functional/apps/discover/_shared_links.ts b/test/functional/apps/discover/_shared_links.ts
index a676878382865..51ea5f997e859 100644
--- a/test/functional/apps/discover/_shared_links.ts
+++ b/test/functional/apps/discover/_shared_links.ts
@@ -90,7 +90,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
":(from:'2015-09-19T06:31:44.000Z',to:'2015-09" +
"-23T18:31:44.000Z'))&_a=(columns:!(_source),filters:!(),index:'logstash-" +
"*',interval:auto,query:(language:kuery,query:'')" +
- ',sort:!())';
+ ",sort:!(!('@timestamp',desc)))";
const actualUrl = await PageObjects.share.getSharedUrl();
// strip the timestamp out of each URL
expect(actualUrl.replace(/_t=\d{13}/, '_t=TIMESTAMP')).to.be(
From 563fa6de22be7f0c00133e17cbe3639e37eb031f Mon Sep 17 00:00:00 2001
From: Jason Stoltzfus
Date: Fri, 18 Dec 2020 17:01:05 -0500
Subject: [PATCH 19/40] [App Search] Updates to results on the documents view
(#86181)
---
.../build_search_ui_config.test.ts | 38 ++++++++++
.../build_search_ui_config.ts | 34 +++++++++
.../search_experience/search_experience.tsx | 11 +--
.../search_experience_content.test.tsx | 11 ++-
.../search_experience_content.tsx | 4 +-
.../views/result_view.test.tsx | 13 +++-
.../search_experience/views/result_view.tsx | 10 ++-
.../app_search/components/library/library.tsx | 37 +++++++++-
.../app_search/components/result/result.scss | 1 +
.../components/result/result.test.tsx | 36 +++++++++
.../app_search/components/result/result.tsx | 73 +++++++++++++------
.../components/result/result_field_value.scss | 5 --
.../result/result_field_value.test.tsx | 4 +-
.../components/result/result_field_value.tsx | 5 +-
.../public/applications/app_search/routes.ts | 3 +
15 files changed, 235 insertions(+), 50 deletions(-)
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts
new file mode 100644
index 0000000000000..dd52f6b8227ba
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { SchemaTypes } from '../../../../shared/types';
+
+import { buildSearchUIConfig } from './build_search_ui_config';
+
+describe('buildSearchUIConfig', () => {
+ it('builds a configuration object for Search UI', () => {
+ const connector = {};
+ const schema = {
+ foo: 'text' as SchemaTypes,
+ bar: 'number' as SchemaTypes,
+ };
+
+ const config = buildSearchUIConfig(connector, schema);
+ expect(config.apiConnector).toEqual(connector);
+ expect(config.searchQuery.result_fields).toEqual({
+ bar: {
+ raw: {},
+ snippet: {
+ fallback: true,
+ size: 300,
+ },
+ },
+ foo: {
+ raw: {},
+ snippet: {
+ fallback: true,
+ size: 300,
+ },
+ },
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts
new file mode 100644
index 0000000000000..533adbaf5bab9
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Schema } from '../../../../shared/types';
+
+export const buildSearchUIConfig = (apiConnector: object, schema: Schema) => {
+ return {
+ alwaysSearchOnInitialLoad: true,
+ apiConnector,
+ trackUrlState: false,
+ initialState: {
+ sortDirection: 'desc',
+ sortField: 'id',
+ },
+ searchQuery: {
+ result_fields: Object.keys(schema || {}).reduce(
+ (acc: { [key: string]: object }, key: string) => {
+ acc[key] = {
+ snippet: {
+ size: 300,
+ fallback: true,
+ },
+ raw: {},
+ };
+ return acc;
+ },
+ {}
+ ),
+ },
+ };
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx
index 49cc573b686bc..1501efc589fc0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx
@@ -20,6 +20,7 @@ import { externalUrl } from '../../../../shared/enterprise_search_url';
import { SearchBoxView, SortingView } from './views';
import { SearchExperienceContent } from './search_experience_content';
+import { buildSearchUIConfig } from './build_search_ui_config';
const DEFAULT_SORT_OPTIONS = [
{
@@ -52,15 +53,7 @@ export const SearchExperience: React.FC = () => {
searchKey: engine.apiKey,
});
- const searchProviderConfig = {
- alwaysSearchOnInitialLoad: true,
- apiConnector: connector,
- trackUrlState: false,
- initialState: {
- sortDirection: 'desc',
- sortField: 'id',
- },
- };
+ const searchProviderConfig = buildSearchUIConfig(connector, engine.schema || {});
return (
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx
index 455e237848a4b..a46ec560a13e0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx
@@ -15,6 +15,7 @@ import { Results } from '@elastic/react-search-ui';
import { ResultView } from './views';
import { Pagination } from './pagination';
+import { SchemaTypes } from '../../../../shared/types';
import { SearchExperienceContent } from './search_experience_content';
describe('SearchExperienceContent', () => {
@@ -27,6 +28,11 @@ describe('SearchExperienceContent', () => {
engineName: 'engine1',
isMetaEngine: false,
myRole: { canManageEngineDocuments: true },
+ engine: {
+ schema: {
+ title: 'string' as SchemaTypes,
+ },
+ },
};
beforeEach(() => {
@@ -40,7 +46,7 @@ describe('SearchExperienceContent', () => {
expect(wrapper.isEmptyRender()).toBe(false);
});
- it('passes engineName to the result view', () => {
+ it('passes engineName and schema to the result view', () => {
const props = {
result: {
id: {
@@ -56,6 +62,9 @@ describe('SearchExperienceContent', () => {
raw: 'bar',
},
},
+ schemaForTypeHighlights: {
+ title: 'string' as SchemaTypes,
+ },
};
const wrapper = shallow(
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx
index 9194a3a1db5e4..55a8377261dd9 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx
@@ -25,7 +25,7 @@ export const SearchExperienceContent: React.FC = () => {
const { resultSearchTerm, totalResults, wasSearched } = useSearchContextState();
const { myRole } = useValues(AppLogic);
- const { isMetaEngine } = useValues(EngineLogic);
+ const { isMetaEngine, engine } = useValues(EngineLogic);
if (!wasSearched) return null;
@@ -44,7 +44,7 @@ export const SearchExperienceContent: React.FC = () => {
{
- return ;
+ return ;
}}
/>
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx
index 049a3ad1bed66..91334f312623d 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx
@@ -9,6 +9,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { ResultView } from '.';
+import { SchemaTypes } from '../../../../../shared/types';
import { Result } from '../../../result/result';
describe('ResultView', () => {
@@ -27,8 +28,16 @@ describe('ResultView', () => {
},
};
+ const schema = {
+ title: 'string' as SchemaTypes,
+ };
+
it('renders', () => {
- const wrapper = shallow( );
- expect(wrapper.find(Result).exists()).toBe(true);
+ const wrapper = shallow( );
+ expect(wrapper.find(Result).props()).toEqual({
+ result,
+ shouldLinkToDetailPage: true,
+ schemaForTypeHighlights: schema,
+ });
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx
index 52b845a1aee2d..543c63b334940 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx
@@ -7,16 +7,22 @@
import React from 'react';
import { Result as ResultType } from '../../../result/types';
+import { Schema } from '../../../../../shared/types';
import { Result } from '../../../result/result';
export interface Props {
result: ResultType;
+ schemaForTypeHighlights?: Schema;
}
-export const ResultView: React.FC = ({ result }) => {
+export const ResultView: React.FC = ({ result, schemaForTypeHighlights }) => {
return (
-
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx
index 66c0cc165fc05..1b222cfaacf7c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx
@@ -15,6 +15,7 @@ import {
import React from 'react';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
+import { Schema } from '../../../shared/types';
import { Result } from '../result/result';
export const Library: React.FC = () => {
@@ -35,12 +36,18 @@ export const Library: React.FC = () => {
description: {
raw: 'A description',
},
- states: {
- raw: ['Pennsylvania', 'Ohio'],
+ date_established: {
+ raw: '1968-10-02T05:00:00Z',
+ },
+ location: {
+ raw: '37.3,-113.05',
},
visitors: {
raw: 1000,
},
+ states: {
+ raw: ['Pennsylvania', 'Ohio'],
+ },
size: {
raw: 200,
},
@@ -50,6 +57,17 @@ export const Library: React.FC = () => {
},
};
+ const schema: Schema = {
+ title: 'text',
+ description: 'text',
+ date_established: 'date',
+ location: 'geolocation',
+ states: 'text',
+ visitors: 'number',
+ size: 'number',
+ length: 'number',
+ };
+
return (
<>
@@ -170,6 +188,21 @@ export const Library: React.FC = () => {
},
}}
/>
+
+
+
+ With a link
+
+
+
+
+
+
+
+ With field value type highlights
+
+
+
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss
index ed8ce512a2eb8..8342061ee00c3 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss
@@ -5,6 +5,7 @@
width: 100%;
padding: $euiSize;
overflow: hidden;
+ color: $euiTextColor;
}
&__hiddenFieldsIndicator {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx
index ade26551039fa..5b598a0b8565e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx
@@ -11,6 +11,9 @@ import { EuiPanel } from '@elastic/eui';
import { ResultField } from './result_field';
import { ResultHeader } from './result_header';
+import { ReactRouterHelper } from '../../../shared/react_router_helpers/eui_components';
+import { SchemaTypes } from '../../../shared/types';
+
import { Result } from './result';
describe('Result', () => {
@@ -37,6 +40,12 @@ describe('Result', () => {
},
};
+ const schema = {
+ title: 'text' as SchemaTypes,
+ description: 'text' as SchemaTypes,
+ length: 'number' as SchemaTypes,
+ };
+
it('renders', () => {
const wrapper = shallow( );
expect(wrapper.find(EuiPanel).exists()).toBe(true);
@@ -62,6 +71,33 @@ describe('Result', () => {
});
});
+ describe('document detail link', () => {
+ it('will render a link if shouldLinkToDetailPage is true', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(ReactRouterHelper).prop('to')).toEqual('/engines/my-engine/documents/1');
+ expect(wrapper.find('article.appSearchResult__content').exists()).toBe(false);
+ expect(wrapper.find('a.appSearchResult__content').exists()).toBe(true);
+ });
+
+ it('will not render a link if shouldLinkToDetailPage is not set', () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(ReactRouterHelper).exists()).toBe(false);
+ expect(wrapper.find('article.appSearchResult__content').exists()).toBe(true);
+ expect(wrapper.find('a.appSearchResult__content').exists()).toBe(false);
+ });
+ });
+
+ it('will render field details with type highlights if schemaForTypeHighlights has been provided', () => {
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find(ResultField).map((rf) => rf.prop('type'))).toEqual([
+ 'text',
+ 'text',
+ 'number',
+ ]);
+ });
+
describe('when there are more than 5 fields', () => {
const propsWithMoreFields = {
result: {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx
index 4f343e64b12ae..11415f5512380 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx
@@ -14,15 +14,25 @@ import { i18n } from '@kbn/i18n';
import { FieldValue, Result as ResultType } from './types';
import { ResultField } from './result_field';
import { ResultHeader } from './result_header';
+import { getDocumentDetailRoute } from '../../routes';
+import { ReactRouterHelper } from '../../../shared/react_router_helpers/eui_components';
+import { Schema } from '../../../shared/types';
interface Props {
result: ResultType;
showScore?: boolean;
+ shouldLinkToDetailPage?: boolean;
+ schemaForTypeHighlights?: Schema;
}
const RESULT_CUTOFF = 5;
-export const Result: React.FC = ({ result, showScore }) => {
+export const Result: React.FC = ({
+ result,
+ showScore = false,
+ shouldLinkToDetailPage = false,
+ schemaForTypeHighlights,
+}) => {
const [isOpen, setIsOpen] = useState(false);
const ID = 'id';
@@ -33,6 +43,19 @@ export const Result: React.FC = ({ result, showScore }) => {
[result]
);
const numResults = resultFields.length;
+ const typeForField = (fieldName: string) => {
+ if (schemaForTypeHighlights) return schemaForTypeHighlights[fieldName];
+ };
+
+ const conditionallyLinkedArticle = (children: React.ReactNode) => {
+ return shouldLinkToDetailPage ? (
+
+ {children}
+
+ ) : (
+ {children}
+ );
+ };
return (
= ({ result, showScore }) => {
defaultMessage: 'View document details',
})}
>
-
-
-
- {resultFields
- .slice(0, isOpen ? resultFields.length : RESULT_CUTOFF)
- .map(([field, value]: [string, FieldValue]) => (
-
- ))}
-
- {numResults > RESULT_CUTOFF && !isOpen && (
-
- {i18n.translate('xpack.enterpriseSearch.appSearch.result.numberOfAdditionalFields', {
- defaultMessage: '{numberOfAdditionalFields} more fields',
- values: {
- numberOfAdditionalFields: numResults - RESULT_CUTOFF,
- },
- })}
-
- )}
-
+ {conditionallyLinkedArticle(
+ <>
+
+
+ {resultFields
+ .slice(0, isOpen ? resultFields.length : RESULT_CUTOFF)
+ .map(([field, value]: [string, FieldValue]) => (
+
+ ))}
+
+ {numResults > RESULT_CUTOFF && !isOpen && (
+
+ {i18n.translate('xpack.enterpriseSearch.appSearch.result.numberOfAdditionalFields', {
+ defaultMessage: '{numberOfAdditionalFields} more fields',
+ values: {
+ numberOfAdditionalFields: numResults - RESULT_CUTOFF,
+ },
+ })}
+
+ )}
+ >
+ )}
{numResults > RESULT_CUTOFF && (
{
);
});
- it('will render content as html with class names appended to em tags', () => {
+ it('will render content as html with mark tags', () => {
expect(wrapper.find('div').html()).toContain(
- 'a long description'
+ 'a long description'
);
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field_value.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field_value.tsx
index 8e4b264017c0b..4d58a86a2ce55 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field_value.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field_value.tsx
@@ -22,10 +22,7 @@ const getRawArrayDisplay = (rawArray: Array): string => {
};
const parseHighlights = (highlight: string): string => {
- return highlight.replace(
- /(.+?)<\/em>/gi,
- '$1 '
- );
+ return highlight.replace(/(.+?)<\/em>/gi, '$1 ');
};
const isFieldValueEmpty = (type?: string, raw?: Raw, snippet?: Snippet) => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts
index ade3c9a917410..ca2ce177617b8 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts
@@ -29,6 +29,9 @@ export const ENGINE_ANALYTICS_PATH = '/analytics';
export const ENGINE_DOCUMENTS_PATH = '/documents';
export const ENGINE_DOCUMENT_DETAIL_PATH = `${ENGINE_DOCUMENTS_PATH}/:documentId`;
+export const getDocumentDetailRoute = (engineName: string, documentId: string) => {
+ return generatePath(ENGINE_PATH + ENGINE_DOCUMENT_DETAIL_PATH, { engineName, documentId });
+};
export const ENGINE_SCHEMA_PATH = '/schema/edit';
export const ENGINE_REINDEX_JOB_PATH = '/reindex-job/:activeReindexJobId';
From 646a34043ab9c18fba13e24523bc9c9686a85066 Mon Sep 17 00:00:00 2001
From: Brian Seeders
Date: Fri, 18 Dec 2020 17:32:58 -0500
Subject: [PATCH 20/40] Explicitly set Elasticsearch heap size during CI and
local development (#86513)
---
packages/kbn-es/src/cluster.js | 9 +++++++++
vars/workers.groovy | 2 +-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/packages/kbn-es/src/cluster.js b/packages/kbn-es/src/cluster.js
index 68bcc37c65600..eaf353b3e55d0 100644
--- a/packages/kbn-es/src/cluster.js
+++ b/packages/kbn-es/src/cluster.js
@@ -275,6 +275,15 @@ exports.Cluster = class Cluster {
this._log.debug('%s %s', ES_BIN, args.join(' '));
+ options.esEnvVars = options.esEnvVars || {};
+
+ // ES now automatically sets heap size to 50% of the machine's available memory
+ // so we need to set it to a smaller size for local dev and CI
+ // especially because we currently run many instances of ES on the same machine during CI
+ options.esEnvVars.ES_JAVA_OPTS =
+ (options.esEnvVars.ES_JAVA_OPTS ? `${options.esEnvVars.ES_JAVA_OPTS} ` : '') +
+ '-Xms1g -Xmx1g';
+
this._process = execa(ES_BIN, args, {
cwd: installPath,
env: {
diff --git a/vars/workers.groovy b/vars/workers.groovy
index 365c30204ebdc..a1d569595ab4b 100644
--- a/vars/workers.groovy
+++ b/vars/workers.groovy
@@ -147,7 +147,7 @@ def intake(jobName, String script) {
// Worker for running functional tests. Runs a setup process (e.g. the kibana build) then executes a map of closures in parallel (e.g. one for each ciGroup)
def functional(name, Closure setup, Map processes) {
return {
- parallelProcesses(name: name, setup: setup, processes: processes, delayBetweenProcesses: 20, size: 'xl-highmem')
+ parallelProcesses(name: name, setup: setup, processes: processes, delayBetweenProcesses: 20, size: 'xl')
}
}
From e3e166e05aaa36ac1ec0f0229ba28f4e60d79b92 Mon Sep 17 00:00:00 2001
From: Brian Seeders
Date: Fri, 18 Dec 2020 17:39:32 -0500
Subject: [PATCH 21/40] [CI] Bump memory for main CI workers (#86541)
---
vars/workers.groovy | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vars/workers.groovy b/vars/workers.groovy
index a1d569595ab4b..dd634f3c25a32 100644
--- a/vars/workers.groovy
+++ b/vars/workers.groovy
@@ -18,7 +18,7 @@ def label(size) {
case 'xl-highmem':
return 'docker && tests-xl-highmem'
case 'xxl':
- return 'docker && tests-xxl'
+ return 'docker && tests-xxl && gobld/machineType:custom-64-270336'
}
error "unknown size '${size}'"
From 94b49451efa40bf3376bc74c0c3d1547e8c124c5 Mon Sep 17 00:00:00 2001
From: igoristic
Date: Fri, 18 Dec 2020 18:09:14 -0500
Subject: [PATCH 22/40] [Monitoring][Alerting] CCR read exceptions alert
(#85908)
* CCR read exceptions all branches
* cleanup
* CR feedback
* Added UI/UX to ccr/shards listing and details
* Fixed snaps
* Added reason for the exception
* Added setup mode funtionality and alert status
---
x-pack/plugins/monitoring/common/constants.ts | 22 ++
.../plugins/monitoring/common/types/alerts.ts | 16 +
.../monitoring/public/alerts/callout.tsx | 29 +-
.../ccr_read_exceptions_alert/index.tsx | 49 +++
.../lib/get_alert_panels_by_category.tsx | 3 +-
.../alerts/lib/get_alert_panels_by_node.tsx | 6 +-
.../public/alerts/lib/replace_tokens.tsx | 1 +
.../monitoring/public/alerts/panel.tsx | 13 +
.../cluster/overview/elasticsearch_panel.js | 7 +-
.../ccr/__snapshots__/ccr.test.js.snap | 6 +
.../components/elasticsearch/ccr/ccr.js | 117 ++++---
.../__snapshots__/ccr_shard.test.js.snap | 154 +++++-----
.../elasticsearch/ccr_shard/ccr_shard.js | 16 +-
.../elasticsearch/ccr_shard/status.js | 9 +-
x-pack/plugins/monitoring/public/plugin.ts | 2 +
.../public/views/elasticsearch/ccr/index.js | 29 +-
.../views/elasticsearch/ccr/shard/index.js | 35 ++-
.../server/alerts/alerts_factory.ts | 4 +-
.../monitoring/server/alerts/base_alert.ts | 13 +-
.../alerts/ccr_read_exceptions_alert.ts | 289 ++++++++++++++++++
.../server/alerts/cpu_usage_alert.test.ts | 7 +
.../plugins/monitoring/server/alerts/index.ts | 1 +
.../missing_monitoring_data_alert.test.ts | 8 +
.../lib/alerts/fetch_ccr_read_exceptions.ts | 131 ++++++++
24 files changed, 825 insertions(+), 142 deletions(-)
create mode 100644 x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx
create mode 100644 x-pack/plugins/monitoring/server/alerts/ccr_read_exceptions_alert.ts
create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts
diff --git a/x-pack/plugins/monitoring/common/constants.ts b/x-pack/plugins/monitoring/common/constants.ts
index cf382701ca40f..c8b3ae0be631a 100644
--- a/x-pack/plugins/monitoring/common/constants.ts
+++ b/x-pack/plugins/monitoring/common/constants.ts
@@ -251,6 +251,7 @@ export const ALERT_MEMORY_USAGE = `${ALERT_PREFIX}alert_jvm_memory_usage`;
export const ALERT_MISSING_MONITORING_DATA = `${ALERT_PREFIX}alert_missing_monitoring_data`;
export const ALERT_THREAD_POOL_SEARCH_REJECTIONS = `${ALERT_PREFIX}alert_thread_pool_search_rejections`;
export const ALERT_THREAD_POOL_WRITE_REJECTIONS = `${ALERT_PREFIX}alert_thread_pool_write_rejections`;
+export const ALERT_CCR_READ_EXCEPTIONS = `${ALERT_PREFIX}ccr_read_exceptions`;
/**
* Legacy alerts details/label for server and public use
@@ -451,6 +452,25 @@ export const ALERT_DETAILS = {
'Alert when the number of rejections in the write thread pool exceeds the threshold.',
}),
},
+ [ALERT_CCR_READ_EXCEPTIONS]: {
+ paramDetails: {
+ duration: {
+ label: i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.paramDetails.duration.label',
+ {
+ defaultMessage: `In the last`,
+ }
+ ),
+ type: AlertParamType.Duration,
+ },
+ },
+ label: i18n.translate('xpack.monitoring.alerts.ccrReadExceptions.label', {
+ defaultMessage: 'CCR read exceptions',
+ }),
+ description: i18n.translate('xpack.monitoring.alerts.ccrReadExceptions.description', {
+ defaultMessage: 'Alert if any CCR read exceptions have been detected.',
+ }),
+ },
};
export const ALERT_PANEL_MENU = [
@@ -485,6 +505,7 @@ export const ALERT_PANEL_MENU = [
{ alertName: ALERT_LICENSE_EXPIRATION },
{ alertName: ALERT_THREAD_POOL_SEARCH_REJECTIONS },
{ alertName: ALERT_THREAD_POOL_WRITE_REJECTIONS },
+ { alertName: ALERT_CCR_READ_EXCEPTIONS },
],
},
];
@@ -505,6 +526,7 @@ export const ALERTS = [
ALERT_MISSING_MONITORING_DATA,
ALERT_THREAD_POOL_SEARCH_REJECTIONS,
ALERT_THREAD_POOL_WRITE_REJECTIONS,
+ ALERT_CCR_READ_EXCEPTIONS,
];
/**
diff --git a/x-pack/plugins/monitoring/common/types/alerts.ts b/x-pack/plugins/monitoring/common/types/alerts.ts
index 0f10e0e48962b..93807f9df12b0 100644
--- a/x-pack/plugins/monitoring/common/types/alerts.ts
+++ b/x-pack/plugins/monitoring/common/types/alerts.ts
@@ -22,6 +22,7 @@ export interface CommonAlertState {
export interface CommonAlertFilter {
nodeUuid?: string;
+ shardId?: string;
}
export interface CommonAlertParamDetail {
@@ -103,6 +104,7 @@ export interface AlertUiState {
export interface AlertMessage {
text: string; // Do this. #link this is a link #link
+ code?: string;
nextSteps?: AlertMessage[];
tokens?: AlertMessageToken[];
}
@@ -165,6 +167,20 @@ export interface AlertMemoryUsageNodeStats extends AlertNodeStats {
export interface AlertMissingData extends AlertNodeStats {
gapDuration: number;
}
+export interface CCRReadExceptionsStats {
+ remoteCluster: string;
+ followerIndex: string;
+ shardId: number;
+ leaderIndex: string;
+ lastReadException: { type: string; reason: string };
+ clusterUuid: string;
+ ccs: string;
+}
+
+export interface CCRReadExceptionsUIMeta extends CCRReadExceptionsStats {
+ instanceId: string;
+ itemLabel: string;
+}
export interface AlertData {
nodeName?: string;
diff --git a/x-pack/plugins/monitoring/public/alerts/callout.tsx b/x-pack/plugins/monitoring/public/alerts/callout.tsx
index d3feb148cf986..af2d8c3fef60a 100644
--- a/x-pack/plugins/monitoring/public/alerts/callout.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/callout.tsx
@@ -15,6 +15,7 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
+ EuiCodeBlock,
} from '@elastic/eui';
import { replaceTokens } from './lib/replace_tokens';
import { AlertMessage } from '../../common/types/alerts';
@@ -66,12 +67,24 @@ export const AlertsCallout: React.FC = (props: Props) => {
);
+ const { code } = status.state.state.ui.message;
const accordion = (
+ {code?.length ? (
+
+ {code}
+
+ ) : null}
= (props: Props) => {
paddingLeft: `0.5rem`,
}}
>
- {(status.state.state.ui.message.nextSteps || []).map((step: AlertMessage) => {
- return {}} label={replaceTokens(step)} />;
- })}
+ {(status.state.state.ui.message.nextSteps || []).map(
+ (step: AlertMessage, stepIndex: number) => {
+ return (
+ {}}
+ label={replaceTokens(step)}
+ key={index + stepIndex}
+ />
+ );
+ }
+ )}
}
+ label={ }
/>
diff --git a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx
new file mode 100644
index 0000000000000..2dafadf272608
--- /dev/null
+++ b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx
@@ -0,0 +1,49 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+import { Expression, Props } from '../components/duration/expression';
+import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public';
+import { ALERT_CCR_READ_EXCEPTIONS, ALERT_DETAILS } from '../../../common/constants';
+
+interface ValidateOptions {
+ duration: string;
+}
+
+const validate = (inputValues: ValidateOptions): ValidationResult => {
+ const validationResult = { errors: {} };
+ const errors: { [key: string]: string[] } = {
+ duration: [],
+ };
+ if (!inputValues.duration) {
+ errors.duration.push(
+ i18n.translate('xpack.monitoring.alerts.validation.duration', {
+ defaultMessage: 'A valid duration is required.',
+ })
+ );
+ }
+ validationResult.errors = errors;
+ return validationResult;
+};
+
+export function createCCRReadExceptionsAlertType(): AlertTypeModel {
+ return {
+ id: ALERT_CCR_READ_EXCEPTIONS,
+ name: ALERT_DETAILS[ALERT_CCR_READ_EXCEPTIONS].label,
+ description: ALERT_DETAILS[ALERT_CCR_READ_EXCEPTIONS].description,
+ iconClass: 'bell',
+ documentationUrl(docLinks) {
+ return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/kibana-alerts.html`;
+ },
+ alertParamsExpression: (props: Props) => (
+
+ ),
+ validate,
+ defaultActionMessage: '{{context.internalFullMessage}}',
+ requiresAppContext: true,
+ };
+}
diff --git a/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_category.tsx b/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_category.tsx
index 82a1a1f841a22..bbea32e4d2d04 100644
--- a/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_category.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_category.tsx
@@ -171,6 +171,7 @@ export function getAlertPanelsByCategory(
for (const { alert, states } of category.alerts) {
const items = [];
for (const alertState of states.filter(({ state }) => stateFilter(state))) {
+ const { nodeName, itemLabel } = alertState.state;
items.push({
name: (
@@ -188,7 +189,7 @@ export function getAlertPanelsByCategory(
)}
- {alertState.state.nodeName}
+ {nodeName || itemLabel}
),
panel: ++tertiaryPanelIndex,
diff --git a/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_node.tsx b/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_node.tsx
index c48706f4edcb9..735b9c3637cdd 100644
--- a/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_node.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/lib/get_alert_panels_by_node.tsx
@@ -69,10 +69,11 @@ export function getAlertPanelsByNode(
const states = (statesByNodes[nodeUuid] as CommonAlertState[]).filter(({ state }) =>
stateFilter(state)
);
+ const { nodeName, itemLabel } = states[0].state;
return {
name: (
- {states[0].state.nodeName} ({states.length})
+ {nodeName || itemLabel} ({states.length})
),
panel: index + 1,
@@ -86,7 +87,8 @@ export function getAlertPanelsByNode(
let title = '';
for (const { alert, states } of alertsForNode) {
for (const alertState of states) {
- title = alertState.state.nodeName;
+ const { nodeName, itemLabel } = alertState.state;
+ title = nodeName || itemLabel;
panelItems.push({
name: (
diff --git a/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx b/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx
index b8ac69cbae68a..0ddda96a1100d 100644
--- a/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx
@@ -77,6 +77,7 @@ export function replaceTokens(alertMessage: AlertMessage): JSX.Element | string
}
const url = linkToken.partialUrl
+ .replace('{basePath}', Legacy.shims.getBasePath())
.replace('{elasticWebsiteUrl}', Legacy.shims.docLinks.ELASTIC_WEBSITE_URL)
.replace('{docLinkVersion}', Legacy.shims.docLinks.DOC_LINK_VERSION);
const index = text.indexOf(linkPart[0]);
diff --git a/x-pack/plugins/monitoring/public/alerts/panel.tsx b/x-pack/plugins/monitoring/public/alerts/panel.tsx
index 139010a3d2446..2d319a81dd063 100644
--- a/x-pack/plugins/monitoring/public/alerts/panel.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/panel.tsx
@@ -10,6 +10,7 @@ import {
EuiHorizontalRule,
EuiListGroup,
EuiListGroupItem,
+ EuiCodeBlock,
} from '@elastic/eui';
import { CommonAlert, CommonAlertState, AlertMessage } from '../../common/types/alerts';
@@ -47,12 +48,24 @@ export const AlertPanel: React.FC = (props: Props) => {
) : null;
+ const { code } = alertState.state.ui.message;
return (
{replaceTokens(alertState.state.ui.message)}
+ {code?.length ? (
+
+ {code}
+
+ ) : null}
{nextStepsUi ? : null}
{nextStepsUi}
diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js
index ded309ce64e2e..8849fb05fcf3c 100644
--- a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js
+++ b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js
@@ -47,6 +47,7 @@ import {
ALERT_NODES_CHANGED,
ALERT_ELASTICSEARCH_VERSION_MISMATCH,
ALERT_MISSING_MONITORING_DATA,
+ ALERT_CCR_READ_EXCEPTIONS,
} from '../../../../common/constants';
import { AlertsBadge } from '../../../alerts/badge';
import { shouldShowAlertBadge } from '../../../alerts/lib/should_show_alert_badge';
@@ -159,7 +160,11 @@ function renderLog(log) {
);
}
-const OVERVIEW_PANEL_ALERTS = [ALERT_CLUSTER_HEALTH, ALERT_LICENSE_EXPIRATION];
+const OVERVIEW_PANEL_ALERTS = [
+ ALERT_CLUSTER_HEALTH,
+ ALERT_LICENSE_EXPIRATION,
+ ALERT_CCR_READ_EXCEPTIONS,
+];
const NODES_PANEL_ALERTS = [
ALERT_CPU_USAGE,
diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap
index d54612b6f4f29..794982a0b6193 100644
--- a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap
+++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap
@@ -29,6 +29,12 @@ exports[`Ccr that it renders normally 1`] = `
"name": "Follows",
"sortable": true,
},
+ Object {
+ "field": "alerts",
+ "name": "Alerts",
+ "render": [Function],
+ "sortable": true,
+ },
Object {
"field": "syncLagOps",
"name": "Sync Lag (ops)",
diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js
index ab26b6a9cc0bb..8b7c386a4dcc6 100644
--- a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js
+++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { Fragment, Component } from 'react';
+import React, { Fragment, useState } from 'react';
import {
EuiInMemoryTable,
EuiLink,
@@ -20,27 +20,20 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link';
+import { AlertsStatus } from '../../../alerts/status';
import './ccr.scss';
function toSeconds(ms) {
return Math.floor(ms / 1000) + 's';
}
-export class Ccr extends Component {
- constructor(props) {
- super(props);
- this.state = {
- itemIdToExpandedRowMap: {},
- };
- }
-
- toggleShards(index, shards) {
- const itemIdToExpandedRowMap = {
- ...this.state.itemIdToExpandedRowMap,
- };
+export const Ccr = (props) => {
+ const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState({});
+ const toggleShards = (index, shards) => {
+ const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
- if (itemIdToExpandedRowMap[index]) {
- delete itemIdToExpandedRowMap[index];
+ if (itemIdToExpandedRowMapValues[index]) {
+ delete itemIdToExpandedRowMapValues[index];
} else {
let pagination = {
initialPageSize: 5,
@@ -51,7 +44,7 @@ export class Ccr extends Component {
pagination = false;
}
- itemIdToExpandedRowMap[index] = (
+ itemIdToExpandedRowMapValues[index] = (
null,
},
+ {
+ field: 'alerts',
+ sortable: true,
+ name: i18n.translate(
+ 'xpack.monitoring.elasticsearch.ccr.shardsTable.alertsColumnTitle',
+ {
+ defaultMessage: 'Alerts',
+ }
+ ),
+ render: (_field, item) => {
+ return (
+ state.meta.shardId === item.shardId}
+ />
+ );
+ },
+ },
{
field: 'syncLagOps',
name: i18n.translate(
@@ -157,11 +169,11 @@ export class Ccr extends Component {
/>
);
}
- this.setState({ itemIdToExpandedRowMap });
- }
+ setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
+ };
- renderTable() {
- const { data } = this.props;
+ const renderTable = () => {
+ const { data, alerts } = props;
const items = data;
let pagination = {
@@ -194,9 +206,9 @@ export class Ccr extends Component {
),
sortable: true,
render: (index, { shards }) => {
- const expanded = !!this.state.itemIdToExpandedRowMap[index];
+ const expanded = !!itemIdToExpandedRowMap[index];
return (
- this.toggleShards(index, shards)}>
+ toggleShards(index, shards)}>
{index}
{expanded ? : }
@@ -214,6 +226,25 @@ export class Ccr extends Component {
}
),
},
+ {
+ field: 'alerts',
+ sortable: true,
+ name: i18n.translate(
+ 'xpack.monitoring.elasticsearch.ccr.ccrListingTable.alertsColumnTitle',
+ {
+ defaultMessage: 'Alerts',
+ }
+ ),
+ render: (_field, item) => {
+ return (
+ state.meta.followerIndex === item.index}
+ />
+ );
+ },
+ },
{
field: 'syncLagOps',
sortable: true,
@@ -264,28 +295,26 @@ export class Ccr extends Component {
}}
sorting={sorting}
itemId="id"
- itemIdToExpandedRowMap={this.state.itemIdToExpandedRowMap}
+ itemIdToExpandedRowMap={itemIdToExpandedRowMap}
/>
);
- }
+ };
- render() {
- return (
-
-
-
-
-
-
-
-
- {this.renderTable()}
-
-
-
- );
- }
-}
+ return (
+
+
+
+
+
+
+
+
+ {renderTable()}
+
+
+
+ );
+};
diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap
index e35d2ba6108f5..81398c1d8e836 100644
--- a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap
+++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap
@@ -2,50 +2,46 @@
exports[`CcrShard that is renders an exception properly 1`] = `
-
-
-
-
-
-
-
-
-
`;
@@ -59,44 +55,50 @@ exports[`CcrShard that it renders normally 1`] = `
}
>
-
+
+
+
+
-
-
+
+
+
+
+
+
{this.renderErrors()}
{this.renderCharts()}
diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js
index 52de0659ed527..657301d6e1cb3 100644
--- a/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js
+++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js
@@ -8,8 +8,9 @@ import React from 'react';
import { SummaryStatus } from '../../summary_status';
import { formatMetric } from '../../../lib/format_number';
import { i18n } from '@kbn/i18n';
+import { AlertsStatus } from '../../../alerts/status';
-export function Status({ stat, formattedLeader, oldestStat }) {
+export function Status({ stat, formattedLeader, oldestStat, alerts = {} }) {
const {
follower_index: followerIndex,
shard_id: shardId,
@@ -23,6 +24,12 @@ export function Status({ stat, formattedLeader, oldestStat }) {
} = oldestStat;
const metrics = [
+ {
+ label: i18n.translate('xpack.monitoring.elasticsearch.ccrShard.status.alerts', {
+ defaultMessage: 'Alerts',
+ }),
+ value: ,
+ },
{
label: i18n.translate('xpack.monitoring.elasticsearch.ccrShard.status.followerIndexLabel', {
defaultMessage: 'Follower Index',
diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts
index 0439b47569e72..a0de3a7663a12 100644
--- a/x-pack/plugins/monitoring/public/plugin.ts
+++ b/x-pack/plugins/monitoring/public/plugin.ts
@@ -156,6 +156,7 @@ export class MonitoringPlugin
'./alerts/thread_pool_rejections_alert'
);
const { createMemoryUsageAlertType } = await import('./alerts/memory_usage_alert');
+ const { createCCRReadExceptionsAlertType } = await import('./alerts/ccr_read_exceptions_alert');
const {
triggersActionsUi: { alertTypeRegistry },
@@ -176,6 +177,7 @@ export class MonitoringPlugin
ALERT_DETAILS[ALERT_THREAD_POOL_WRITE_REJECTIONS]
)
);
+ alertTypeRegistry.register(createCCRReadExceptionsAlertType());
const legacyAlertTypes = createLegacyAlertTypes();
for (const legacyAlertType of legacyAlertTypes) {
alertTypeRegistry.register(legacyAlertType);
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js
index 6569340785736..9e26d453d76a3 100644
--- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js
+++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js
@@ -12,7 +12,13 @@ import { routeInitProvider } from '../../../lib/route_init';
import template from './index.html';
import { Ccr } from '../../../components/elasticsearch/ccr';
import { MonitoringViewBaseController } from '../../base_controller';
-import { CODE_PATH_ELASTICSEARCH } from '../../../../common/constants';
+import {
+ CODE_PATH_ELASTICSEARCH,
+ ALERT_CCR_READ_EXCEPTIONS,
+ ELASTICSEARCH_SYSTEM_ID,
+} from '../../../../common/constants';
+import { SetupModeRenderer } from '../../../components/renderers';
+import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context';
uiRoutes.when('/elasticsearch/ccr', {
template,
@@ -37,6 +43,12 @@ uiRoutes.when('/elasticsearch/ccr', {
getPageData,
$scope,
$injector,
+ alerts: {
+ shouldFetch: true,
+ options: {
+ alertTypeIds: [ALERT_CCR_READ_EXCEPTIONS],
+ },
+ },
});
$scope.$watch(
@@ -45,7 +57,20 @@ uiRoutes.when('/elasticsearch/ccr', {
if (!data) {
return;
}
- this.renderReact( );
+ this.renderReact(
+ (
+
+ {flyoutComponent}
+
+ {bottomBarComponent}
+
+ )}
+ />
+ );
}
);
}
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js
index 33a2d27f39856..6c1c4218568e3 100644
--- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js
+++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js
@@ -13,7 +13,13 @@ import { routeInitProvider } from '../../../../lib/route_init';
import template from './index.html';
import { MonitoringViewBaseController } from '../../../base_controller';
import { CcrShard } from '../../../../components/elasticsearch/ccr_shard';
-import { CODE_PATH_ELASTICSEARCH } from '../../../../../common/constants';
+import {
+ CODE_PATH_ELASTICSEARCH,
+ ALERT_CCR_READ_EXCEPTIONS,
+ ELASTICSEARCH_SYSTEM_ID,
+} from '../../../../../common/constants';
+import { SetupModeRenderer } from '../../../../components/renderers';
+import { SetupModeContext } from '../../../../components/setup_mode/setup_mode_context';
uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', {
template,
@@ -27,6 +33,7 @@ uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', {
controllerAs: 'elasticsearchCcr',
controller: class ElasticsearchCcrController extends MonitoringViewBaseController {
constructor($injector, $scope, pageData) {
+ const $route = $injector.get('$route');
super({
title: i18n.translate('xpack.monitoring.elasticsearch.ccr.shard.routeTitle', {
defaultMessage: 'Elasticsearch - Ccr - Shard',
@@ -35,6 +42,17 @@ uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', {
getPageData,
$scope,
$injector,
+ alerts: {
+ shouldFetch: true,
+ options: {
+ alertTypeIds: [ALERT_CCR_READ_EXCEPTIONS],
+ filters: [
+ {
+ shardId: $route.current.pathParams.shardId,
+ },
+ ],
+ },
+ },
});
$scope.instance = i18n.translate('xpack.monitoring.elasticsearch.ccr.shard.instanceTitle', {
@@ -62,7 +80,20 @@ uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', {
})
);
- this.renderReact( );
+ this.renderReact(
+ (
+
+ {flyoutComponent}
+
+ {bottomBarComponent}
+
+ )}
+ />
+ );
}
);
}
diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts
index b43a56562a2aa..64b7148d87d9e 100644
--- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts
+++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts
@@ -5,6 +5,7 @@
*/
import {
+ CCRReadExceptionsAlert,
CpuUsageAlert,
MissingMonitoringDataAlert,
DiskUsageAlert,
@@ -32,6 +33,7 @@ import {
ALERT_LOGSTASH_VERSION_MISMATCH,
ALERT_KIBANA_VERSION_MISMATCH,
ALERT_ELASTICSEARCH_VERSION_MISMATCH,
+ ALERT_CCR_READ_EXCEPTIONS,
} from '../../common/constants';
import { AlertsClient } from '../../../alerts/server';
import { Alert } from '../../../alerts/common';
@@ -49,6 +51,7 @@ const BY_TYPE = {
[ALERT_LOGSTASH_VERSION_MISMATCH]: LogstashVersionMismatchAlert,
[ALERT_KIBANA_VERSION_MISMATCH]: KibanaVersionMismatchAlert,
[ALERT_ELASTICSEARCH_VERSION_MISMATCH]: ElasticsearchVersionMismatchAlert,
+ [ALERT_CCR_READ_EXCEPTIONS]: CCRReadExceptionsAlert,
};
export class AlertsFactory {
@@ -68,7 +71,6 @@ export class AlertsFactory {
if (!alertClientAlerts.total || !alertClientAlerts.data?.length) {
return;
- // return new alertCls() as BaseAlert;
}
const [rawAlert] = alertClientAlerts.data as [Alert];
diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts
index ebff72a255777..a3bcc310b8084 100644
--- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts
+++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts
@@ -345,7 +345,7 @@ export class BaseAlert {
const firingNodeUuids = nodes
.filter((node) => node.shouldFire)
- .map((node) => node.meta.nodeId)
+ .map((node) => node.meta.nodeId || node.meta.instanceId)
.join(',');
const instanceId = `${this.alertOptions.id}:${cluster.clusterUuid}:${firingNodeUuids}`;
const instance = services.alertInstanceFactory(instanceId);
@@ -355,13 +355,16 @@ export class BaseAlert {
if (!node.shouldFire) {
continue;
}
- const stat = node.meta as AlertNodeState;
+ const { meta } = node;
const nodeState = this.getDefaultAlertState(cluster, node) as AlertNodeState;
if (key) {
- nodeState[key] = stat[key];
+ nodeState[key] = meta[key];
}
- nodeState.nodeId = stat.nodeId || node.nodeId!;
- nodeState.nodeName = stat.nodeName || node.nodeName || nodeState.nodeId;
+ nodeState.nodeId = meta.nodeId || node.nodeId! || meta.instanceId;
+ // TODO: make these functions more generic, so it's node/item agnostic
+ nodeState.nodeName = meta.itemLabel || meta.nodeName || node.nodeName || nodeState.nodeId;
+ nodeState.itemLabel = meta.itemLabel;
+ nodeState.meta = meta;
nodeState.ui.triggeredMS = currentUTC;
nodeState.ui.isFiring = true;
nodeState.ui.severity = node.severity;
diff --git a/x-pack/plugins/monitoring/server/alerts/ccr_read_exceptions_alert.ts b/x-pack/plugins/monitoring/server/alerts/ccr_read_exceptions_alert.ts
new file mode 100644
index 0000000000000..6034f32a8c659
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/alerts/ccr_read_exceptions_alert.ts
@@ -0,0 +1,289 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { BaseAlert } from './base_alert';
+import {
+ AlertData,
+ AlertCluster,
+ AlertState,
+ AlertMessage,
+ CCRReadExceptionsUIMeta,
+ AlertMessageTimeToken,
+ AlertMessageLinkToken,
+ AlertInstanceState,
+ CommonAlertParams,
+ CommonAlertFilter,
+ CCRReadExceptionsStats,
+} from '../../common/types/alerts';
+import { AlertInstance } from '../../../alerts/server';
+import {
+ INDEX_PATTERN_ELASTICSEARCH,
+ ALERT_CCR_READ_EXCEPTIONS,
+ ALERT_DETAILS,
+} from '../../common/constants';
+import { fetchCCRReadExceptions } from '../lib/alerts/fetch_ccr_read_exceptions';
+import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern';
+import { AlertMessageTokenType, AlertSeverity } from '../../common/enums';
+import { parseDuration } from '../../../alerts/common/parse_duration';
+import { SanitizedAlert, RawAlertInstance } from '../../../alerts/common';
+import { AlertingDefaults, createLink } from './alert_helpers';
+import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index';
+import { Globals } from '../static_globals';
+
+export class CCRReadExceptionsAlert extends BaseAlert {
+ constructor(public rawAlert?: SanitizedAlert) {
+ super(rawAlert, {
+ id: ALERT_CCR_READ_EXCEPTIONS,
+ name: ALERT_DETAILS[ALERT_CCR_READ_EXCEPTIONS].label,
+ throttle: '6h',
+ defaultParams: {
+ duration: '1h',
+ },
+ actionVariables: [
+ {
+ name: 'remoteClusters',
+ description: i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.actionVariables.remoteClusters',
+ {
+ defaultMessage: 'List of remote clusters that are experiencing CCR read exceptions.',
+ }
+ ),
+ },
+ {
+ name: 'followerIndices',
+ description: i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.actionVariables.followerIndices',
+ {
+ defaultMessage: 'List of follower indices reporting CCR read exceptions.',
+ }
+ ),
+ },
+ ...Object.values(AlertingDefaults.ALERT_TYPE.context),
+ ],
+ });
+ }
+
+ protected async fetchData(
+ params: CommonAlertParams,
+ callCluster: any,
+ clusters: AlertCluster[],
+ availableCcs: string[]
+ ): Promise {
+ let esIndexPattern = appendMetricbeatIndex(Globals.app.config, INDEX_PATTERN_ELASTICSEARCH);
+ if (availableCcs) {
+ esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs);
+ }
+ const { duration: durationString } = params;
+ const duration = parseDuration(durationString);
+ const endMs = +new Date();
+ const startMs = endMs - duration;
+ const stats = await fetchCCRReadExceptions(
+ callCluster,
+ esIndexPattern,
+ startMs,
+ endMs,
+ Globals.app.config.ui.max_bucket_size
+ );
+
+ return stats.map((stat) => {
+ const {
+ remoteCluster,
+ followerIndex,
+ shardId,
+ leaderIndex,
+ lastReadException,
+ clusterUuid,
+ ccs,
+ } = stat;
+ return {
+ shouldFire: true,
+ severity: AlertSeverity.Danger,
+ meta: {
+ remoteCluster,
+ followerIndex,
+ shardId,
+ leaderIndex,
+ lastReadException,
+ instanceId: `${remoteCluster}:${followerIndex}`,
+ itemLabel: followerIndex,
+ },
+ clusterUuid,
+ ccs,
+ };
+ });
+ }
+
+ protected getUiMessage(alertState: AlertState, item: AlertData): AlertMessage {
+ const {
+ remoteCluster,
+ followerIndex,
+ shardId,
+ lastReadException,
+ } = item.meta as CCRReadExceptionsUIMeta;
+ return {
+ text: i18n.translate('xpack.monitoring.alerts.ccrReadExceptions.ui.firingMessage', {
+ defaultMessage: `Follower index #start_link{followerIndex}#end_link is reporting CCR read exceptions on remote cluster: {remoteCluster} at #absolute`,
+ values: {
+ remoteCluster,
+ followerIndex,
+ },
+ }),
+ code: JSON.stringify(lastReadException, null, 2),
+ nextSteps: [
+ createLink(
+ i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.ui.nextSteps.identifyCCRStats',
+ {
+ defaultMessage: '#start_linkIdentify CCR usage/stats#end_link',
+ }
+ ),
+ 'elasticsearch/ccr',
+ AlertMessageTokenType.Link
+ ),
+ createLink(
+ i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.ui.nextSteps.stackManagmentFollow',
+ {
+ defaultMessage: '#start_linkManage CCR follower indices#end_link',
+ }
+ ),
+ `{basePath}management/data/cross_cluster_replication/follower_indices`
+ ),
+ createLink(
+ i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.ui.nextSteps.stackManagmentAutoFollow',
+ {
+ defaultMessage: '#start_linkCreate auto-follow patterns#end_link',
+ }
+ ),
+ `{basePath}management/data/cross_cluster_replication/auto_follow_patterns`
+ ),
+ createLink(
+ i18n.translate('xpack.monitoring.alerts.ccrReadExceptions.ui.nextSteps.followerAPIDoc', {
+ defaultMessage: '#start_linkAdd follower index API (Docs)#end_link',
+ }),
+ `{elasticWebsiteUrl}guide/en/elasticsearch/reference/{docLinkVersion}/ccr-put-follow.html`
+ ),
+ createLink(
+ i18n.translate('xpack.monitoring.alerts.ccrReadExceptions.ui.nextSteps.ccrDocs', {
+ defaultMessage: '#start_linkCross-cluster replication (Docs)#end_link',
+ }),
+ `{elasticWebsiteUrl}guide/en/elasticsearch/reference/{docLinkVersion}/xpack-ccr.html`
+ ),
+ createLink(
+ i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.ui.nextSteps.biDirectionalReplication',
+ {
+ defaultMessage: '#start_linkBi-directional replication (Blog)#end_link',
+ }
+ ),
+ `{elasticWebsiteUrl}blog/bi-directional-replication-with-elasticsearch-cross-cluster-replication-ccr`
+ ),
+ createLink(
+ i18n.translate('xpack.monitoring.alerts.ccrReadExceptions.ui.nextSteps.followTheLeader', {
+ defaultMessage: '#start_linkFollow the Leader (Blog)#end_link',
+ }),
+ `{elasticWebsiteUrl}blog/follow-the-leader-an-introduction-to-cross-cluster-replication-in-elasticsearch`
+ ),
+ ],
+ tokens: [
+ {
+ startToken: '#absolute',
+ type: AlertMessageTokenType.Time,
+ isAbsolute: true,
+ isRelative: false,
+ timestamp: alertState.ui.triggeredMS,
+ } as AlertMessageTimeToken,
+ {
+ startToken: '#start_link',
+ endToken: '#end_link',
+ type: AlertMessageTokenType.Link,
+ url: `elasticsearch/ccr/${followerIndex}/shard/${shardId}`,
+ } as AlertMessageLinkToken,
+ ],
+ };
+ }
+
+ protected filterAlertInstance(alertInstance: RawAlertInstance, filters: CommonAlertFilter[]) {
+ const alertInstanceStates = alertInstance.state?.alertStates as AlertState[];
+ const alertFilter = filters?.find((filter) => filter.shardId);
+ if (!filters || !filters.length || !alertInstanceStates?.length || !alertFilter?.shardId) {
+ return alertInstance;
+ }
+ const shardIdInt = parseInt(alertFilter.shardId!, 10);
+ const alertStates = alertInstanceStates.filter(
+ ({ meta }) => (meta as CCRReadExceptionsStats).shardId === shardIdInt
+ );
+ return { state: { alertStates } };
+ }
+
+ protected executeActions(
+ instance: AlertInstance,
+ { alertStates }: AlertInstanceState,
+ item: AlertData | null,
+ cluster: AlertCluster
+ ) {
+ const remoteClustersList = alertStates
+ .map((alertState) => (alertState.meta as CCRReadExceptionsUIMeta).remoteCluster)
+ .join(', ');
+ const followerIndicesList = alertStates
+ .map((alertState) => (alertState.meta as CCRReadExceptionsUIMeta).followerIndex)
+ .join(', ');
+
+ const shortActionText = i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.shortAction',
+ {
+ defaultMessage:
+ 'Verify follower and leader index relationships across the affected remote clusters.',
+ }
+ );
+ const fullActionText = i18n.translate('xpack.monitoring.alerts.ccrReadExceptions.fullAction', {
+ defaultMessage: 'View CCR stats',
+ });
+
+ const ccs = alertStates.find((state) => state.ccs)?.ccs;
+ const globalStateLink = this.createGlobalStateLink(
+ 'elasticsearch/ccr',
+ cluster.clusterUuid,
+ ccs
+ );
+
+ const action = `[${fullActionText}](${globalStateLink})`;
+ const internalShortMessage = i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.firing.internalShortMessage',
+ {
+ defaultMessage: `CCR read exceptions alert is firing for the following remote clusters: {remoteClustersList}. {shortActionText}`,
+ values: {
+ remoteClustersList,
+ shortActionText,
+ },
+ }
+ );
+ const internalFullMessage = i18n.translate(
+ 'xpack.monitoring.alerts.ccrReadExceptions.firing.internalFullMessage',
+ {
+ defaultMessage: `CCR read exceptions alert is firing for the following remote clusters: {remoteClustersList}. Current 'follower_index' indices are affected: {followerIndicesList}. {action}`,
+ values: {
+ action,
+ remoteClustersList,
+ followerIndicesList,
+ },
+ }
+ );
+
+ instance.scheduleActions('default', {
+ internalShortMessage,
+ internalFullMessage,
+ state: AlertingDefaults.ALERT_STATE.firing,
+ remoteClusters: remoteClustersList,
+ followerIndices: followerIndicesList,
+ clusterName: cluster.clusterName,
+ action,
+ actionPlain: shortActionText,
+ });
+ }
+}
diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts
index 63195621fb9c8..4622f73b9feb0 100644
--- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts
+++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts
@@ -125,6 +125,13 @@ describe('CpuUsageAlert', () => {
ccs: undefined,
cluster: { clusterUuid, clusterName },
cpuUsage,
+ itemLabel: undefined,
+ meta: {
+ clusterUuid,
+ cpuUsage,
+ nodeId,
+ nodeName,
+ },
nodeId,
nodeName,
ui: {
diff --git a/x-pack/plugins/monitoring/server/alerts/index.ts b/x-pack/plugins/monitoring/server/alerts/index.ts
index 5fa718dfb34cd..b58476a01dc14 100644
--- a/x-pack/plugins/monitoring/server/alerts/index.ts
+++ b/x-pack/plugins/monitoring/server/alerts/index.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+export { CCRReadExceptionsAlert } from './ccr_read_exceptions_alert';
export { BaseAlert } from './base_alert';
export { CpuUsageAlert } from './cpu_usage_alert';
export { MissingMonitoringDataAlert } from './missing_monitoring_data_alert';
diff --git a/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts
index 6ba4333309f00..65205738f82c3 100644
--- a/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts
+++ b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts
@@ -131,6 +131,14 @@ describe('MissingMonitoringDataAlert', () => {
nodeId,
nodeName,
gapDuration,
+ itemLabel: undefined,
+ meta: {
+ clusterUuid,
+ gapDuration,
+ limit: 86400000,
+ nodeId,
+ nodeName,
+ },
ui: {
isFiring: true,
message: {
diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts
new file mode 100644
index 0000000000000..c8933a7cd14a9
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts
@@ -0,0 +1,131 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { get } from 'lodash';
+import { CCRReadExceptionsStats } from '../../../common/types/alerts';
+
+export async function fetchCCRReadExceptions(
+ callCluster: any,
+ index: string,
+ startMs: number,
+ endMs: number,
+ size: number
+): Promise {
+ const params = {
+ index,
+ filterPath: ['aggregations.remote_clusters.buckets'],
+ body: {
+ size: 0,
+ query: {
+ bool: {
+ filter: [
+ {
+ nested: {
+ path: 'ccr_stats.read_exceptions',
+ query: {
+ exists: {
+ field: 'ccr_stats.read_exceptions.exception',
+ },
+ },
+ },
+ },
+ {
+ term: {
+ type: 'ccr_stats',
+ },
+ },
+ {
+ range: {
+ timestamp: {
+ format: 'epoch_millis',
+ gte: startMs,
+ lte: endMs,
+ },
+ },
+ },
+ ],
+ },
+ },
+ aggs: {
+ remote_clusters: {
+ terms: {
+ field: 'ccr_stats.remote_cluster',
+ size,
+ },
+ aggs: {
+ follower_indices: {
+ terms: {
+ field: 'ccr_stats.follower_index',
+ size,
+ },
+ aggs: {
+ hits: {
+ top_hits: {
+ sort: [
+ {
+ timestamp: {
+ order: 'desc',
+ unmapped_type: 'long',
+ },
+ },
+ ],
+ _source: {
+ includes: [
+ 'cluster_uuid',
+ 'ccr_stats.read_exceptions',
+ 'ccr_stats.shard_id',
+ 'ccr_stats.leader_index',
+ ],
+ },
+ size: 1,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ const response = await callCluster('search', params);
+ const stats: CCRReadExceptionsStats[] = [];
+ const { buckets: remoteClusterBuckets = [] } = response.aggregations.remote_clusters;
+
+ if (!remoteClusterBuckets.length) {
+ return stats;
+ }
+
+ for (const remoteClusterBucket of remoteClusterBuckets) {
+ const followerIndicesBuckets = remoteClusterBucket.follower_indices.buckets;
+ const remoteCluster = remoteClusterBucket.key;
+
+ for (const followerIndexBucket of followerIndicesBuckets) {
+ const followerIndex = followerIndexBucket.key;
+ const {
+ _index: monitoringIndexName,
+ _source: { ccr_stats: ccrStats, cluster_uuid: clusterUuid },
+ } = get(followerIndexBucket, 'hits.hits.hits[0]');
+ const {
+ read_exceptions: readExceptions,
+ leader_index: leaderIndex,
+ shard_id: shardId,
+ } = ccrStats;
+ const { exception: lastReadException } = readExceptions[readExceptions.length - 1];
+
+ stats.push({
+ clusterUuid,
+ remoteCluster,
+ followerIndex,
+ shardId,
+ leaderIndex,
+ lastReadException,
+ ccs: monitoringIndexName.includes(':') ? monitoringIndexName.split(':')[0] : null,
+ });
+ }
+ }
+ return stats;
+}
From f7ace5e16defed0692424a22faa3b703905f3836 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yulia=20=C4=8Cech?=
<6585477+yuliacech@users.noreply.github.com>
Date: Sat, 19 Dec 2020 01:53:09 +0100
Subject: [PATCH 23/40] [Rollup Jobs] Added autofocus to cron editor (#86324)
---
.../public/components/cron_editor/cron_editor.tsx | 2 ++
.../crud_app/sections/job_create/steps/step_logistics.js | 5 +++++
2 files changed, 7 insertions(+)
diff --git a/src/plugins/es_ui_shared/public/components/cron_editor/cron_editor.tsx b/src/plugins/es_ui_shared/public/components/cron_editor/cron_editor.tsx
index 72e2f51c37e4c..19af93b67aca0 100644
--- a/src/plugins/es_ui_shared/public/components/cron_editor/cron_editor.tsx
+++ b/src/plugins/es_ui_shared/public/components/cron_editor/cron_editor.tsx
@@ -67,6 +67,7 @@ interface Props {
fieldToPreferredValueMap: FieldToValueMap;
frequency: Frequency;
}) => void;
+ autoFocus?: boolean;
}
type State = FieldToValueMap;
@@ -234,6 +235,7 @@ export class CronEditor extends Component {
fullWidth
>
) =>
diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_create/steps/step_logistics.js b/x-pack/plugins/rollup/public/crud_app/sections/job_create/steps/step_logistics.js
index 5e9c2f62ceef8..bb217fbeed304 100644
--- a/x-pack/plugins/rollup/public/crud_app/sections/job_create/steps/step_logistics.js
+++ b/x-pack/plugins/rollup/public/crud_app/sections/job_create/steps/step_logistics.js
@@ -45,8 +45,10 @@ export class StepLogistics extends Component {
hasMatchingIndices: PropTypes.bool.isRequired,
indexPatternAsyncErrors: PropTypes.array,
};
+ state = { cronFocus: false };
showAdvancedCron = () => {
+ this.setState({ cronFocus: true });
const { onFieldsChange } = this.props;
onFieldsChange({
@@ -55,6 +57,7 @@ export class StepLogistics extends Component {
};
hideAdvancedCron = () => {
+ this.setState({ cronFocus: true });
const { onFieldsChange, fields } = this.props;
const { simpleRollupCron } = fields;
@@ -156,6 +159,7 @@ export class StepLogistics extends Component {
fullWidth
>
onFieldsChange({ rollupCron: e.target.value })}
isInvalid={Boolean(areStepErrorsVisible && errorRollupCron)}
@@ -181,6 +185,7 @@ export class StepLogistics extends Component {
return (
Date: Fri, 18 Dec 2020 19:53:41 -0500
Subject: [PATCH 24/40] [Maps] Use Json for mvt-tests (#86492)
---
package.json | 2 +
.../server/mvt/__tests__/pbf/0_0_0_docs.pbf | Bin 155 -> 0 bytes
.../mvt/__tests__/pbf/0_0_0_grid_asgrid.pbf | Bin 91 -> 0 bytes
.../mvt/__tests__/pbf/0_0_0_grid_aspoint.pbf | Bin 83 -> 0 bytes
.../plugins/maps/server/mvt/get_tile.test.ts | 135 +++++++++++++++---
yarn.lock | 4 +-
6 files changed, 122 insertions(+), 19 deletions(-)
delete mode 100644 x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_docs.pbf
delete mode 100644 x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_asgrid.pbf
delete mode 100644 x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_aspoint.pbf
diff --git a/package.json b/package.json
index b4b3cbe22b715..8e99b5c693cec 100644
--- a/package.json
+++ b/package.json
@@ -381,6 +381,7 @@
"@mapbox/geojson-rewind": "^0.5.0",
"@mapbox/mapbox-gl-draw": "^1.2.0",
"@mapbox/mapbox-gl-rtl-text": "^0.2.3",
+ "@mapbox/vector-tile": "1.3.1",
"@microsoft/api-documenter": "7.7.2",
"@microsoft/api-extractor": "7.7.0",
"@octokit/rest": "^16.35.0",
@@ -750,6 +751,7 @@
"ora": "^4.0.4",
"p-limit": "^3.0.1",
"parse-link-header": "^1.0.1",
+ "pbf": "3.2.1",
"pirates": "^4.0.1",
"pixelmatch": "^5.1.0",
"pkg-up": "^2.0.0",
diff --git a/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_docs.pbf b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_docs.pbf
deleted file mode 100644
index 9a9296e2ece3f94ac726f6a83f2958ba2e22074b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 155
zcmb1|!C1k>#Z#PLT9lj`pOaXbTBOmSAfzP3#=yYH$iyVUtR%)cfww_Yse%1Hdjp%m
z1GW`x?>R5<@Jq49XXd4(R!A|&XQoIA$H!+U<;BORr6!h?7Nr7(;^URrxL6AEb1Id@
lxJ2B|1A=@b0-e$;E2DHXOfw@hld_a#xuikzR@fx13;_42EcXBa
diff --git a/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_asgrid.pbf b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_asgrid.pbf
deleted file mode 100644
index f2289865b80229bd3c96d44cfba54ef8aab2d16b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 91
zcmb1&tYG5eDb6n~N=}W>NvupQ(r8c+5*K1&U|?jFU{>PgY!GgckYaGKXJD}Bm*Pyx
uPmWK{FU>2F;!i9~kIzqw5AhFi^oe&2Q)1ERJY
diff --git a/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_aspoint.pbf b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_aspoint.pbf
deleted file mode 100644
index 54b0791ccd1361b7d26b2c689235c433e7f1c378..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 83
zcmb0NtYG5eDb6n~N=}W>NvupQ(r8c+;um6JU|?jFU{qq|d?VB*A;p=JpB$f@Uz%4U
m#h+M~9-p5UAL1Y4=o9Z4ro^JrdzMLwQ*x{OUx}m{A&vm0mKV|h
diff --git a/x-pack/plugins/maps/server/mvt/get_tile.test.ts b/x-pack/plugins/maps/server/mvt/get_tile.test.ts
index 1e00fd27e3d1b..3660039f2513c 100644
--- a/x-pack/plugins/maps/server/mvt/get_tile.test.ts
+++ b/x-pack/plugins/maps/server/mvt/get_tile.test.ts
@@ -7,9 +7,26 @@
import { getGridTile, getTile } from './get_tile';
import { TILE_GRIDAGGS, TILE_SEARCHES } from './__tests__/tile_es_responses';
import { Logger } from 'src/core/server';
-import * as path from 'path';
-import * as fs from 'fs';
-import { ES_GEO_FIELD_TYPE, RENDER_AS } from '../../common/constants';
+import { ES_GEO_FIELD_TYPE, MVT_SOURCE_LAYER_NAME, RENDER_AS } from '../../common/constants';
+
+// @ts-expect-error
+import { VectorTile, VectorTileLayer } from '@mapbox/vector-tile';
+// @ts-expect-error
+import Protobuf from 'pbf';
+
+interface ITileLayerJsonExpectation {
+ version: number;
+ name: string;
+ extent: number;
+ length: number;
+ features: Array<{
+ id: string | number | undefined;
+ type: number;
+ properties: object;
+ extent: number;
+ pointArrays: object;
+ }>;
+}
describe('getTile', () => {
const mockCallElasticsearch = jest.fn();
@@ -39,7 +56,7 @@ describe('getTile', () => {
}
});
- const tile = await getTile({
+ const pbfTile = await getTile({
x: 0,
y: 0,
z: 0,
@@ -53,7 +70,35 @@ describe('getTile', () => {
geoFieldType: ES_GEO_FIELD_TYPE.GEO_SHAPE,
});
- compareTiles('./__tests__/pbf/0_0_0_docs.pbf', tile);
+ const jsonTile = new VectorTile(new Protobuf(pbfTile));
+ compareJsonTiles(jsonTile, {
+ version: 2,
+ name: 'source_layer',
+ extent: 4096,
+ length: 1,
+ features: [
+ {
+ id: undefined,
+ type: 3,
+ properties: {
+ __kbn__feature_id__: 'poly:G7PRMXQBgyyZ-h5iYibj:0',
+ _id: 'G7PRMXQBgyyZ-h5iYibj',
+ _index: 'poly',
+ },
+ extent: 4096,
+ pointArrays: [
+ [
+ { x: 840, y: 1600 },
+ { x: 1288, y: 1096 },
+ { x: 1672, y: 1104 },
+ { x: 2104, y: 1508 },
+ { x: 1472, y: 2316 },
+ { x: 840, y: 1600 },
+ ],
+ ],
+ },
+ ],
+ });
});
});
@@ -115,22 +160,78 @@ describe('getGridTile', () => {
};
test('0.0.0 tile (clusters)', async () => {
- const tile = await getGridTile(defaultParams);
- compareTiles('./__tests__/pbf/0_0_0_grid_aspoint.pbf', tile);
+ const pbfTile = await getGridTile(defaultParams);
+ const jsonTile = new VectorTile(new Protobuf(pbfTile));
+ compareJsonTiles(jsonTile, {
+ version: 2,
+ name: 'source_layer',
+ extent: 4096,
+ length: 1,
+ features: [
+ {
+ id: undefined,
+ type: 1,
+ properties: {
+ ['avg_of_TOTAL_AV']: 5398920.390458991,
+ doc_count: 42637,
+ },
+ extent: 4096,
+ pointArrays: [[{ x: 1206, y: 1539 }]],
+ },
+ ],
+ });
});
test('0.0.0 tile (grids)', async () => {
- const tile = await getGridTile({ ...defaultParams, requestType: RENDER_AS.GRID });
- compareTiles('./__tests__/pbf/0_0_0_grid_asgrid.pbf', tile);
+ const pbfTile = await getGridTile({ ...defaultParams, requestType: RENDER_AS.GRID });
+ const jsonTile = new VectorTile(new Protobuf(pbfTile));
+ compareJsonTiles(jsonTile, {
+ version: 2,
+ name: 'source_layer',
+ extent: 4096,
+ length: 1,
+ features: [
+ {
+ id: undefined,
+ type: 3,
+ properties: {
+ ['avg_of_TOTAL_AV']: 5398920.390458991,
+ doc_count: 42637,
+ },
+ extent: 4096,
+ pointArrays: [
+ [
+ { x: 1216, y: 1536 },
+ { x: 1216, y: 1568 },
+ { x: 1184, y: 1568 },
+ { x: 1184, y: 1536 },
+ { x: 1216, y: 1536 },
+ ],
+ ],
+ },
+ ],
+ });
});
});
-function compareTiles(expectedRelativePath: string, actualTile: Buffer | null) {
- if (actualTile === null) {
- throw new Error('Tile should be created');
- }
- const expectedPath = path.resolve(__dirname, expectedRelativePath);
- const expectedBin = fs.readFileSync(expectedPath, 'binary');
- const expectedTile = Buffer.from(expectedBin, 'binary');
- expect(expectedTile.equals(actualTile)).toBe(true);
+/**
+ * Verifies JSON-representation of tile-contents
+ * @param actualTileJson
+ * @param expectedLayer
+ */
+function compareJsonTiles(actualTileJson: VectorTile, expectedLayer: ITileLayerJsonExpectation) {
+ const actualLayer: VectorTileLayer = actualTileJson.layers[MVT_SOURCE_LAYER_NAME];
+ expect(actualLayer.version).toEqual(expectedLayer.version);
+ expect(actualLayer.extent).toEqual(expectedLayer.extent);
+ expect(actualLayer.name).toEqual(expectedLayer.name);
+ expect(actualLayer.length).toEqual(expectedLayer.features.length);
+
+ expectedLayer.features.forEach((expectedFeature, index) => {
+ const actualFeature = actualLayer.feature(index);
+ expect(actualFeature.type).toEqual(expectedFeature.type);
+ expect(actualFeature.extent).toEqual(expectedFeature.extent);
+ expect(actualFeature.id).toEqual(expectedFeature.id);
+ expect(actualFeature.properties).toEqual(expectedFeature.properties);
+ expect(actualFeature.loadGeometry()).toEqual(expectedFeature.pointArrays);
+ });
}
diff --git a/yarn.lock b/yarn.lock
index 4dbfa610be6c3..25cbe964c19e1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2998,7 +2998,7 @@
resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e"
integrity sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4=
-"@mapbox/vector-tile@^1.3.1":
+"@mapbox/vector-tile@1.3.1", "@mapbox/vector-tile@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz#d3a74c90402d06e89ec66de49ec817ff53409666"
integrity sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==
@@ -21931,7 +21931,7 @@ pathval@^1.1.0:
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
-pbf@^3.0.5, pbf@^3.2.1:
+pbf@3.2.1, pbf@^3.0.5, pbf@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a"
integrity sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==
From 8dc86c5f4b720c1eaa9cee26aaad7e6389f46df1 Mon Sep 17 00:00:00 2001
From: Brian Seeders
Date: Fri, 18 Dec 2020 20:00:51 -0500
Subject: [PATCH 25/40] [CI] TeamCity updates (#85843)
---
.ci/teamcity/bootstrap.sh | 2 +-
.ci/teamcity/checks/bundle_limits.sh | 3 +-
.ci/teamcity/checks/commit.sh | 13 +++++++
.ci/teamcity/checks/commit_check_runner.sh | 9 +++++
.ci/teamcity/checks/jest_configs.sh | 8 ++++
.../checks/plugins_with_circular_deps.sh | 8 ++++
.ci/teamcity/oss/plugin_functional.sh | 21 ++++++++--
.ci/teamcity/setup_env.sh | 3 ++
.teamcity/pom.xml | 17 +++++++++
.teamcity/settings.kts | 2 +-
.teamcity/src/Agents.kt | 28 ++++++++++++++
.teamcity/src/Extensions.kt | 38 +------------------
.teamcity/src/builds/Checks.kt | 6 ++-
.../src/builds/default/DefaultCiGroup.kt | 4 ++
.../src/builds/default/DefaultCiGroups.kt | 2 +-
.teamcity/src/builds/es_snapshots/Verify.kt | 4 +-
.../OssApiServerIntegration.kt} | 8 +---
.teamcity/src/builds/test/AllTests.kt | 3 +-
.teamcity/src/builds/test/QuickTests.kt | 2 +-
.teamcity/src/projects/Kibana.kt | 38 +++----------------
.teamcity/src/templates/DefaultTemplate.kt | 7 ++--
.teamcity/src/templates/KibanaTemplate.kt | 9 ++---
.teamcity/tests/projects/KibanaTest.kt | 5 ++-
23 files changed, 143 insertions(+), 97 deletions(-)
create mode 100755 .ci/teamcity/checks/commit.sh
create mode 100755 .ci/teamcity/checks/commit_check_runner.sh
create mode 100755 .ci/teamcity/checks/jest_configs.sh
create mode 100755 .ci/teamcity/checks/plugins_with_circular_deps.sh
create mode 100644 .teamcity/src/Agents.kt
rename .teamcity/src/builds/{test/ApiServerIntegration.kt => oss/OssApiServerIntegration.kt} (62%)
diff --git a/.ci/teamcity/bootstrap.sh b/.ci/teamcity/bootstrap.sh
index adb884ca064ba..fc57811bb2077 100755
--- a/.ci/teamcity/bootstrap.sh
+++ b/.ci/teamcity/bootstrap.sh
@@ -7,7 +7,7 @@ source "$(dirname "${0}")/util.sh"
tc_start_block "Bootstrap"
tc_start_block "yarn install and kbn bootstrap"
-verify_no_git_changes yarn kbn bootstrap --prefer-offline
+verify_no_git_changes yarn kbn bootstrap
tc_end_block "yarn install and kbn bootstrap"
tc_start_block "build kbn-pm"
diff --git a/.ci/teamcity/checks/bundle_limits.sh b/.ci/teamcity/checks/bundle_limits.sh
index 3f7daef6d0473..751ec5a03ee7b 100755
--- a/.ci/teamcity/checks/bundle_limits.sh
+++ b/.ci/teamcity/checks/bundle_limits.sh
@@ -4,4 +4,5 @@ set -euo pipefail
source "$(dirname "${0}")/../util.sh"
-node scripts/build_kibana_platform_plugins --validate-limits
+checks-reporter-with-killswitch "Check Bundle Limits" \
+ node scripts/build_kibana_platform_plugins --validate-limits
diff --git a/.ci/teamcity/checks/commit.sh b/.ci/teamcity/checks/commit.sh
new file mode 100755
index 0000000000000..387ec0c126785
--- /dev/null
+++ b/.ci/teamcity/checks/commit.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source "$(dirname "${0}")/../util.sh"
+
+# Runs pre-commit hook script for the files touched in the last commit.
+# That way we can ensure a set of quick commit checks earlier as we removed
+# the pre-commit hook installation by default.
+# If files are more than 200 we will skip it and just use
+# the further ci steps that already check linting and file casing for the entire repo.
+checks-reporter-with-killswitch "Quick commit checks" \
+ "$(dirname "${0}")/commit_check_runner.sh"
diff --git a/.ci/teamcity/checks/commit_check_runner.sh b/.ci/teamcity/checks/commit_check_runner.sh
new file mode 100755
index 0000000000000..f2a4a20568215
--- /dev/null
+++ b/.ci/teamcity/checks/commit_check_runner.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+echo "!!!!!!!! ATTENTION !!!!!!!!
+That check is intended to provide earlier CI feedback after we remove the automatic install for the local pre-commit hook.
+If you want, you can still manually install the pre-commit hook locally by running 'node scripts/register_git_hook locally'
+!!!!!!!!!!!!!!!!!!!!!!!!!!!
+"
+
+node scripts/precommit_hook.js --ref HEAD~1..HEAD --max-files 200 --verbose
diff --git a/.ci/teamcity/checks/jest_configs.sh b/.ci/teamcity/checks/jest_configs.sh
new file mode 100755
index 0000000000000..6703ffffb5651
--- /dev/null
+++ b/.ci/teamcity/checks/jest_configs.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source "$(dirname "${0}")/../util.sh"
+
+checks-reporter-with-killswitch "Check Jest Configs" \
+ node scripts/check_jest_configs
diff --git a/.ci/teamcity/checks/plugins_with_circular_deps.sh b/.ci/teamcity/checks/plugins_with_circular_deps.sh
new file mode 100755
index 0000000000000..5acc4b2ae351b
--- /dev/null
+++ b/.ci/teamcity/checks/plugins_with_circular_deps.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source "$(dirname "${0}")/../util.sh"
+
+checks-reporter-with-killswitch "Check Plugins With Circular Dependencies" \
+ node scripts/find_plugins_with_circular_deps
diff --git a/.ci/teamcity/oss/plugin_functional.sh b/.ci/teamcity/oss/plugin_functional.sh
index 5d1ecbcbd48ee..3570bf01e49c4 100755
--- a/.ci/teamcity/oss/plugin_functional.sh
+++ b/.ci/teamcity/oss/plugin_functional.sh
@@ -13,6 +13,21 @@ if [[ ! -d "target" ]]; then
fi
cd -
-./test/scripts/test/plugin_functional.sh
-./test/scripts/test/example_functional.sh
-./test/scripts/test/interpreter_functional.sh
+checks-reporter-with-killswitch "Plugin Functional Tests" \
+ node scripts/functional_tests \
+ --config test/plugin_functional/config.ts \
+ --bail \
+ --debug
+
+checks-reporter-with-killswitch "Example Functional Tests" \
+ node scripts/functional_tests \
+ --config test/examples/config.js \
+ --bail \
+ --debug
+
+checks-reporter-with-killswitch "Interpreter Functional Tests" \
+ node scripts/functional_tests \
+ --config test/interpreter_functional/config.ts \
+ --bail \
+ --debug \
+ --kibana-install-dir "$KIBANA_INSTALL_DIR"
diff --git a/.ci/teamcity/setup_env.sh b/.ci/teamcity/setup_env.sh
index f662d36247a2f..982d129dae2a6 100755
--- a/.ci/teamcity/setup_env.sh
+++ b/.ci/teamcity/setup_env.sh
@@ -25,12 +25,14 @@ tc_set_env FORCE_COLOR 1
tc_set_env TEST_BROWSER_HEADLESS 1
tc_set_env ELASTIC_APM_ENVIRONMENT ci
+tc_set_env ELASTIC_APM_TRANSACTION_SAMPLE_RATE 0.1
if [[ "${KIBANA_CI_REPORTER_KEY_BASE64-}" ]]; then
tc_set_env KIBANA_CI_REPORTER_KEY "$(echo "$KIBANA_CI_REPORTER_KEY_BASE64" | base64 -d)"
fi
if is_pr; then
+ tc_set_env ELASTIC_APM_ACTIVE false
tc_set_env CHECKS_REPORTER_ACTIVE true
# These can be removed once we're not supporting Jenkins and TeamCity at the same time
@@ -39,6 +41,7 @@ if is_pr; then
tc_set_env ghprbActualCommit "$GITHUB_PR_TRIGGERED_SHA"
tc_set_env BUILD_URL "$TEAMCITY_BUILD_URL"
else
+ tc_set_env ELASTIC_APM_ACTIVE true
tc_set_env CHECKS_REPORTER_ACTIVE false
fi
diff --git a/.teamcity/pom.xml b/.teamcity/pom.xml
index 5fa068d0a92e0..e6ec1f1c043c2 100644
--- a/.teamcity/pom.xml
+++ b/.teamcity/pom.xml
@@ -46,6 +46,14 @@
true
+
+ teamcity
+ https://artifactory.elstc.co/artifactory/teamcity
+
+ true
+ always
+
+
@@ -53,6 +61,10 @@
JetBrains
https://download.jetbrains.com/teamcity-repository
+
+ teamcity
+ https://artifactory.elstc.co/artifactory/teamcity
+
@@ -124,5 +136,10 @@
junit
4.13
+
+ co.elastic.teamcity
+ teamcity-common
+ 1.0.0-SNAPSHOT
+
diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts
index ec1b1c6eb94ef..28108d019327b 100644
--- a/.teamcity/settings.kts
+++ b/.teamcity/settings.kts
@@ -2,7 +2,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.*
import projects.Kibana
import projects.KibanaConfiguration
-version = "2020.1"
+version = "2020.2"
val config = KibanaConfiguration {
agentNetwork = DslContext.getParameter("agentNetwork", "teamcity")
diff --git a/.teamcity/src/Agents.kt b/.teamcity/src/Agents.kt
new file mode 100644
index 0000000000000..557cce80d0f55
--- /dev/null
+++ b/.teamcity/src/Agents.kt
@@ -0,0 +1,28 @@
+import co.elastic.teamcity.common.GoogleCloudAgent
+import co.elastic.teamcity.common.GoogleCloudAgentDiskType
+import co.elastic.teamcity.common.GoogleCloudProfile
+
+private val sizes = listOf("2", "4", "8", "16")
+
+val StandardAgents = sizes.map { size -> size to GoogleCloudAgent {
+ sourceImageFamily = "elastic-kibana-ci-ubuntu-1804-lts"
+ agentPrefix = "kibana-standard-$size-"
+ machineType = "n2-standard-$size"
+ diskSizeGb = 75
+ diskType = GoogleCloudAgentDiskType.SSD
+} }.toMap()
+
+val BuildAgent = GoogleCloudAgent {
+ sourceImageFamily = "elastic-kibana-ci-ubuntu-1804-lts"
+ agentPrefix = "kibana-c2-16-"
+ machineType = "c2-standard-16"
+ diskSizeGb = 250
+ diskType = GoogleCloudAgentDiskType.SSD
+}
+
+val CloudProfile = GoogleCloudProfile {
+ accessKeyId = "447fdd4d-7129-46b7-9822-2e57658c7422"
+
+ agents(StandardAgents)
+ agent(BuildAgent)
+}
diff --git a/.teamcity/src/Extensions.kt b/.teamcity/src/Extensions.kt
index 120b333d43e72..2942a6385f13f 100644
--- a/.teamcity/src/Extensions.kt
+++ b/.teamcity/src/Extensions.kt
@@ -1,9 +1,7 @@
+import co.elastic.teamcity.common.requireAgent
import jetbrains.buildServer.configs.kotlin.v2019_2.*
-import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.notifications
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.ScriptBuildStep
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
-import jetbrains.buildServer.configs.kotlin.v2019_2.ui.insert
-import projects.kibanaConfiguration
fun BuildFeatures.junit(dirs: String = "target/**/TEST-*.xml") {
feature {
@@ -13,40 +11,8 @@ fun BuildFeatures.junit(dirs: String = "target/**/TEST-*.xml") {
}
}
-fun ProjectFeatures.kibanaAgent(init: ProjectFeature.() -> Unit) {
- feature {
- type = "CloudImage"
- param("network", kibanaConfiguration.agentNetwork)
- param("subnet", kibanaConfiguration.agentSubnet)
- param("growingId", "true")
- param("agent_pool_id", "-2")
- param("preemptible", "false")
- param("sourceProject", "elastic-images-prod")
- param("sourceImageFamily", "elastic-kibana-ci-ubuntu-1804-lts")
- param("zone", "us-central1-a")
- param("profileId", "kibana")
- param("diskType", "pd-ssd")
- param("machineCustom", "false")
- param("maxInstances", "200")
- param("imageType", "ImageFamily")
- param("diskSizeGb", "75") // TODO
- init()
- }
-}
-
-fun ProjectFeatures.kibanaAgent(size: String, init: ProjectFeature.() -> Unit = {}) {
- kibanaAgent {
- id = "KIBANA_STANDARD_$size"
- param("source-id", "kibana-standard-$size-")
- param("machineType", "n2-standard-$size")
- init()
- }
-}
-
fun BuildType.kibanaAgent(size: String) {
- requirements {
- startsWith("teamcity.agent.name", "kibana-standard-$size-", "RQ_AGENT_NAME")
- }
+ requireAgent(StandardAgents[size]!!)
}
fun BuildType.kibanaAgent(size: Int) {
diff --git a/.teamcity/src/builds/Checks.kt b/.teamcity/src/builds/Checks.kt
index 1228ea4d94f4c..37336316c4c91 100644
--- a/.teamcity/src/builds/Checks.kt
+++ b/.teamcity/src/builds/Checks.kt
@@ -11,16 +11,18 @@ object Checks : BuildType({
kibanaAgent(4)
val checkScripts = mapOf(
+ "Quick Commit Checks" to ".ci/teamcity/checks/commit.sh",
"Check Telemetry Schema" to ".ci/teamcity/checks/telemetry.sh",
"Check TypeScript Projects" to ".ci/teamcity/checks/ts_projects.sh",
"Check File Casing" to ".ci/teamcity/checks/file_casing.sh",
"Check Licenses" to ".ci/teamcity/checks/licenses.sh",
"Verify NOTICE" to ".ci/teamcity/checks/verify_notice.sh",
- "Test Hardening" to ".ci/teamcity/checks/test_hardening.sh",
"Check Types" to ".ci/teamcity/checks/type_check.sh",
+ "Check Jest Configs" to ".ci/teamcity/checks/jest_configs.sh",
"Check Doc API Changes" to ".ci/teamcity/checks/doc_api_changes.sh",
"Check Bundle Limits" to ".ci/teamcity/checks/bundle_limits.sh",
- "Check i18n" to ".ci/teamcity/checks/i18n.sh"
+ "Check i18n" to ".ci/teamcity/checks/i18n.sh",
+ "Check Plugins With Circular Dependencies" to ".ci/teamcity/checks/plugins_with_circular_deps.sh"
)
steps {
diff --git a/.teamcity/src/builds/default/DefaultCiGroup.kt b/.teamcity/src/builds/default/DefaultCiGroup.kt
index 7dbe9cd0ba84c..2c3b0d348591e 100755
--- a/.teamcity/src/builds/default/DefaultCiGroup.kt
+++ b/.teamcity/src/builds/default/DefaultCiGroup.kt
@@ -1,5 +1,7 @@
package builds.default
+import StandardAgents
+import co.elastic.teamcity.common.requireAgent
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import runbld
@@ -11,5 +13,7 @@ class DefaultCiGroup(val ciGroup: Int = 0, init: BuildType.() -> Unit = {}) : De
runbld("Default CI Group $ciGroup", "./.ci/teamcity/default/ci_group.sh $ciGroup")
}
+ requireAgent(StandardAgents["4"]!!)
+
init()
})
diff --git a/.teamcity/src/builds/default/DefaultCiGroups.kt b/.teamcity/src/builds/default/DefaultCiGroups.kt
index 6f1d45598c92e..4f39283149e73 100644
--- a/.teamcity/src/builds/default/DefaultCiGroups.kt
+++ b/.teamcity/src/builds/default/DefaultCiGroups.kt
@@ -3,7 +3,7 @@ package builds.default
import dependsOn
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
-const val DEFAULT_CI_GROUP_COUNT = 10
+const val DEFAULT_CI_GROUP_COUNT = 11
val defaultCiGroups = (1..DEFAULT_CI_GROUP_COUNT).map { DefaultCiGroup(it) }
object DefaultCiGroups : BuildType({
diff --git a/.teamcity/src/builds/es_snapshots/Verify.kt b/.teamcity/src/builds/es_snapshots/Verify.kt
index c778814af536c..4c0307e9eca55 100644
--- a/.teamcity/src/builds/es_snapshots/Verify.kt
+++ b/.teamcity/src/builds/es_snapshots/Verify.kt
@@ -6,7 +6,7 @@ import builds.default.defaultCiGroups
import builds.oss.OssBuild
import builds.oss.OssPluginFunctional
import builds.oss.ossCiGroups
-import builds.test.ApiServerIntegration
+import builds.oss.OssApiServerIntegration
import builds.test.JestIntegration
import dependsOn
import jetbrains.buildServer.configs.kotlin.v2019_2.*
@@ -49,7 +49,7 @@ val defaultBuildsToClone = listOf(
val defaultCloned = defaultBuildsToClone.map { cloneForVerify(it) }
val integrationsBuildsToClone = listOf(
- ApiServerIntegration,
+ OssApiServerIntegration,
JestIntegration
)
diff --git a/.teamcity/src/builds/test/ApiServerIntegration.kt b/.teamcity/src/builds/oss/OssApiServerIntegration.kt
similarity index 62%
rename from .teamcity/src/builds/test/ApiServerIntegration.kt
rename to .teamcity/src/builds/oss/OssApiServerIntegration.kt
index ca58b628cbd22..a04512fb2aba5 100644
--- a/.teamcity/src/builds/test/ApiServerIntegration.kt
+++ b/.teamcity/src/builds/oss/OssApiServerIntegration.kt
@@ -1,10 +1,8 @@
-package builds.test
+package builds.oss
-import addTestSettings
-import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
import runbld
-object ApiServerIntegration : BuildType({
+object OssApiServerIntegration : OssFunctionalBase({
name = "API/Server Integration"
description = "Executes API and Server Integration Tests"
@@ -12,6 +10,4 @@ object ApiServerIntegration : BuildType({
runbld("API Integration", "./.ci/teamcity/oss/api_integration.sh")
runbld("Server Integration", "./.ci/teamcity/oss/server_integration.sh")
}
-
- addTestSettings()
})
diff --git a/.teamcity/src/builds/test/AllTests.kt b/.teamcity/src/builds/test/AllTests.kt
index d1b5898d1a5f5..9506d98cbe50e 100644
--- a/.teamcity/src/builds/test/AllTests.kt
+++ b/.teamcity/src/builds/test/AllTests.kt
@@ -1,5 +1,6 @@
package builds.test
+import builds.oss.OssApiServerIntegration
import dependsOn
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
@@ -8,5 +9,5 @@ object AllTests : BuildType({
description = "All Non-Functional Tests"
type = Type.COMPOSITE
- dependsOn(QuickTests, Jest, XPackJest, JestIntegration, ApiServerIntegration)
+ dependsOn(QuickTests, Jest, XPackJest, JestIntegration, OssApiServerIntegration)
})
diff --git a/.teamcity/src/builds/test/QuickTests.kt b/.teamcity/src/builds/test/QuickTests.kt
index 5b1d2541480ad..a294fce9599c3 100644
--- a/.teamcity/src/builds/test/QuickTests.kt
+++ b/.teamcity/src/builds/test/QuickTests.kt
@@ -12,7 +12,7 @@ object QuickTests : BuildType({
kibanaAgent(2)
val testScripts = mapOf(
- "Test Hardening" to ".ci/teamcity/checkes/test_hardening.sh",
+ "Test Hardening" to ".ci/teamcity/checks/test_hardening.sh",
"Test Projects" to ".ci/teamcity/tests/test_projects.sh",
"Mocha Tests" to ".ci/teamcity/tests/mocha.sh"
)
diff --git a/.teamcity/src/projects/Kibana.kt b/.teamcity/src/projects/Kibana.kt
index 20c30eedf5b91..1878f49debe8c 100644
--- a/.teamcity/src/projects/Kibana.kt
+++ b/.teamcity/src/projects/Kibana.kt
@@ -5,9 +5,10 @@ import builds.*
import builds.default.*
import builds.oss.*
import builds.test.*
+import CloudProfile
+import co.elastic.teamcity.common.googleCloudProfile
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.projectFeatures.slackConnection
-import kibanaAgent
import templates.KibanaTemplate
import templates.DefaultTemplate
import vcs.Elasticsearch
@@ -31,7 +32,7 @@ fun Kibana(config: KibanaConfiguration = KibanaConfiguration()) : Project {
param("teamcity.ui.settings.readOnly", "true")
// https://github.com/JetBrains/teamcity-webhooks
- param("teamcity.internal.webhooks.enable", "true")
+ param("teamcity.internal.webhooks.enable", "false")
param("teamcity.internal.webhooks.events", "BUILD_STARTED;BUILD_FINISHED;BUILD_INTERRUPTED;CHANGES_LOADED;BUILD_TYPE_ADDED_TO_QUEUE;BUILD_PROBLEMS_CHANGED")
param("teamcity.internal.webhooks.url", "https://ci-stats.kibana.dev/_teamcity_webhook")
param("teamcity.internal.webhooks.username", "automation")
@@ -46,36 +47,9 @@ fun Kibana(config: KibanaConfiguration = KibanaConfiguration()) : Project {
defaultTemplate = DefaultTemplate
- features {
- val sizes = listOf("2", "4", "8", "16")
- for (size in sizes) {
- kibanaAgent(size)
- }
-
- kibanaAgent {
- id = "KIBANA_C2_16"
- param("source-id", "kibana-c2-16-")
- param("machineType", "c2-standard-16")
- }
-
- feature {
- id = "kibana"
- type = "CloudProfile"
- param("agentPushPreset", "")
- param("profileId", "kibana")
- param("profileServerUrl", "")
- param("name", "kibana")
- param("total-work-time", "")
- param("credentialsType", "key")
- param("description", "")
- param("next-hour", "")
- param("cloud-code", "google")
- param("terminate-after-build", "true")
- param("terminate-idle-time", "30")
- param("enabled", "true")
- param("secure:accessKey", "credentialsJSON:447fdd4d-7129-46b7-9822-2e57658c7422")
- }
+ googleCloudProfile(CloudProfile)
+ features {
slackConnection {
id = "KIBANA_SLACK"
displayName = "Kibana Slack"
@@ -106,7 +80,6 @@ fun Kibana(config: KibanaConfiguration = KibanaConfiguration()) : Project {
buildType(JestIntegration)
}
- buildType(ApiServerIntegration)
buildType(QuickTests)
buildType(AllTests)
}
@@ -125,6 +98,7 @@ fun Kibana(config: KibanaConfiguration = KibanaConfiguration()) : Project {
buildType(OssFirefox)
buildType(OssAccessibility)
buildType(OssPluginFunctional)
+ buildType(OssApiServerIntegration)
subProject {
id("CIGroups")
diff --git a/.teamcity/src/templates/DefaultTemplate.kt b/.teamcity/src/templates/DefaultTemplate.kt
index 762218b72ab10..1f7f364600e21 100644
--- a/.teamcity/src/templates/DefaultTemplate.kt
+++ b/.teamcity/src/templates/DefaultTemplate.kt
@@ -1,15 +1,14 @@
package templates
+import StandardAgents
+import co.elastic.teamcity.common.requireAgent
import jetbrains.buildServer.configs.kotlin.v2019_2.Template
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.perfmon
object DefaultTemplate : Template({
name = "Default Template"
- requirements {
- equals("system.cloud.profile_id", "kibana", "RQ_CLOUD_PROFILE_ID")
- startsWith("teamcity.agent.name", "kibana-standard-2-", "RQ_AGENT_NAME")
- }
+ requireAgent(StandardAgents["2"]!!)
params {
param("env.HOME", "/var/lib/jenkins") // TODO once the agent images are sorted out
diff --git a/.teamcity/src/templates/KibanaTemplate.kt b/.teamcity/src/templates/KibanaTemplate.kt
index 117c30ddb86e3..83fe4fdaa1edd 100644
--- a/.teamcity/src/templates/KibanaTemplate.kt
+++ b/.teamcity/src/templates/KibanaTemplate.kt
@@ -1,5 +1,7 @@
package templates
+import StandardAgents
+import co.elastic.teamcity.common.requireAgent
import vcs.Kibana
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildStep
import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
@@ -21,10 +23,7 @@ object KibanaTemplate : Template({
// checkoutDir = "/dev/shm/%system.teamcity.buildType.id%/%system.build.number%/kibana"
}
- requirements {
- equals("system.cloud.profile_id", "kibana", "RQ_CLOUD_PROFILE_ID")
- startsWith("teamcity.agent.name", "kibana-standard-2-", "RQ_AGENT_NAME")
- }
+ requireAgent(StandardAgents["2"]!!)
features {
perfmon { }
@@ -41,7 +40,7 @@ object KibanaTemplate : Template({
}
failureConditions {
- executionTimeoutMin = 120
+ executionTimeoutMin = 160
testFailure = false
}
diff --git a/.teamcity/tests/projects/KibanaTest.kt b/.teamcity/tests/projects/KibanaTest.kt
index 677effec5be65..311c15a1da7cb 100644
--- a/.teamcity/tests/projects/KibanaTest.kt
+++ b/.teamcity/tests/projects/KibanaTest.kt
@@ -1,5 +1,7 @@
package projects
+import jetbrains.buildServer.configs.kotlin.v2019_2.AbsoluteId
+import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext
import org.junit.Assert.*
import org.junit.Test
@@ -18,10 +20,11 @@ class KibanaTest {
@Test
fun test_CloudImages_Exist() {
+ DslContext.projectId = AbsoluteId("My Project")
val project = Kibana(TestConfig)
assertTrue(project.features.items.any {
- it.type == "CloudImage" && it.params.any { param -> param.name == "network" && param.value == "network"}
+ it.type == "CloudImage" && it.params.any { param -> param.name == "network" && param.value == "teamcity" }
})
}
}
From 7e9177d3a9e0da3c812de08f11d673af99334451 Mon Sep 17 00:00:00 2001
From: Nick Partridge
Date: Fri, 18 Dec 2020 19:07:00 -0600
Subject: [PATCH 26/40] Rename chartLibrary setting to legacyChartsLibrary
(#86529)
* rename chartLibrary setting to legacyChartsLibrary
* fix spelling
* fix plugin setting check boolean
---
docs/management/advanced-options.asciidoc | 4 ++--
src/plugins/vis_type_vislib/public/plugin.ts | 4 ++--
src/plugins/vis_type_xy/common/index.ts | 2 +-
.../public/components/split_chart_warning.tsx | 4 ++--
src/plugins/vis_type_xy/public/plugin.ts | 4 ++--
.../public/vis_types/split_tooltip.tsx | 5 +----
src/plugins/vis_type_xy/server/plugin.ts | 14 +++++++-------
.../apps/dashboard/dashboard_state.ts | 14 +++++++-------
test/functional/apps/dashboard/index.ts | 4 ++--
.../apps/getting_started/_shakespeare.ts | 8 ++++----
test/functional/apps/getting_started/index.ts | 6 +++---
test/functional/apps/visualize/index.ts | 6 +++---
.../page_objects/visualize_chart_page.ts | 17 +++++++++--------
.../page_objects/visualize_editor_page.ts | 2 +-
test/functional/page_objects/visualize_page.ts | 2 +-
15 files changed, 47 insertions(+), 49 deletions(-)
diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc
index 9a87d4c9d886a..99fadb240335a 100644
--- a/docs/management/advanced-options.asciidoc
+++ b/docs/management/advanced-options.asciidoc
@@ -453,8 +453,8 @@ of buckets to try to represent.
==== Visualization
[horizontal]
-[[visualization-visualize-chartslibrary]]`visualization:visualize:chartsLibrary`::
-Enables the new charts library for area, line, and bar charts in visualization panels. Does *not* support the split chart aggregation.
+[[visualization-visualize-chartslibrary]]`visualization:visualize:legacyChartsLibrary`::
+Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation.
[[visualization-colormapping]]`visualization:colorMapping`::
**This setting is deprecated and will not be supported as of 8.0.**
diff --git a/src/plugins/vis_type_vislib/public/plugin.ts b/src/plugins/vis_type_vislib/public/plugin.ts
index bef8ad26fb12c..36a184d3da507 100644
--- a/src/plugins/vis_type_vislib/public/plugin.ts
+++ b/src/plugins/vis_type_vislib/public/plugin.ts
@@ -24,7 +24,7 @@ import { VisualizationsSetup } from '../../visualizations/public';
import { ChartsPluginSetup } from '../../charts/public';
import { DataPublicPluginStart } from '../../data/public';
import { KibanaLegacyStart } from '../../kibana_legacy/public';
-import { CHARTS_LIBRARY } from '../../vis_type_xy/public';
+import { LEGACY_CHARTS_LIBRARY } from '../../vis_type_xy/public';
import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn';
import { createPieVisFn } from './pie_fn';
@@ -61,7 +61,7 @@ export class VisTypeVislibPlugin
core: VisTypeVislibCoreSetup,
{ expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies
) {
- if (core.uiSettings.get(CHARTS_LIBRARY)) {
+ if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, true)) {
// Register only non-replaced vis types
convertedTypeDefinitions.forEach(visualizations.createBaseVisualization);
visualizations.createBaseVisualization(pieVisTypeDefinition);
diff --git a/src/plugins/vis_type_xy/common/index.ts b/src/plugins/vis_type_xy/common/index.ts
index bf498229a1b54..edee1ea3219db 100644
--- a/src/plugins/vis_type_xy/common/index.ts
+++ b/src/plugins/vis_type_xy/common/index.ts
@@ -34,4 +34,4 @@ export type ChartType = $Values;
*/
export type XyVisType = ChartType | 'horizontal_bar';
-export const CHARTS_LIBRARY = 'visualization:visualize:chartsLibrary';
+export const LEGACY_CHARTS_LIBRARY = 'visualization:visualize:legacyChartsLibrary';
diff --git a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx b/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx
index a9aa2bf24601b..7265e70a791a3 100644
--- a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx
+++ b/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx
@@ -38,13 +38,13 @@ export const SplitChartWarning: FC = () => {
>
),
diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_type_xy/public/plugin.ts
index c8812b07e5949..7425c5f7248ac 100644
--- a/src/plugins/vis_type_xy/public/plugin.ts
+++ b/src/plugins/vis_type_xy/public/plugin.ts
@@ -34,7 +34,7 @@ import {
setDocLinks,
} from './services';
import { visTypesDefinitions } from './vis_types';
-import { CHARTS_LIBRARY } from '../common';
+import { LEGACY_CHARTS_LIBRARY } from '../common';
import { xyVisRenderer } from './vis_renderer';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
@@ -71,7 +71,7 @@ export class VisTypeXyPlugin
core: VisTypeXyCoreSetup,
{ expressions, visualizations, charts }: VisTypeXyPluginSetupDependencies
) {
- if (core.uiSettings.get(CHARTS_LIBRARY, false)) {
+ if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, true)) {
setUISettings(core.uiSettings);
setThemeService(charts.theme);
setColorsService(charts.legacyColors);
diff --git a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx b/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx
index 2abe3e9b8cf71..84f1fd9187f4f 100644
--- a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx
+++ b/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx
@@ -25,10 +25,7 @@ export function SplitTooltip() {
return (
charts library,
- }}
+ defaultMessage="Split chart aggregation is not supported with the new charts library. Please enable the legacy charts library advanced setting to use split chart aggregation."
/>
);
}
diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_type_xy/server/plugin.ts
index 51d741f9374fe..b5999535064aa 100644
--- a/src/plugins/vis_type_xy/server/plugin.ts
+++ b/src/plugins/vis_type_xy/server/plugin.ts
@@ -22,21 +22,21 @@ import { schema } from '@kbn/config-schema';
import { CoreSetup, Plugin, UiSettingsParams } from 'kibana/server';
-import { CHARTS_LIBRARY } from '../common';
+import { LEGACY_CHARTS_LIBRARY } from '../common';
export const uiSettingsConfig: Record> = {
// TODO: Remove this when vis_type_vislib is removed
// https://github.com/elastic/kibana/issues/56143
- [CHARTS_LIBRARY]: {
- name: i18n.translate('visTypeXy.advancedSettings.visualization.chartsLibrary', {
- defaultMessage: 'Charts library',
+ [LEGACY_CHARTS_LIBRARY]: {
+ name: i18n.translate('visTypeXy.advancedSettings.visualization.legacyChartsLibrary.name', {
+ defaultMessage: 'Legacy charts library',
}),
- value: false,
+ value: true,
description: i18n.translate(
- 'visTypeXy.advancedSettings.visualization.chartsLibrary.description',
+ 'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.description',
{
defaultMessage:
- 'Enables new charts library for areas, lines and bars in visualize. Currently, does not support split chart aggregation.',
+ 'Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation.',
}
),
category: ['visualization'],
diff --git a/test/functional/apps/dashboard/dashboard_state.ts b/test/functional/apps/dashboard/dashboard_state.ts
index 48fec4efee5db..728787543927c 100644
--- a/test/functional/apps/dashboard/dashboard_state.ts
+++ b/test/functional/apps/dashboard/dashboard_state.ts
@@ -46,16 +46,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('dashboard state', function describeIndexTests() {
// Used to track flag before and after reset
- let isNewChartUiEnabled = false;
+ let isNewChartsLibraryEnabled = false;
before(async function () {
- isNewChartUiEnabled = await PageObjects.visChart.isNewChartUiEnabled();
+ isNewChartsLibraryEnabled = await PageObjects.visChart.isNewChartsLibraryEnabled();
await PageObjects.dashboard.initTests();
await PageObjects.dashboard.preserveCrossAppState();
- if (isNewChartUiEnabled) {
+ if (isNewChartsLibraryEnabled) {
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': true,
+ 'visualization:visualize:legacyChartsLibrary': false,
});
await browser.refresh();
}
@@ -73,12 +73,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const visName = await PageObjects.visChart.getExpectedValue(
AREA_CHART_VIS_NAME,
- `${AREA_CHART_VIS_NAME} - chartsLibrary`
+ `${AREA_CHART_VIS_NAME} - new charts library`
);
await dashboardAddPanel.addVisualization(visName);
const dashboarName = await PageObjects.visChart.getExpectedValue(
'Overridden colors',
- 'Overridden colors - chartsLibrary'
+ 'Overridden colors - new charts library'
);
await PageObjects.dashboard.saveDashboard(dashboarName);
@@ -93,7 +93,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.loadSavedDashboard(dashboarName);
- if (await PageObjects.visChart.isNewChartUiEnabled()) {
+ if (await PageObjects.visChart.isNewChartsLibraryEnabled()) {
await elasticChart.setNewChartUiDebugFlag();
await queryBar.submitQuery();
}
diff --git a/test/functional/apps/dashboard/index.ts b/test/functional/apps/dashboard/index.ts
index f2dba4785ea05..6fb5f874022a0 100644
--- a/test/functional/apps/dashboard/index.ts
+++ b/test/functional/apps/dashboard/index.ts
@@ -126,7 +126,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
before(async () => {
await loadLogstash();
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': true,
+ 'visualization:visualize:legacyChartsLibrary': false,
});
await browser.refresh();
});
@@ -134,7 +134,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
after(async () => {
await unloadLogstash();
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': false,
+ 'visualization:visualize:legacyChartsLibrary': true,
});
await browser.refresh();
});
diff --git a/test/functional/apps/getting_started/_shakespeare.ts b/test/functional/apps/getting_started/_shakespeare.ts
index fa7d932aca1e8..09fdff9977b5e 100644
--- a/test/functional/apps/getting_started/_shakespeare.ts
+++ b/test/functional/apps/getting_started/_shakespeare.ts
@@ -49,22 +49,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// order they are added.
let aggIndex = 1;
// Used to track flag before and after reset
- let isNewChartUiEnabled = false;
+ let isNewChartsLibraryEnabled = false;
before(async function () {
log.debug(
'Load empty_kibana and Shakespeare Getting Started data\n' +
'https://www.elastic.co/guide/en/kibana/current/tutorial-load-dataset.html'
);
- isNewChartUiEnabled = await PageObjects.visChart.isNewChartUiEnabled();
+ isNewChartsLibraryEnabled = await PageObjects.visChart.isNewChartsLibraryEnabled();
await security.testUser.setRoles(['kibana_admin', 'test_shakespeare_reader']);
await esArchiver.load('empty_kibana', { skipExisting: true });
log.debug('Load shakespeare data');
await esArchiver.loadIfNeeded('getting_started/shakespeare');
- if (isNewChartUiEnabled) {
+ if (isNewChartsLibraryEnabled) {
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': true,
+ 'visualization:visualize:legacyChartsLibrary': false,
});
await browser.refresh();
}
diff --git a/test/functional/apps/getting_started/index.ts b/test/functional/apps/getting_started/index.ts
index 4cef16a47571e..b832c797adac6 100644
--- a/test/functional/apps/getting_started/index.ts
+++ b/test/functional/apps/getting_started/index.ts
@@ -31,17 +31,17 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
});
// TODO: Remove when vislib is removed
- describe('chartsLibrary', function () {
+ describe('new charts library', function () {
before(async () => {
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': true,
+ 'visualization:visualize:legacyChartsLibrary': false,
});
await browser.refresh();
});
after(async () => {
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': false,
+ 'visualization:visualize:legacyChartsLibrary': true,
});
await browser.refresh();
});
diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts
index ad69ad5ab41cd..94b0c5b6c8a27 100644
--- a/test/functional/apps/visualize/index.ts
+++ b/test/functional/apps/visualize/index.ts
@@ -43,19 +43,19 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
});
// TODO: Remove when vislib is removed
- describe('chartsLibrary', function () {
+ describe('new charts library', function () {
this.tags('ciGroup7');
before(async () => {
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': true,
+ 'visualization:visualize:legacyChartsLibrary': false,
});
await browser.refresh();
});
after(async () => {
await kibanaServer.uiSettings.update({
- 'visualization:visualize:chartsLibrary': false,
+ 'visualization:visualize:legacyChartsLibrary': true,
});
await browser.refresh();
});
diff --git a/test/functional/page_objects/visualize_chart_page.ts b/test/functional/page_objects/visualize_chart_page.ts
index 2ad7c77a58ca9..41af5a4c47e78 100644
--- a/test/functional/page_objects/visualize_chart_page.ts
+++ b/test/functional/page_objects/visualize_chart_page.ts
@@ -41,22 +41,23 @@ export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrPr
}
/**
- * Is chartsLibrary advanced setting enabled
+ * Is new charts library advanced setting enabled
*/
- public async isNewChartUiEnabled(): Promise {
- const enabled =
- Boolean(await kibanaServer.uiSettings.get('visualization:visualize:chartsLibrary')) ??
- false;
- log.debug(`-- isNewChartUiEnabled = ${enabled}`);
+ public async isNewChartsLibraryEnabled(): Promise {
+ const legacyChartsLibrary =
+ Boolean(await kibanaServer.uiSettings.get('visualization:visualize:legacyChartsLibrary')) ??
+ true;
+ const enabled = !legacyChartsLibrary;
+ log.debug(`-- isNewChartsLibraryEnabled = ${enabled}`);
return enabled;
}
/**
- * Is chartsLibrary enabled and an area, line or histogram chart is available
+ * Is new charts library enabled and an area, line or histogram chart exists
*/
private async isVisTypeXYChart(): Promise {
- const enabled = await this.isNewChartUiEnabled();
+ const enabled = await this.isNewChartsLibraryEnabled();
if (!enabled) {
log.debug(`-- isVisTypeXYChart = false`);
diff --git a/test/functional/page_objects/visualize_editor_page.ts b/test/functional/page_objects/visualize_editor_page.ts
index d6134883332e3..18573c5084618 100644
--- a/test/functional/page_objects/visualize_editor_page.ts
+++ b/test/functional/page_objects/visualize_editor_page.ts
@@ -74,7 +74,7 @@ export function VisualizeEditorPageProvider({ getService, getPageObjects }: FtrP
}
public async clickGo() {
- if (await visChart.isNewChartUiEnabled()) {
+ if (await visChart.isNewChartsLibraryEnabled()) {
await elasticChart.setNewChartUiDebugFlag();
}
diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts
index e5bd6a0f10d82..d8329f492fe80 100644
--- a/test/functional/page_objects/visualize_page.ts
+++ b/test/functional/page_objects/visualize_page.ts
@@ -99,7 +99,7 @@ export function VisualizePageProvider({ getService, getPageObjects }: FtrProvide
}
public async clickRefresh() {
- if (await visChart.isNewChartUiEnabled()) {
+ if (await visChart.isNewChartsLibraryEnabled()) {
await elasticChart.setNewChartUiDebugFlag();
}
await queryBar.clickQuerySubmitButton();
From 9a3e2910a33527f74a2add9145ec598865fe19cc Mon Sep 17 00:00:00 2001
From: Luke Elmers
Date: Fri, 18 Dec 2020 18:34:07 -0700
Subject: [PATCH 27/40] App Services: Remove remaining uiActions, expressions,
data, embeddable circular dependencies. (#82791)
* Move applyFilter, selectRange, valueClick triggers to data/embeddables.
* Update imports.
* Remove embeddable references to non-existent data plugin dependency.
* remove data mocks from embeddable
* Remove query, filters, timeRange from EmbeddableInput and move to apps.
* Remove data plugin imports from embeddable test samples.
* Remove circular dependencies caused by expressions renderer handlers.
* Update circular deps allowList.
* Remove data dependency on embeddable.
* Revert accidental data plugin change.
* Fix new circular deps issues.
* Update generated docs.
* Fix type errors in vis_type_xy
* Fix inspector data table.
---
...lugins-data-public.apply_filter_trigger.md | 11 +++
...plyglobalfilteractioncontext.embeddable.md | 2 +-
...a-public.applyglobalfilteractioncontext.md | 2 +-
.../kibana-plugin-plugins-data-public.md | 1 +
...plugin-plugins-data-server.plugin.setup.md | 4 +-
...plugin-plugins-data-server.plugin.start.md | 4 +-
...ble-public.embeddablecontext.embeddable.md | 2 +-
...ins-embeddable-public.embeddablecontext.md | 4 +-
...ugins-embeddable-public.embeddableinput.md | 3 -
...public.embeddablesetupdependencies.data.md | 11 ---
...able-public.embeddablesetupdependencies.md | 1 -
...public.embeddablestartdependencies.data.md | 11 ---
...able-public.embeddablestartdependencies.md | 1 -
...able-public.iscontextmenutriggercontext.md | 2 +-
...kibana-plugin-plugins-embeddable-public.md | 2 +
...-embeddable-public.select_range_trigger.md | 11 +++
...s-embeddable-public.value_click_trigger.md | 11 +++
...sions-public.iinterpreterrenderhandlers.md | 2 +-
...blic.iinterpreterrenderhandlers.uistate.md | 4 +-
...sions-server.iinterpreterrenderhandlers.md | 2 +-
...rver.iinterpreterrenderhandlers.uistate.md | 4 +-
...-ui_actions-public.apply_filter_trigger.md | 11 ---
...ns-ui_actions-public.applyfiltertrigger.md | 11 ---
...kibana-plugin-plugins-ui_actions-public.md | 6 --
...tions-public.rowclickcontext.embeddable.md | 2 +-
...ugins-ui_actions-public.rowclickcontext.md | 2 +-
...-ui_actions-public.select_range_trigger.md | 11 ---
...ns-ui_actions-public.selectrangetrigger.md | 11 ---
...in-plugins-ui_actions-public.trigger.id.md | 2 +-
...lugin-plugins-ui_actions-public.trigger.md | 2 +-
...ic.triggercontextmapping.filter_trigger.md | 11 ---
...ui_actions-public.triggercontextmapping.md | 3 -
...ggercontextmapping.select_range_trigger.md | 11 ---
...iggercontextmapping.value_click_trigger.md | 11 ---
...ublic.uiactionsservice.addtriggeraction.md | 2 +-
...ns-public.uiactionsservice.attachaction.md | 2 +-
....uiactionsservice.executetriggeractions.md | 2 +-
...tions-public.uiactionsservice.getaction.md | 2 +-
...ions-public.uiactionsservice.gettrigger.md | 2 +-
...blic.uiactionsservice.gettriggeractions.md | 2 +-
...ionsservice.gettriggercompatibleactions.md | 2 +-
...gins-ui_actions-public.uiactionsservice.md | 16 ++---
...-public.uiactionsservice.registeraction.md | 2 +-
...s-ui_actions-public.value_click_trigger.md | 11 ---
...ins-ui_actions-public.valueclicktrigger.md | 11 ---
.../run_find_plugins_with_circular_deps.ts | 7 +-
.../public/actions/apply_filter_action.ts | 5 +-
.../create_filters_from_range_select.ts | 12 +++-
.../create_filters_from_value_click.test.ts | 8 ++-
.../create_filters_from_value_click.ts | 15 +++-
.../public/actions/select_range_action.ts | 22 +++---
.../data/public/actions/value_click_action.ts | 26 +++++--
src/plugins/data/public/index.ts | 1 +
src/plugins/data/public/plugin.ts | 18 ++---
src/plugins/data/public/public.api.md | 9 ++-
.../public/triggers/apply_filter_trigger.ts | 6 +-
.../public/triggers/index.ts} | 14 +---
.../components/data_table.tsx | 6 +-
src/plugins/data/server/server.api.md | 4 +-
.../embeddable/search_embeddable.ts | 3 +-
src/plugins/embeddable/common/types.ts | 17 -----
src/plugins/embeddable/public/bootstrap.ts | 26 ++++---
src/plugins/embeddable/public/index.ts | 2 +
.../lib/embeddables/embeddable.test.tsx | 8 ++-
.../add_panel/add_panel_action.test.tsx | 6 +-
.../inspect_panel_action.test.tsx | 3 +-
.../remove_panel_action.test.tsx | 6 +-
.../embeddables/filterable_container.tsx | 6 +-
.../embeddables/filterable_embeddable.tsx | 10 ++-
.../public/lib/triggers/triggers.ts | 26 ++++++-
src/plugins/embeddable/public/mocks.tsx | 3 -
src/plugins/embeddable/public/plugin.tsx | 5 +-
src/plugins/embeddable/public/public.api.md | 71 +++++--------------
.../embeddable/public/tests/container.test.ts | 6 +-
.../public/tests/explicit_input.test.ts | 6 +-
.../embeddable/public/tests/test_plugin.ts | 3 -
.../common/expression_renderers/types.ts | 9 ++-
src/plugins/expressions/public/public.api.md | 4 +-
src/plugins/expressions/server/server.api.md | 4 +-
src/plugins/ui_actions/public/index.ts | 6 --
src/plugins/ui_actions/public/plugin.ts | 12 +---
src/plugins/ui_actions/public/public.api.md | 67 +++--------------
.../ui_actions/public/triggers/index.ts | 3 -
.../public/triggers/row_click_trigger.ts | 5 +-
.../public/triggers/select_range_trigger.ts | 32 ---------
.../ui_actions/public/triggers/trigger.ts | 3 +-
.../public/triggers/trigger_contract.ts | 3 +-
src/plugins/ui_actions/public/types.ts | 8 ---
.../public/components/table_visualization.tsx | 3 +-
.../public/utils/use/use_ui_state.ts | 6 +-
.../public/timeseries_vis_renderer.tsx | 3 +-
.../vis_type_vislib/public/vis_controller.tsx | 6 +-
.../vis_type_vislib/public/vis_wrapper.tsx | 7 +-
.../vis_type_xy/public/vis_component.tsx | 3 +-
.../vis_type_xy/public/vis_renderer.tsx | 3 +-
.../public/embeddable/events.ts | 9 +--
.../public/embeddable/visualize_embeddable.ts | 3 +
.../drilldowns_with_embeddable_example.tsx | 6 +-
.../dashboard_hello_world_drilldown/index.tsx | 6 +-
.../index.tsx | 6 +-
.../drilldown.tsx | 2 +-
.../dashboard_to_discover_drilldown/types.ts | 3 +-
.../button_embeddable/button_embeddable.ts | 7 +-
x-pack/plugins/dashboard_enhanced/kibana.json | 3 +-
.../abstract_dashboard_drilldown/types.ts | 2 +-
.../drilldowns/actions/drilldown_shared.ts | 6 +-
.../embeddable_to_dashboard_drilldown.tsx | 19 +++--
.../explore_data/explore_data_chart_action.ts | 5 +-
.../explore_data_context_menu_action.ts | 21 ++++--
.../discover_enhanced/public/plugin.ts | 7 +-
.../url_drilldown/public/lib/test/data.ts | 4 ++
.../public/lib/url_drilldown.test.ts | 7 +-
.../public/lib/url_drilldown.tsx | 23 ++++--
.../public/lib/url_drilldown_scope.ts | 30 ++++----
.../embeddable_action_storage.test.ts | 2 +-
.../embeddable/embeddable.tsx | 10 ++-
x-pack/plugins/lens/public/types.ts | 5 +-
.../maps/public/embeddable/map_embeddable.tsx | 6 +-
.../plugins/maps/public/embeddable/types.ts | 5 +-
.../translations/translations/ja-JP.json | 12 ++--
.../translations/translations/zh-CN.json | 12 ++--
.../components/action_wizard/test_data.tsx | 7 +-
122 files changed, 437 insertions(+), 581 deletions(-)
create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.apply_filter_trigger.md
delete mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md
delete mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md
create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.select_range_trigger.md
create mode 100644 docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.value_click_trigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.select_range_trigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.value_click_trigger.md
delete mode 100644 docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md
rename src/plugins/{ui_actions => data}/public/triggers/apply_filter_trigger.ts (85%)
rename src/plugins/{ui_actions/public/triggers/value_click_trigger.ts => data/public/triggers/index.ts} (62%)
delete mode 100644 src/plugins/ui_actions/public/triggers/select_range_trigger.ts
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.apply_filter_trigger.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.apply_filter_trigger.md
new file mode 100644
index 0000000000000..aaed18b3b8890
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.apply_filter_trigger.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [APPLY\_FILTER\_TRIGGER](./kibana-plugin-plugins-data-public.apply_filter_trigger.md)
+
+## APPLY\_FILTER\_TRIGGER variable
+
+Signature:
+
+```typescript
+APPLY_FILTER_TRIGGER = "FILTER_TRIGGER"
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md
index 027ae4209b77f..dbeeeb9979aae 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-embeddable?: IEmbeddable;
+embeddable?: unknown;
```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md
index 62817cd0a1e33..2f844b6844645 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md
@@ -14,7 +14,7 @@ export interface ApplyGlobalFilterActionContext
| Property | Type | Description |
| --- | --- | --- |
-| [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md) | IEmbeddable
| |
+| [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md) | unknown
| |
| [filters](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md) | Filter[]
| |
| [timeFieldName](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md) | string
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
index 8de3821161ab4..2040043d4351b 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
@@ -102,6 +102,7 @@
| [ACTION\_GLOBAL\_APPLY\_FILTER](./kibana-plugin-plugins-data-public.action_global_apply_filter.md) | |
| [AggGroupLabels](./kibana-plugin-plugins-data-public.agggrouplabels.md) | |
| [AggGroupNames](./kibana-plugin-plugins-data-public.agggroupnames.md) | |
+| [APPLY\_FILTER\_TRIGGER](./kibana-plugin-plugins-data-public.apply_filter_trigger.md) | |
| [baseFormattersPublic](./kibana-plugin-plugins-data-public.baseformatterspublic.md) | |
| [castEsToKbnFieldTypeName](./kibana-plugin-plugins-data-public.castestokbnfieldtypename.md) | Get the KbnFieldType name for an esType string |
| [connectToQueryState](./kibana-plugin-plugins-data-public.connecttoquerystate.md) | Helper to setup two-way syncing of global data and a state container |
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md
index b90018c3d9cdd..bd90f23b4ab59 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md
@@ -11,7 +11,7 @@ setup(core: CoreSetup, { bfetch, e
__enhance: (enhancements: DataEnhancements) => void;
search: ISearchSetup;
fieldFormats: {
- register: (customFieldFormat: import("../common").FieldFormatInstanceType) => number;
+ register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number;
};
};
```
@@ -29,7 +29,7 @@ setup(core: CoreSetup, { bfetch, e
__enhance: (enhancements: DataEnhancements) => void;
search: ISearchSetup;
fieldFormats: {
- register: (customFieldFormat: import("../common").FieldFormatInstanceType) => number;
+ register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number;
};
}`
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md
index 8a3dbe5a6350c..88f85eb7a7d05 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md
@@ -12,7 +12,7 @@ start(core: CoreStart): {
fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise;
};
indexPatterns: {
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise;
};
search: ISearchStart>;
};
@@ -31,7 +31,7 @@ start(core: CoreStart): {
fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise;
};
indexPatterns: {
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise;
};
search: ISearchStart>;
}`
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md
index 06e51958a2d1e..92926d10a543c 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-embeddable: IEmbeddable;
+embeddable: T;
```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md
index a2c2d9245eabe..753a3ff2ec6ec 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablecontext.md
@@ -7,12 +7,12 @@
Signature:
```typescript
-export interface EmbeddableContext
+export interface EmbeddableContext
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
-| [embeddable](./kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md) | IEmbeddable
| |
+| [embeddable](./kibana-plugin-plugins-embeddable-public.embeddablecontext.embeddable.md) | T
| |
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md
index f36f7b4ee77a4..0f14215ff1309 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md
@@ -16,9 +16,6 @@ export declare type EmbeddableInput = {
enhancements?: SerializableState;
disabledActions?: string[];
disableTriggers?: boolean;
- timeRange?: TimeRange;
- query?: Query;
- filters?: Filter[];
searchSessionId?: string;
};
```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md
deleted file mode 100644
index d3a62657372ac..0000000000000
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableSetupDependencies](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md) > [data](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md)
-
-## EmbeddableSetupDependencies.data property
-
-Signature:
-
-```typescript
-data: DataPublicPluginSetup;
-```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md
index fdd31ca75be2a..957e3f279ff60 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.md
@@ -14,6 +14,5 @@ export interface EmbeddableSetupDependencies
| Property | Type | Description |
| --- | --- | --- |
-| [data](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.data.md) | DataPublicPluginSetup
| |
| [uiActions](./kibana-plugin-plugins-embeddable-public.embeddablesetupdependencies.uiactions.md) | UiActionsSetup
| |
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md
deleted file mode 100644
index 0595609b11e49..0000000000000
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStartDependencies](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md) > [data](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md)
-
-## EmbeddableStartDependencies.data property
-
-Signature:
-
-```typescript
-data: DataPublicPluginStart;
-```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md
index 5a1b5d1e06861..342163ed2e413 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.md
@@ -14,7 +14,6 @@ export interface EmbeddableStartDependencies
| Property | Type | Description |
| --- | --- | --- |
-| [data](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.data.md) | DataPublicPluginStart
| |
| [inspector](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.inspector.md) | InspectorStart
| |
| [uiActions](./kibana-plugin-plugins-embeddable-public.embeddablestartdependencies.uiactions.md) | UiActionsStart
| |
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iscontextmenutriggercontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iscontextmenutriggercontext.md
index 62610624655a1..2f5966f9ba940 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iscontextmenutriggercontext.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iscontextmenutriggercontext.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-isContextMenuTriggerContext: (context: unknown) => context is EmbeddableContext
+isContextMenuTriggerContext: (context: unknown) => context is EmbeddableContext>
```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md
index a6aeba23cd280..b875b1fce4288 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md
@@ -86,6 +86,8 @@
| [PANEL\_NOTIFICATION\_TRIGGER](./kibana-plugin-plugins-embeddable-public.panel_notification_trigger.md) | |
| [panelBadgeTrigger](./kibana-plugin-plugins-embeddable-public.panelbadgetrigger.md) | |
| [panelNotificationTrigger](./kibana-plugin-plugins-embeddable-public.panelnotificationtrigger.md) | |
+| [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-embeddable-public.select_range_trigger.md) | |
+| [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-embeddable-public.value_click_trigger.md) | |
| [withEmbeddableSubscription](./kibana-plugin-plugins-embeddable-public.withembeddablesubscription.md) | |
## Type Aliases
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.select_range_trigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.select_range_trigger.md
new file mode 100644
index 0000000000000..175e3fe947a0f
--- /dev/null
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.select_range_trigger.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-embeddable-public.select_range_trigger.md)
+
+## SELECT\_RANGE\_TRIGGER variable
+
+Signature:
+
+```typescript
+SELECT_RANGE_TRIGGER = "SELECT_RANGE_TRIGGER"
+```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.value_click_trigger.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.value_click_trigger.md
new file mode 100644
index 0000000000000..a85be3142d0f2
--- /dev/null
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.value_click_trigger.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-embeddable-public.value_click_trigger.md)
+
+## VALUE\_CLICK\_TRIGGER variable
+
+Signature:
+
+```typescript
+VALUE_CLICK_TRIGGER = "VALUE_CLICK_TRIGGER"
+```
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md
index 931e474a41006..c22c8bc6b6245 100644
--- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md
@@ -20,6 +20,6 @@ export interface IInterpreterRenderHandlers
| [hasCompatibleActions](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.hascompatibleactions.md) | (event: any) => Promise<boolean>
| |
| [onDestroy](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void
| |
| [reload](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md) | () => void
| |
-| [uiState](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md) | PersistedState
| |
+| [uiState](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md) | unknown
| This uiState interface is actually PersistedState
from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now. |
| [update](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.update.md) | (params: any) => void
| |
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md
index 8d74c8e555fee..461bf861d4d5e 100644
--- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md
@@ -4,8 +4,10 @@
## IInterpreterRenderHandlers.uiState property
+This uiState interface is actually `PersistedState` from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now.
+
Signature:
```typescript
-uiState?: PersistedState;
+uiState?: unknown;
```
diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md
index 273703cacca06..547608f40e6aa 100644
--- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md
+++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md
@@ -20,6 +20,6 @@ export interface IInterpreterRenderHandlers
| [hasCompatibleActions](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.hascompatibleactions.md) | (event: any) => Promise<boolean>
| |
| [onDestroy](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void
| |
| [reload](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md) | () => void
| |
-| [uiState](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md) | PersistedState
| |
+| [uiState](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md) | unknown
| This uiState interface is actually PersistedState
from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now. |
| [update](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.update.md) | (params: any) => void
| |
diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md
index b09433c6454ad..ca1c8eec8c2f7 100644
--- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md
+++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md
@@ -4,8 +4,10 @@
## IInterpreterRenderHandlers.uiState property
+This uiState interface is actually `PersistedState` from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now.
+
Signature:
```typescript
-uiState?: PersistedState;
+uiState?: unknown;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md
deleted file mode 100644
index 94e66bf404f5c..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [APPLY\_FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md)
-
-## APPLY\_FILTER\_TRIGGER variable
-
-Signature:
-
-```typescript
-APPLY_FILTER_TRIGGER = "FILTER_TRIGGER"
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md
deleted file mode 100644
index e1fb6d342457e..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [applyFilterTrigger](./kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md)
-
-## applyFilterTrigger variable
-
-Signature:
-
-```typescript
-applyFilterTrigger: Trigger<'FILTER_TRIGGER'>
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md
index fd1ea7df4fb74..76e347bddd168 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md
@@ -41,14 +41,8 @@
| [ACTION\_VISUALIZE\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_field.md) | |
| [ACTION\_VISUALIZE\_GEO\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_geo_field.md) | |
| [ACTION\_VISUALIZE\_LENS\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_lens_field.md) | |
-| [APPLY\_FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md) | |
-| [applyFilterTrigger](./kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md) | |
| [ROW\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.row_click_trigger.md) | |
| [rowClickTrigger](./kibana-plugin-plugins-ui_actions-public.rowclicktrigger.md) | |
-| [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.select_range_trigger.md) | |
-| [selectRangeTrigger](./kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md) | |
-| [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.value_click_trigger.md) | |
-| [valueClickTrigger](./kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md) | |
| [VISUALIZE\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.visualize_field_trigger.md) | |
| [VISUALIZE\_GEO\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.visualize_geo_field_trigger.md) | |
| [visualizeFieldTrigger](./kibana-plugin-plugins-ui_actions-public.visualizefieldtrigger.md) | |
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.embeddable.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.embeddable.md
index e8baf44ff9cbc..a75637e8ea9d3 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.embeddable.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.embeddable.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-embeddable?: IEmbeddable;
+embeddable?: unknown;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.md
index 74b55d85f10e3..b69734cfc3233 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.rowclickcontext.md
@@ -15,5 +15,5 @@ export interface RowClickContext
| Property | Type | Description |
| --- | --- | --- |
| [data](./kibana-plugin-plugins-ui_actions-public.rowclickcontext.data.md) | {
rowIndex: number;
table: Datatable;
columns?: string[];
}
| |
-| [embeddable](./kibana-plugin-plugins-ui_actions-public.rowclickcontext.embeddable.md) | IEmbeddable
| |
+| [embeddable](./kibana-plugin-plugins-ui_actions-public.rowclickcontext.embeddable.md) | unknown
| |
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.select_range_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.select_range_trigger.md
deleted file mode 100644
index fd784ff17fa84..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.select_range_trigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.select_range_trigger.md)
-
-## SELECT\_RANGE\_TRIGGER variable
-
-Signature:
-
-```typescript
-SELECT_RANGE_TRIGGER = "SELECT_RANGE_TRIGGER"
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md
deleted file mode 100644
index 0d9fa2d83ee57..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [selectRangeTrigger](./kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md)
-
-## selectRangeTrigger variable
-
-Signature:
-
-```typescript
-selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'>
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.id.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.id.md
index 426f17f9a0352..5603c852ad39d 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.id.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.id.md
@@ -4,7 +4,7 @@
## Trigger.id property
-Unique name of the trigger as identified in `ui_actions` plugin trigger registry, such as "SELECT\_RANGE\_TRIGGER" or "VALUE\_CLICK\_TRIGGER".
+Unique name of the trigger as identified in `ui_actions` plugin trigger registry.
Signature:
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.md
index b69bba892f475..ed76cfea97684 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.md
@@ -21,6 +21,6 @@ export interface Trigger
| Property | Type | Description |
| --- | --- | --- |
| [description](./kibana-plugin-plugins-ui_actions-public.trigger.description.md) | string
| A longer user friendly description of the trigger. |
-| [id](./kibana-plugin-plugins-ui_actions-public.trigger.id.md) | ID
| Unique name of the trigger as identified in ui_actions
plugin trigger registry, such as "SELECT\_RANGE\_TRIGGER" or "VALUE\_CLICK\_TRIGGER". |
+| [id](./kibana-plugin-plugins-ui_actions-public.trigger.id.md) | ID
| Unique name of the trigger as identified in ui_actions
plugin trigger registry. |
| [title](./kibana-plugin-plugins-ui_actions-public.trigger.title.md) | string
| User friendly name of the trigger. |
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md
deleted file mode 100644
index 0ccf8aa3d7415..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md)
-
-## TriggerContextMapping.FILTER\_TRIGGER property
-
-Signature:
-
-```typescript
-[APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext;
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md
index 2f0d22cf6dd74..da7a7a8bfe645 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md
@@ -15,10 +15,7 @@ export interface TriggerContextMapping
| Property | Type | Description |
| --- | --- | --- |
| [""](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.__.md) | TriggerContext
| |
-| [FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md) | ApplyGlobalFilterActionContext
| |
| [ROW\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.row_click_trigger.md) | RowClickContext
| |
-| [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md) | RangeSelectContext
| |
-| [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md) | ValueClickContext
| |
| [VISUALIZE\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_field_trigger.md) | VisualizeFieldContext
| |
| [VISUALIZE\_GEO\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_geo_field_trigger.md) | VisualizeFieldContext
| |
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md
deleted file mode 100644
index c5ef6843390b3..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md)
-
-## TriggerContextMapping.SELECT\_RANGE\_TRIGGER property
-
-Signature:
-
-```typescript
-[SELECT_RANGE_TRIGGER]: RangeSelectContext;
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md
deleted file mode 100644
index 129144a66cee5..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md)
-
-## TriggerContextMapping.VALUE\_CLICK\_TRIGGER property
-
-Signature:
-
-```typescript
-[VALUE_CLICK_TRIGGER]: ValueClickContext;
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md
index ca999322b7a56..f29d487d774e0 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md
@@ -11,5 +11,5 @@
Signature:
```typescript
-readonly addTriggerAction: (triggerId: T, action: ActionDefinition | Action) => void;
+readonly addTriggerAction: (triggerId: T, action: ActionDefinition | Action) => void;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md
index e95e7e1eb38b6..1ebb30c49c0b3 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-readonly attachAction: (triggerId: T, actionId: string) => void;
+readonly attachAction: (triggerId: T, actionId: string) => void;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md
index 8e7fb8b8bbf29..b20f08520c43d 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md
@@ -12,5 +12,5 @@
Signature:
```typescript
-readonly executeTriggerActions: (triggerId: T, context: TriggerContext) => Promise;
+readonly executeTriggerActions: (triggerId: T, context: TriggerContext) => Promise;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md
index d540de7637441..300c46a47c47f 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">;
+readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md
index b996620686a28..95b737a8d6cae 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-readonly getTrigger: (triggerId: T) => TriggerContract;
+readonly getTrigger: (triggerId: T) => TriggerContract;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md
index f94b34ecc2d90..27c1b1eb48f16 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-readonly getTriggerActions: (triggerId: T) => Action[];
+readonly getTriggerActions: (triggerId: T) => Action[];
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md
index dff958608ef9e..edb7d2d3a1551 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>;
+readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md
index e35eb503ab62b..4fe8431770dea 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md
@@ -21,19 +21,19 @@ export declare class UiActionsService
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [actions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.actions.md) | | ActionRegistry
| |
-| [addTriggerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "ROW_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, action: ActionDefinition<TriggerContextMapping[T]> | Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">) => void
| addTriggerAction
is similar to attachAction
as it attaches action to a trigger, but it also registers the action, if it has not been registered, yet.addTriggerAction
also infers better typing of the action
argument. |
-| [attachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "ROW_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, actionId: string) => void
| |
+| [addTriggerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md) | | <T extends "" | "ROW_CLICK_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "FILTER_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER">(triggerId: T, action: ActionDefinition<TriggerContextMapping[T]> | Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">) => void
| addTriggerAction
is similar to attachAction
as it attaches action to a trigger, but it also registers the action, if it has not been registered, yet.addTriggerAction
also infers better typing of the action
argument. |
+| [attachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md) | | <T extends "" | "ROW_CLICK_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "FILTER_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER">(triggerId: T, actionId: string) => void
| |
| [clear](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.clear.md) | | () => void
| Removes all registered triggers and actions. |
| [detachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.detachaction.md) | | (triggerId: TriggerId, actionId: string) => void
| |
-| [executeTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "ROW_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, context: TriggerContext<T>) => Promise<void>
| |
+| [executeTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md) | | <T extends "" | "ROW_CLICK_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "FILTER_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER">(triggerId: T, context: TriggerContext<T>) => Promise<void>
| |
| [executionService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executionservice.md) | | UiActionsExecutionService
| |
| [fork](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.fork.md) | | () => UiActionsService
| "Fork" a separate instance of UiActionsService
that inherits all existing triggers and actions, but going forward all new triggers and actions added to this instance of UiActionsService
are only available within this instance. |
-| [getAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md) | | <T extends ActionDefinition<{}>>(id: string) => Action<ActionContext<T>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">
| |
-| [getTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "ROW_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T) => TriggerContract<T>
| |
-| [getTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "ROW_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T) => Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">[]
| |
-| [getTriggerCompatibleActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "ROW_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, context: TriggerContextMapping[T]) => Promise<Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">[]>
| |
+| [getAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md) | | <T extends ActionDefinition<{}>>(id: string) => Action<ActionContext<T>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">
| |
+| [getTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md) | | <T extends "" | "ROW_CLICK_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "FILTER_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER">(triggerId: T) => TriggerContract<T>
| |
+| [getTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md) | | <T extends "" | "ROW_CLICK_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "FILTER_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER">(triggerId: T) => Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">[]
| |
+| [getTriggerCompatibleActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md) | | <T extends "" | "ROW_CLICK_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "FILTER_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER">(triggerId: T, context: TriggerContextMapping[T]) => Promise<Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">[]>
| |
| [hasAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.hasaction.md) | | (actionId: string) => boolean
| |
-| [registerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md) | | <A extends ActionDefinition<{}>>(definition: A) => Action<ActionContext<A>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">
| |
+| [registerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md) | | <A extends ActionDefinition<{}>>(definition: A) => Action<ActionContext<A>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">
| |
| [registerTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registertrigger.md) | | (trigger: Trigger) => void
| |
| [triggers](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggers.md) | | TriggerRegistry
| |
| [triggerToActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggertoactions.md) | | TriggerToActionsRegistry
| |
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md
index 6f03777e14552..dee5f75f7c074 100644
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">;
+readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">;
```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.value_click_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.value_click_trigger.md
deleted file mode 100644
index bd8d4dc50b8fd..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.value_click_trigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.value_click_trigger.md)
-
-## VALUE\_CLICK\_TRIGGER variable
-
-Signature:
-
-```typescript
-VALUE_CLICK_TRIGGER = "VALUE_CLICK_TRIGGER"
-```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md
deleted file mode 100644
index 5c4fc284d83b1..0000000000000
--- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [valueClickTrigger](./kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md)
-
-## valueClickTrigger variable
-
-Signature:
-
-```typescript
-valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'>
-```
diff --git a/src/dev/run_find_plugins_with_circular_deps.ts b/src/dev/run_find_plugins_with_circular_deps.ts
index f4662820a1fb0..1a087e2a01fb2 100644
--- a/src/dev/run_find_plugins_with_circular_deps.ts
+++ b/src/dev/run_find_plugins_with_circular_deps.ts
@@ -31,13 +31,8 @@ interface Options {
type CircularDepList = Set;
const allowedList: CircularDepList = new Set([
- 'src/plugins/charts -> src/plugins/expressions',
+ 'src/plugins/charts -> src/plugins/discover',
'src/plugins/charts -> src/plugins/vis_default_editor',
- 'src/plugins/data -> src/plugins/embeddable',
- 'src/plugins/data -> src/plugins/expressions',
- 'src/plugins/data -> src/plugins/ui_actions',
- 'src/plugins/embeddable -> src/plugins/ui_actions',
- 'src/plugins/expressions -> src/plugins/visualizations',
'src/plugins/vis_default_editor -> src/plugins/visualizations',
'src/plugins/vis_default_editor -> src/plugins/visualize',
'src/plugins/visualizations -> src/plugins/visualize',
diff --git a/src/plugins/data/public/actions/apply_filter_action.ts b/src/plugins/data/public/actions/apply_filter_action.ts
index 944da72bd11d1..84ce5b0382624 100644
--- a/src/plugins/data/public/actions/apply_filter_action.ts
+++ b/src/plugins/data/public/actions/apply_filter_action.ts
@@ -22,7 +22,6 @@ import { toMountPoint } from '../../../kibana_react/public';
import { ActionByType, createAction, IncompatibleActionError } from '../../../ui_actions/public';
import { getOverlays, getIndexPatterns } from '../services';
import { applyFiltersPopover } from '../ui/apply_filters';
-import type { IEmbeddable } from '../../../embeddable/public';
import { Filter, FilterManager, TimefilterContract, esFilters } from '..';
export const ACTION_GLOBAL_APPLY_FILTER = 'ACTION_GLOBAL_APPLY_FILTER';
@@ -30,7 +29,9 @@ export const ACTION_GLOBAL_APPLY_FILTER = 'ACTION_GLOBAL_APPLY_FILTER';
export interface ApplyGlobalFilterActionContext {
filters: Filter[];
timeFieldName?: string;
- embeddable?: IEmbeddable;
+ // Need to make this unknown to prevent circular dependencies.
+ // Apps using this property will need to cast to `IEmbeddable`.
+ embeddable?: unknown;
}
async function isCompatible(context: ApplyGlobalFilterActionContext) {
diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
index 2d7aeff79a689..2b0911b72abd5 100644
--- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
@@ -19,12 +19,20 @@
import { last } from 'lodash';
import moment from 'moment';
+import { Datatable } from 'src/plugins/expressions';
import { esFilters, IFieldType, RangeFilterParams } from '../../../public';
import { getIndexPatterns, getSearchService } from '../../../public/services';
-import { RangeSelectContext } from '../../../../embeddable/public';
import { AggConfigSerialized } from '../../../common/search/aggs';
-export async function createFiltersFromRangeSelectAction(event: RangeSelectContext['data']) {
+/** @internal */
+export interface RangeSelectDataContext {
+ table: Datatable;
+ column: number;
+ range: number[];
+ timeFieldName?: string;
+}
+
+export async function createFiltersFromRangeSelectAction(event: RangeSelectDataContext) {
const column: Record = event.table.columns[event.column];
if (!column || !column.meta) {
diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts
index 23d2ab080d75e..04801a5ee1cea 100644
--- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts
@@ -25,8 +25,10 @@ import {
} from '../../../public';
import { dataPluginMock } from '../../../public/mocks';
import { setIndexPatterns, setSearchService } from '../../../public/services';
-import { createFiltersFromValueClickAction } from './create_filters_from_value_click';
-import { ValueClickContext } from '../../../../embeddable/public';
+import {
+ createFiltersFromValueClickAction,
+ ValueClickDataContext,
+} from './create_filters_from_value_click';
const mockField = {
name: 'bytes',
@@ -34,7 +36,7 @@ const mockField = {
};
describe('createFiltersFromValueClick', () => {
- let dataPoints: ValueClickContext['data']['data'];
+ let dataPoints: ValueClickDataContext['data'];
beforeEach(() => {
dataPoints = [
diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
index ce7ecf434056a..30fef7e3a7c66 100644
--- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
@@ -20,9 +20,20 @@
import { Datatable } from '../../../../../plugins/expressions/public';
import { esFilters, Filter } from '../../../public';
import { getIndexPatterns, getSearchService } from '../../../public/services';
-import { ValueClickContext } from '../../../../embeddable/public';
import { AggConfigSerialized } from '../../../common/search/aggs';
+/** @internal */
+export interface ValueClickDataContext {
+ data: Array<{
+ table: Pick;
+ column: number;
+ row: number;
+ value: any;
+ }>;
+ timeFieldName?: string;
+ negate?: boolean;
+}
+
/**
* For terms aggregations on `__other__` buckets, this assembles a list of applicable filter
* terms based on a specific cell in the tabified data.
@@ -120,7 +131,7 @@ const createFilter = async (
export const createFiltersFromValueClickAction = async ({
data,
negate,
-}: ValueClickContext['data']) => {
+}: ValueClickDataContext) => {
const filters: Filter[] = [];
await Promise.all(
diff --git a/src/plugins/data/public/actions/select_range_action.ts b/src/plugins/data/public/actions/select_range_action.ts
index 1781da980dc30..3b84523d782f6 100644
--- a/src/plugins/data/public/actions/select_range_action.ts
+++ b/src/plugins/data/public/actions/select_range_action.ts
@@ -17,16 +17,22 @@
* under the License.
*/
-import {
- ActionByType,
- APPLY_FILTER_TRIGGER,
- createAction,
- UiActionsStart,
-} from '../../../../plugins/ui_actions/public';
+import { Datatable } from 'src/plugins/expressions/public';
+import { ActionByType, createAction, UiActionsStart } from '../../../../plugins/ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../triggers';
import { createFiltersFromRangeSelectAction } from './filters/create_filters_from_range_select';
-import type { RangeSelectContext } from '../../../embeddable/public';
-export type SelectRangeActionContext = RangeSelectContext;
+export interface SelectRangeActionContext {
+ // Need to make this unknown to prevent circular dependencies.
+ // Apps using this property will need to cast to `IEmbeddable`.
+ embeddable?: unknown;
+ data: {
+ table: Datatable;
+ column: number;
+ range: number[];
+ timeFieldName?: string;
+ };
+}
export const ACTION_SELECT_RANGE = 'ACTION_SELECT_RANGE';
diff --git a/src/plugins/data/public/actions/value_click_action.ts b/src/plugins/data/public/actions/value_click_action.ts
index 81e62380eacfb..8f207e94e8fbe 100644
--- a/src/plugins/data/public/actions/value_click_action.ts
+++ b/src/plugins/data/public/actions/value_click_action.ts
@@ -17,19 +17,31 @@
* under the License.
*/
-import {
- ActionByType,
- APPLY_FILTER_TRIGGER,
- createAction,
- UiActionsStart,
-} from '../../../../plugins/ui_actions/public';
+import { Datatable } from 'src/plugins/expressions/public';
+import { ActionByType, createAction, UiActionsStart } from '../../../../plugins/ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../triggers';
import { createFiltersFromValueClickAction } from './filters/create_filters_from_value_click';
import type { Filter } from '../../common/es_query/filters';
-import type { ValueClickContext } from '../../../embeddable/public';
export type ValueClickActionContext = ValueClickContext;
export const ACTION_VALUE_CLICK = 'ACTION_VALUE_CLICK';
+export interface ValueClickContext {
+ // Need to make this unknown to prevent circular dependencies.
+ // Apps using this property will need to cast to `IEmbeddable`.
+ embeddable?: unknown;
+ data: {
+ data: Array<{
+ table: Pick;
+ column: number;
+ row: number;
+ value: any;
+ }>;
+ timeFieldName?: string;
+ negate?: boolean;
+ };
+}
+
export function createValueClickAction(
getStartServices: () => { uiActions: UiActionsStart }
): ActionByType {
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index 3dda04d738c96..7b15e2576e704 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -483,6 +483,7 @@ export {
export { isTimeRange, isQuery, isFilter, isFilters } from '../common';
export { ACTION_GLOBAL_APPLY_FILTER, ApplyGlobalFilterActionContext } from './actions';
+export { APPLY_FILTER_TRIGGER } from './triggers';
/*
* Plugin setup
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index eb3a053b78a2d..c60a1efabf987 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -48,11 +48,6 @@ import {
setUiSettings,
} from './services';
import { createSearchBar } from './ui/search_bar/create_search_bar';
-import {
- SELECT_RANGE_TRIGGER,
- VALUE_CLICK_TRIGGER,
- APPLY_FILTER_TRIGGER,
-} from '../../ui_actions/public';
import {
ACTION_GLOBAL_APPLY_FILTER,
createFilterAction,
@@ -66,13 +61,18 @@ import {
createValueClickAction,
createSelectRangeAction,
} from './actions';
-
+import { APPLY_FILTER_TRIGGER, applyFilterTrigger } from './triggers';
import { SavedObjectsClientPublicToCommon } from './index_patterns';
import { getIndexPatternLoad } from './index_patterns/expressions';
import { UsageCollectionSetup } from '../../usage_collection/public';
import { getTableViewDescription } from './utils/table_inspector_view';
+import { TriggerId } from '../../ui_actions/public';
declare module '../../ui_actions/public' {
+ export interface TriggerContextMapping {
+ [APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext;
+ }
+
export interface ActionContextMapping {
[ACTION_GLOBAL_APPLY_FILTER]: ApplyGlobalFilterActionContext;
[ACTION_SELECT_RANGE]: SelectRangeActionContext;
@@ -118,19 +118,21 @@ export class DataPublicPlugin
storage: this.storage,
});
+ uiActions.registerTrigger(applyFilterTrigger);
+
uiActions.registerAction(
createFilterAction(queryService.filterManager, queryService.timefilter.timefilter)
);
uiActions.addTriggerAction(
- SELECT_RANGE_TRIGGER,
+ 'SELECT_RANGE_TRIGGER' as TriggerId,
createSelectRangeAction(() => ({
uiActions: startServices().plugins.uiActions,
}))
);
uiActions.addTriggerAction(
- VALUE_CLICK_TRIGGER,
+ 'VALUE_CLICK_TRIGGER' as TriggerId,
createValueClickAction(() => ({
uiActions: startServices().plugins.uiActions,
}))
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index e5df6d860b404..120540ddb92ec 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -464,14 +464,17 @@ export type AggsStart = Assign;
+// Warning: (ae-missing-release-tag) "APPLY_FILTER_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const APPLY_FILTER_TRIGGER = "FILTER_TRIGGER";
+
// Warning: (ae-missing-release-tag) "ApplyGlobalFilterActionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface ApplyGlobalFilterActionContext {
- // Warning: (ae-forgotten-export) The symbol "IEmbeddable" needs to be exported by the entry point index.d.ts
- //
// (undocumented)
- embeddable?: IEmbeddable;
+ embeddable?: unknown;
// (undocumented)
filters: Filter[];
// (undocumented)
diff --git a/src/plugins/ui_actions/public/triggers/apply_filter_trigger.ts b/src/plugins/data/public/triggers/apply_filter_trigger.ts
similarity index 85%
rename from src/plugins/ui_actions/public/triggers/apply_filter_trigger.ts
rename to src/plugins/data/public/triggers/apply_filter_trigger.ts
index aa54706476a8f..816c1737608da 100644
--- a/src/plugins/ui_actions/public/triggers/apply_filter_trigger.ts
+++ b/src/plugins/data/public/triggers/apply_filter_trigger.ts
@@ -18,15 +18,15 @@
*/
import { i18n } from '@kbn/i18n';
-import { Trigger } from '.';
+import { Trigger } from '../../../ui_actions/public';
export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER';
export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'> = {
id: APPLY_FILTER_TRIGGER,
- title: i18n.translate('uiActions.triggers.applyFilterTitle', {
+ title: i18n.translate('data.triggers.applyFilterTitle', {
defaultMessage: 'Apply filter',
}),
- description: i18n.translate('uiActions.triggers.applyFilterDescription', {
+ description: i18n.translate('data.triggers.applyFilterDescription', {
defaultMessage: 'When kibana filter is applied. Could be a single value or a range filter.',
}),
};
diff --git a/src/plugins/ui_actions/public/triggers/value_click_trigger.ts b/src/plugins/data/public/triggers/index.ts
similarity index 62%
rename from src/plugins/ui_actions/public/triggers/value_click_trigger.ts
rename to src/plugins/data/public/triggers/index.ts
index f1aff6322522a..36a38ae76bc0e 100644
--- a/src/plugins/ui_actions/public/triggers/value_click_trigger.ts
+++ b/src/plugins/data/public/triggers/index.ts
@@ -17,16 +17,4 @@
* under the License.
*/
-import { i18n } from '@kbn/i18n';
-import { Trigger } from '.';
-
-export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER';
-export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> = {
- id: VALUE_CLICK_TRIGGER,
- title: i18n.translate('uiActions.triggers.valueClickTitle', {
- defaultMessage: 'Single click',
- }),
- description: i18n.translate('uiActions.triggers.valueClickDescription', {
- defaultMessage: 'A data point click on the visualization',
- }),
-};
+export * from './apply_filter_trigger';
diff --git a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx
index f4d1a8988da78..f842568859fc2 100644
--- a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx
+++ b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx
@@ -38,7 +38,7 @@ import { DataViewRow, DataViewColumn } from '../types';
import { IUiSettingsClient } from '../../../../../../core/public';
import { Datatable, DatatableColumn } from '../../../../../expressions/public';
import { FieldFormatsStart } from '../../../field_formats';
-import { UiActionsStart } from '../../../../../ui_actions/public';
+import { TriggerId, UiActionsStart } from '../../../../../ui_actions/public';
interface DataTableFormatState {
columns: DataViewColumn[];
@@ -112,7 +112,7 @@ export class DataTableFormat extends Component {
const value = table.rows[rowIndex][column.id];
const eventData = { table, column: columnIndex, row: rowIndex, value };
- uiActions.executeTriggerActions('VALUE_CLICK_TRIGGER', {
+ uiActions.executeTriggerActions('VALUE_CLICK_TRIGGER' as TriggerId, {
data: { data: [eventData] },
});
}}
@@ -145,7 +145,7 @@ export class DataTableFormat extends Component {
const value = table.rows[rowIndex][column.id];
const eventData = { table, column: columnIndex, row: rowIndex, value };
- uiActions.executeTriggerActions('VALUE_CLICK_TRIGGER', {
+ uiActions.executeTriggerActions('VALUE_CLICK_TRIGGER' as TriggerId, {
data: { data: [eventData], negate: true },
});
}}
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 4d24e6d1afd49..60d5e167921cc 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -1115,7 +1115,7 @@ export class Plugin implements Plugin_2 void;
search: ISearchSetup;
fieldFormats: {
- register: (customFieldFormat: import("../common").FieldFormatInstanceType) => number;
+ register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number;
};
};
// (undocumented)
@@ -1124,7 +1124,7 @@ export class Plugin implements Plugin_2 Promise;
};
indexPatterns: {
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise;
};
search: ISearchStart>;
};
diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
index ff408ba431ed9..d0c3907d31242 100644
--- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts
+++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
@@ -21,9 +21,10 @@ import angular from 'angular';
import _ from 'lodash';
import { Subscription } from 'rxjs';
import { i18n } from '@kbn/i18n';
-import { UiActionsStart, APPLY_FILTER_TRIGGER } from '../../../../ui_actions/public';
+import { UiActionsStart } from '../../../../ui_actions/public';
import { RequestAdapter, Adapters } from '../../../../inspector/public';
import {
+ APPLY_FILTER_TRIGGER,
esFilters,
Filter,
TimeRange,
diff --git a/src/plugins/embeddable/common/types.ts b/src/plugins/embeddable/common/types.ts
index 8965446cc85fa..d893724f616d2 100644
--- a/src/plugins/embeddable/common/types.ts
+++ b/src/plugins/embeddable/common/types.ts
@@ -18,8 +18,6 @@
*/
import { PersistableStateService, SerializableState } from '../../kibana_utils/common';
-import { Query, TimeRange } from '../../data/common/query';
-import { Filter } from '../../data/common/es_query/filters';
export enum ViewMode {
EDIT = 'edit',
@@ -53,21 +51,6 @@ export type EmbeddableInput = {
*/
disableTriggers?: boolean;
- /**
- * Time range of the chart.
- */
- timeRange?: TimeRange;
-
- /**
- * Visualization query string used to narrow down results.
- */
- query?: Query;
-
- /**
- * Visualization filters used to narrow down results.
- */
- filters?: Filter[];
-
/**
* Search session id to group searches
*/
diff --git a/src/plugins/embeddable/public/bootstrap.ts b/src/plugins/embeddable/public/bootstrap.ts
index 5c95214ef591b..efaff42c19e2f 100644
--- a/src/plugins/embeddable/public/bootstrap.ts
+++ b/src/plugins/embeddable/public/bootstrap.ts
@@ -18,18 +18,24 @@
*/
import { UiActionsSetup } from '../../ui_actions/public';
import {
- contextMenuTrigger,
- panelBadgeTrigger,
- EmbeddableContext,
- CONTEXT_MENU_TRIGGER,
- PANEL_BADGE_TRIGGER,
ACTION_ADD_PANEL,
ACTION_CUSTOMIZE_PANEL,
- ACTION_INSPECT_PANEL,
- REMOVE_PANEL_ACTION,
ACTION_EDIT_PANEL,
- panelNotificationTrigger,
+ ACTION_INSPECT_PANEL,
+ CONTEXT_MENU_TRIGGER,
+ contextMenuTrigger,
+ EmbeddableContext,
+ PANEL_BADGE_TRIGGER,
PANEL_NOTIFICATION_TRIGGER,
+ panelBadgeTrigger,
+ panelNotificationTrigger,
+ RangeSelectContext,
+ REMOVE_PANEL_ACTION,
+ SELECT_RANGE_TRIGGER,
+ selectRangeTrigger,
+ ValueClickContext,
+ VALUE_CLICK_TRIGGER,
+ valueClickTrigger,
} from './lib';
declare module '../../ui_actions/public' {
@@ -37,6 +43,8 @@ declare module '../../ui_actions/public' {
[CONTEXT_MENU_TRIGGER]: EmbeddableContext;
[PANEL_BADGE_TRIGGER]: EmbeddableContext;
[PANEL_NOTIFICATION_TRIGGER]: EmbeddableContext;
+ [SELECT_RANGE_TRIGGER]: RangeSelectContext;
+ [VALUE_CLICK_TRIGGER]: ValueClickContext;
}
export interface ActionContextMapping {
@@ -56,4 +64,6 @@ export const bootstrap = (uiActions: UiActionsSetup) => {
uiActions.registerTrigger(contextMenuTrigger);
uiActions.registerTrigger(panelBadgeTrigger);
uiActions.registerTrigger(panelNotificationTrigger);
+ uiActions.registerTrigger(selectRangeTrigger);
+ uiActions.registerTrigger(valueClickTrigger);
};
diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts
index 0fc7c7965010b..d537ef2bd0c5c 100644
--- a/src/plugins/embeddable/public/index.ts
+++ b/src/plugins/embeddable/public/index.ts
@@ -65,6 +65,8 @@ export {
PanelNotFoundError,
PanelState,
PropertySpec,
+ SELECT_RANGE_TRIGGER,
+ VALUE_CLICK_TRIGGER,
ViewMode,
withEmbeddableSubscription,
SavedObjectEmbeddableInput,
diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable.test.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable.test.tsx
index c7c71656bceb2..c0e13a84066ca 100644
--- a/src/plugins/embeddable/public/lib/embeddables/embeddable.test.tsx
+++ b/src/plugins/embeddable/public/lib/embeddables/embeddable.test.tsx
@@ -24,8 +24,10 @@ import { Embeddable } from './embeddable';
import { EmbeddableOutput, EmbeddableInput } from './i_embeddable';
import { ViewMode } from '../types';
import { ContactCardEmbeddable } from '../test_samples/embeddables/contact_card/contact_card_embeddable';
-import { FilterableEmbeddable } from '../test_samples/embeddables/filterable_embeddable';
-import type { Filter } from '../../../../data/public';
+import {
+ MockFilter,
+ FilterableEmbeddable,
+} from '../test_samples/embeddables/filterable_embeddable';
class TestClass {
constructor() {}
@@ -83,7 +85,7 @@ test('Embeddable reload is called if lastReloadRequest input time changes', asyn
test('Embeddable reload is called if lastReloadRequest input time changed and new input is used', async () => {
const hello = new FilterableEmbeddable({ id: '123', filters: [], lastReloadRequestTime: 0 });
- const aFilter = ({} as unknown) as Filter;
+ const aFilter = ({} as unknown) as MockFilter;
hello.reload = jest.fn(() => {
// when reload is called embeddable already has new input
expect(hello.getInput().filters).toEqual([aFilter]);
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx
index 0361939fd07e6..cb78fac5471a9 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx
@@ -20,6 +20,7 @@
import { ViewMode, EmbeddableOutput, isErrorEmbeddable } from '../../../../';
import { AddPanelAction } from './add_panel_action';
import {
+ MockFilter,
FILTERABLE_EMBEDDABLE,
FilterableEmbeddable,
FilterableEmbeddableInput,
@@ -28,7 +29,6 @@ import { FilterableEmbeddableFactory } from '../../../../test_samples/embeddable
import { FilterableContainer } from '../../../../test_samples/embeddables/filterable_container';
import { coreMock } from '../../../../../../../../core/public/mocks';
import { ContactCardEmbeddable } from '../../../../test_samples';
-import { esFilters, Filter } from '../../../../../../../../plugins/data/public';
import { EmbeddableStart } from '../../../../../plugin';
import { embeddablePluginMock } from '../../../../../mocks';
import { defaultTrigger } from '../../../../../../../ui_actions/public/triggers';
@@ -51,8 +51,8 @@ beforeEach(async () => {
() => null
);
- const derivedFilter: Filter = {
- $state: { store: esFilters.FilterStateStore.APP_STATE },
+ const derivedFilter: MockFilter = {
+ $state: { store: 'appState' },
meta: { disabled: false, alias: 'name', negate: false },
query: { match: {} },
};
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx
index eb83641448986..b784a46127305 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx
@@ -29,7 +29,6 @@ import {
import { inspectorPluginMock } from '../../../../../../../plugins/inspector/public/mocks';
import { EmbeddableOutput, isErrorEmbeddable, ErrorEmbeddable } from '../../../embeddables';
import { of } from '../../../../tests/helpers';
-import { esFilters } from '../../../../../../../plugins/data/public';
import { embeddablePluginMock } from '../../../../mocks';
import { EmbeddableStart } from '../../../../plugin';
@@ -43,7 +42,7 @@ const setupTests = async () => {
panels: {},
filters: [
{
- $state: { store: esFilters.FilterStateStore.APP_STATE },
+ $state: { store: 'appState' },
meta: { disabled: false, alias: 'name', negate: false },
query: { match: {} },
},
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx
index dea4a88bda082..ce6a1cc20fc4d 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx
@@ -21,6 +21,7 @@ import { EmbeddableOutput, isErrorEmbeddable } from '../../../';
import { RemovePanelAction } from './remove_panel_action';
import { EmbeddableStart } from '../../../../plugin';
import {
+ MockFilter,
FILTERABLE_EMBEDDABLE,
FilterableEmbeddable,
FilterableEmbeddableInput,
@@ -29,7 +30,6 @@ import { FilterableEmbeddableFactory } from '../../../test_samples/embeddables/f
import { FilterableContainer } from '../../../test_samples/embeddables/filterable_container';
import { ViewMode } from '../../../types';
import { ContactCardEmbeddable } from '../../../test_samples/embeddables/contact_card/contact_card_embeddable';
-import { esFilters, Filter } from '../../../../../../../plugins/data/public';
import { embeddablePluginMock } from '../../../../mocks';
const { setup, doStart } = embeddablePluginMock.createInstance();
@@ -39,8 +39,8 @@ let container: FilterableContainer;
let embeddable: FilterableEmbeddable;
beforeEach(async () => {
- const derivedFilter: Filter = {
- $state: { store: esFilters.FilterStateStore.APP_STATE },
+ const derivedFilter: MockFilter = {
+ $state: { store: 'appState' },
meta: { disabled: false, alias: 'name', negate: false },
query: { match: {} },
};
diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx
index db71b94ac855f..23696612fd82a 100644
--- a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx
+++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx
@@ -18,13 +18,13 @@
*/
import { Container, ContainerInput } from '../../containers';
-import { Filter } from '../../../../../data/public';
import { EmbeddableStart } from '../../../plugin';
+import { MockFilter } from './filterable_embeddable';
export const FILTERABLE_CONTAINER = 'FILTERABLE_CONTAINER';
export interface FilterableContainerInput extends ContainerInput {
- filters: Filter[];
+ filters: MockFilter[];
}
/**
@@ -33,7 +33,7 @@ export interface FilterableContainerInput extends ContainerInput {
* here instead
*/
export type InheritedChildrenInput = {
- filters: Filter[];
+ filters: MockFilter[];
id?: string;
};
diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx
index fd6ea3b9aa2b2..99d21198dd151 100644
--- a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx
+++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx
@@ -19,12 +19,18 @@
import { IContainer } from '../../containers';
import { EmbeddableOutput, EmbeddableInput, Embeddable } from '../../embeddables';
-import { Filter } from '../../../../../data/public';
+
+/** @internal */
+export interface MockFilter {
+ $state?: any;
+ meta: any;
+ query?: any;
+}
export const FILTERABLE_EMBEDDABLE = 'FILTERABLE_EMBEDDABLE';
export interface FilterableEmbeddableInput extends EmbeddableInput {
- filters: Filter[];
+ filters: MockFilter[];
}
export class FilterableEmbeddable extends Embeddable {
diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts
index c3b1496b8eca8..d9fb063a5bb56 100644
--- a/src/plugins/embeddable/public/lib/triggers/triggers.ts
+++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts
@@ -22,8 +22,8 @@ import { Datatable } from '../../../../expressions';
import { Trigger, RowClickContext } from '../../../../ui_actions/public';
import { IEmbeddable } from '..';
-export interface EmbeddableContext {
- embeddable: IEmbeddable;
+export interface EmbeddableContext {
+ embeddable: T;
}
export interface ValueClickContext {
@@ -88,6 +88,28 @@ export const panelNotificationTrigger: Trigger<'PANEL_NOTIFICATION_TRIGGER'> = {
}),
};
+export const SELECT_RANGE_TRIGGER = 'SELECT_RANGE_TRIGGER';
+export const selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'> = {
+ id: SELECT_RANGE_TRIGGER,
+ title: i18n.translate('embeddableApi.selectRangeTrigger.title', {
+ defaultMessage: 'Range selection',
+ }),
+ description: i18n.translate('embeddableApi.selectRangeTrigger.description', {
+ defaultMessage: 'A range of values on the visualization',
+ }),
+};
+
+export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER';
+export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> = {
+ id: VALUE_CLICK_TRIGGER,
+ title: i18n.translate('embeddableApi.valueClickTrigger.title', {
+ defaultMessage: 'Single click',
+ }),
+ description: i18n.translate('embeddableApi.valueClickTrigger.description', {
+ defaultMessage: 'A data point click on the visualization',
+ }),
+};
+
export const isValueClickTriggerContext = (
context: ChartActionContext
): context is ValueClickContext => context.data && 'data' in context.data;
diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx
index df24d9c0393fe..c41ecaabe8479 100644
--- a/src/plugins/embeddable/public/mocks.tsx
+++ b/src/plugins/embeddable/public/mocks.tsx
@@ -34,7 +34,6 @@ import { coreMock } from '../../../core/public/mocks';
import { UiActionsService } from './lib/ui_actions';
import { CoreStart } from '../../../core/public';
import { Start as InspectorStart } from '../../inspector/public';
-import { dataPluginMock } from '../../data/public/mocks';
import { inspectorPluginMock } from '../../inspector/public/mocks';
import { uiActionsPluginMock } from '../../ui_actions/public/mocks';
@@ -136,13 +135,11 @@ const createInstance = (setupPlugins: Partial = {})
const plugin = new EmbeddablePublicPlugin({} as any);
const setup = plugin.setup(coreMock.createSetup(), {
uiActions: setupPlugins.uiActions || uiActionsPluginMock.createSetupContract(),
- data: dataPluginMock.createSetupContract(),
});
const doStart = (startPlugins: Partial = {}) =>
plugin.start(coreMock.createStart(), {
uiActions: startPlugins.uiActions || uiActionsPluginMock.createStartContract(),
inspector: inspectorPluginMock.createStartContract(),
- data: dataPluginMock.createStartContract(),
});
return {
plugin,
diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx
index 6f43d87bdcd53..a417fb3938b8a 100644
--- a/src/plugins/embeddable/public/plugin.tsx
+++ b/src/plugins/embeddable/public/plugin.tsx
@@ -19,7 +19,6 @@
import React from 'react';
import { Subscription } from 'rxjs';
import { identity } from 'lodash';
-import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public';
import { getSavedObjectFinder, showSaveModal } from '../../saved_objects/public';
import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public';
import { Start as InspectorStart } from '../../inspector/public';
@@ -62,12 +61,10 @@ import {
} from '../common/lib';
export interface EmbeddableSetupDependencies {
- data: DataPublicPluginSetup;
uiActions: UiActionsSetup;
}
export interface EmbeddableStartDependencies {
- data: DataPublicPluginStart;
uiActions: UiActionsStart;
inspector: InspectorStart;
}
@@ -144,7 +141,7 @@ export class EmbeddablePublicPlugin implements Plugin {
this.embeddableFactories.set(
diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md
index 7563b66e58ae9..a401795c498b3 100644
--- a/src/plugins/embeddable/public/public.api.md
+++ b/src/plugins/embeddable/public/public.api.md
@@ -8,48 +8,29 @@ import { Action } from 'history';
import { Action as Action_3 } from 'src/plugins/ui_actions/public';
import { ActionExecutionContext as ActionExecutionContext_2 } from 'src/plugins/ui_actions/public';
import { ApiResponse } from '@elastic/elasticsearch/lib/Transport';
-import { ApiResponse as ApiResponse_2 } from '@elastic/elasticsearch';
import { ApplicationStart as ApplicationStart_2 } from 'kibana/public';
-import { Assign } from '@kbn/utility-types';
-import { BehaviorSubject } from 'rxjs';
-import { BfetchPublicSetup } from 'src/plugins/bfetch/public';
import Boom from '@hapi/boom';
import { ConfigDeprecationProvider } from '@kbn/config';
-import { CoreSetup as CoreSetup_2 } from 'src/core/public';
-import { CoreSetup as CoreSetup_3 } from 'kibana/public';
-import { CoreStart as CoreStart_2 } from 'kibana/public';
import * as CSS from 'csstype';
-import { DatatableColumn as DatatableColumn_2 } from 'src/plugins/expressions';
import { EmbeddableStart as EmbeddableStart_2 } from 'src/plugins/embeddable/public/plugin';
-import { Ensure } from '@kbn/utility-types';
import { EnvironmentMode } from '@kbn/config';
-import { ErrorToastOptions as ErrorToastOptions_2 } from 'src/core/public/notifications';
import { EuiBreadcrumb } from '@elastic/eui';
import { EuiButtonEmptyProps } from '@elastic/eui';
-import { EuiComboBoxProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiContextMenuPanelDescriptor } from '@elastic/eui';
import { EuiFlyoutSize } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
import { EventEmitter } from 'events';
-import { ExpressionAstExpression } from 'src/plugins/expressions/common';
import { History } from 'history';
import { Href } from 'history';
-import { HttpSetup as HttpSetup_2 } from 'kibana/public';
import { I18nStart as I18nStart_2 } from 'src/core/public';
import { IconType } from '@elastic/eui';
-import { ISearchOptions } from 'src/plugins/data/public';
-import { ISearchSource } from 'src/plugins/data/public';
-import { IStorageWrapper as IStorageWrapper_2 } from 'src/plugins/kibana_utils/public';
-import { IUiSettingsClient as IUiSettingsClient_2 } from 'src/core/public';
import { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { Location } from 'history';
import { LocationDescriptorObject } from 'history';
import { Logger } from '@kbn/logging';
import { LogMeta } from '@kbn/logging';
import { MaybePromise } from '@kbn/utility-types';
-import { Moment } from 'moment';
-import { NameList } from 'elasticsearch';
import { NotificationsStart as NotificationsStart_2 } from 'src/core/public';
import { Observable } from 'rxjs';
import { Optional } from '@kbn/utility-types';
@@ -57,39 +38,23 @@ import { OverlayStart as OverlayStart_2 } from 'src/core/public';
import { PackageInfo } from '@kbn/config';
import { Path } from 'history';
import { PluginInitializerContext } from 'src/core/public';
-import { PluginInitializerContext as PluginInitializerContext_3 } from 'kibana/public';
import * as PropTypes from 'prop-types';
-import { PublicContract } from '@kbn/utility-types';
import { PublicMethodsOf } from '@kbn/utility-types';
import { PublicUiSettingsParams } from 'src/core/server/types';
import React from 'react';
import { RecursiveReadonly } from '@kbn/utility-types';
-import { RequestAdapter as RequestAdapter_2 } from 'src/plugins/inspector/common';
-import { Required } from '@kbn/utility-types';
import * as Rx from 'rxjs';
-import { SavedObject as SavedObject_2 } from 'kibana/server';
-import { SavedObject as SavedObject_3 } from 'src/core/server';
import { SavedObjectAttributes } from 'kibana/server';
import { SavedObjectAttributes as SavedObjectAttributes_2 } from 'src/core/public';
import { SavedObjectAttributes as SavedObjectAttributes_3 } from 'kibana/public';
-import { SavedObjectsClientContract as SavedObjectsClientContract_3 } from 'src/core/public';
-import { SavedObjectsFindOptions as SavedObjectsFindOptions_3 } from 'kibana/public';
-import { SavedObjectsFindResponse as SavedObjectsFindResponse_2 } from 'kibana/server';
-import { Search } from '@elastic/elasticsearch/api/requestParams';
-import { SearchResponse } from 'elasticsearch';
-import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common';
import { ShallowPromise } from '@kbn/utility-types';
import { SimpleSavedObject as SimpleSavedObject_2 } from 'src/core/public';
import { Start as Start_2 } from 'src/plugins/inspector/public';
-import { StartServicesAccessor as StartServicesAccessor_2 } from 'kibana/public';
-import { ToastInputFields as ToastInputFields_2 } from 'src/core/public/notifications';
-import { ToastsSetup as ToastsSetup_2 } from 'kibana/public';
import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport';
import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport';
import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
import { TypeOf } from '@kbn/config-schema';
import { UiComponent } from 'src/plugins/kibana_utils/public';
-import { UiCounterMetricType } from '@kbn/analytics';
import { UnregisterCallback } from 'history';
import { UserProvidedValues } from 'src/core/server/types';
@@ -348,7 +313,7 @@ export abstract class Embeddable {
+export class EmbeddableChildPanel extends React.Component {
constructor(props: EmbeddableChildPanelProps);
// (undocumented)
[panel: string]: any;
@@ -381,9 +346,9 @@ export interface EmbeddableChildPanelProps {
// Warning: (ae-missing-release-tag) "EmbeddableContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export interface EmbeddableContext {
+export interface EmbeddableContext {
// (undocumented)
- embeddable: IEmbeddable;
+ embeddable: T;
}
// @public
@@ -444,9 +409,6 @@ export type EmbeddableInput = {
enhancements?: SerializableState;
disabledActions?: string[];
disableTriggers?: boolean;
- timeRange?: TimeRange;
- query?: Query;
- filters?: Filter[];
searchSessionId?: string;
};
@@ -501,7 +463,7 @@ export interface EmbeddablePackageState {
// Warning: (ae-missing-release-tag) "EmbeddablePanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export class EmbeddablePanel extends React.Component {
+export class EmbeddablePanel extends React.Component {
constructor(props: Props);
// (undocumented)
closeMyContextMenuPanel: () => void;
@@ -571,10 +533,6 @@ export interface EmbeddableSetup {
//
// @public (undocumented)
export interface EmbeddableSetupDependencies {
- // Warning: (ae-forgotten-export) The symbol "DataPublicPluginSetup" needs to be exported by the entry point index.d.ts
- //
- // (undocumented)
- data: DataPublicPluginSetup;
// Warning: (ae-forgotten-export) The symbol "UiActionsSetup" needs to be exported by the entry point index.d.ts
//
// (undocumented)
@@ -610,10 +568,6 @@ export interface EmbeddableStart extends PersistableStateService context is EmbeddableContext;
+export const isContextMenuTriggerContext: (context: unknown) => context is EmbeddableContext>;
// Warning: (ae-missing-release-tag) "isEmbeddable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -869,6 +823,16 @@ export interface SavedObjectEmbeddableInput extends EmbeddableInput {
savedObjectId: string;
}
+// Warning: (ae-missing-release-tag) "SELECT_RANGE_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const SELECT_RANGE_TRIGGER = "SELECT_RANGE_TRIGGER";
+
+// Warning: (ae-missing-release-tag) "VALUE_CLICK_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const VALUE_CLICK_TRIGGER = "VALUE_CLICK_TRIGGER";
+
// Warning: (ae-missing-release-tag) "ValueClickContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -912,10 +876,7 @@ export const withEmbeddableSubscription: {
test('Explicit embeddable input mapped to undefined will default to inherited', async () => {
const { start } = await creatHelloWorldContainerAndEmbeddable();
- const derivedFilter: Filter = {
- $state: { store: esFilters.FilterStateStore.APP_STATE },
+ const derivedFilter: MockFilter = {
+ $state: { store: 'appState' },
meta: { disabled: false, alias: 'name', negate: false },
query: { match: {} },
};
diff --git a/src/plugins/embeddable/public/tests/explicit_input.test.ts b/src/plugins/embeddable/public/tests/explicit_input.test.ts
index 24785dd50a032..531fbcee94db6 100644
--- a/src/plugins/embeddable/public/tests/explicit_input.test.ts
+++ b/src/plugins/embeddable/public/tests/explicit_input.test.ts
@@ -20,6 +20,7 @@
import { skip } from 'rxjs/operators';
import { testPlugin } from './test_plugin';
import {
+ MockFilter,
FILTERABLE_EMBEDDABLE,
FilterableEmbeddableInput,
} from '../lib/test_samples/embeddables/filterable_embeddable';
@@ -34,7 +35,6 @@ import { FilterableContainer } from '../lib/test_samples/embeddables/filterable_
import { isErrorEmbeddable } from '../lib';
import { HelloWorldContainer } from '../lib/test_samples/embeddables/hello_world_container';
import { coreMock } from '../../../../core/public/mocks';
-import { esFilters, Filter } from '../../../../plugins/data/public';
import { createEmbeddablePanelMock } from '../mocks';
const { setup, doStart, coreStart, uiActions } = testPlugin(
@@ -56,8 +56,8 @@ setup.registerEmbeddableFactory(
const start = doStart();
test('Explicit embeddable input mapped to undefined will default to inherited', async () => {
- const derivedFilter: Filter = {
- $state: { store: esFilters.FilterStateStore.APP_STATE },
+ const derivedFilter: MockFilter = {
+ $state: { store: 'appState' },
meta: { disabled: false, alias: 'name', negate: false },
query: { match: {} },
};
diff --git a/src/plugins/embeddable/public/tests/test_plugin.ts b/src/plugins/embeddable/public/tests/test_plugin.ts
index 2c298b437a118..74bb70e913bcc 100644
--- a/src/plugins/embeddable/public/tests/test_plugin.ts
+++ b/src/plugins/embeddable/public/tests/test_plugin.ts
@@ -21,7 +21,6 @@ import { CoreSetup, CoreStart } from 'src/core/public';
import { UiActionsStart } from '../../../ui_actions/public';
import { uiActionsPluginMock } from '../../../ui_actions/public/mocks';
import { inspectorPluginMock } from '../../../inspector/public/mocks';
-import { dataPluginMock } from '../../../data/public/mocks';
import { coreMock } from '../../../../core/public/mocks';
import { EmbeddablePublicPlugin, EmbeddableSetup, EmbeddableStart } from '../plugin';
@@ -42,7 +41,6 @@ export const testPlugin = (
const initializerContext = {} as any;
const plugin = new EmbeddablePublicPlugin(initializerContext);
const setup = plugin.setup(coreSetup, {
- data: dataPluginMock.createSetupContract(),
uiActions: uiActions.setup,
});
@@ -53,7 +51,6 @@ export const testPlugin = (
setup,
doStart: (anotherCoreStart: CoreStart = coreStart) => {
const start = plugin.start(anotherCoreStart, {
- data: dataPluginMock.createStartContract(),
inspector: inspectorPluginMock.createStartContract(),
uiActions: uiActionsPluginMock.createStartContract(),
});
diff --git a/src/plugins/expressions/common/expression_renderers/types.ts b/src/plugins/expressions/common/expression_renderers/types.ts
index 88aca4c07ee31..fca1694747ce2 100644
--- a/src/plugins/expressions/common/expression_renderers/types.ts
+++ b/src/plugins/expressions/common/expression_renderers/types.ts
@@ -17,8 +17,6 @@
* under the License.
*/
-import { PersistedState } from 'src/plugins/visualizations/public';
-
export interface ExpressionRenderDefinition {
/**
* Technical name of the renderer, used as ID to identify renderer in
@@ -84,5 +82,10 @@ export interface IInterpreterRenderHandlers {
event: (event: any) => void;
hasCompatibleActions?: (event: any) => Promise;
getRenderMode: () => RenderMode;
- uiState?: PersistedState;
+ /**
+ * This uiState interface is actually `PersistedState` from the visualizations plugin,
+ * but expressions cannot know about vis or it creates a mess of circular dependencies.
+ * Downstream consumers of the uiState handler will need to cast for now.
+ */
+ uiState?: unknown;
}
diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md
index bb1f5dd9270d5..404df2db019a1 100644
--- a/src/plugins/expressions/public/public.api.md
+++ b/src/plugins/expressions/public/public.api.md
@@ -12,7 +12,6 @@ import { EventEmitter } from 'events';
import { KibanaRequest } from 'src/core/server';
import { Observable } from 'rxjs';
import { PackageInfo } from '@kbn/config';
-import { PersistedState } from 'src/plugins/visualizations/public';
import { Plugin as Plugin_2 } from 'src/core/public';
import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public';
import React from 'react';
@@ -924,8 +923,7 @@ export interface IInterpreterRenderHandlers {
onDestroy: (fn: () => void) => void;
// (undocumented)
reload: () => void;
- // (undocumented)
- uiState?: PersistedState;
+ uiState?: unknown;
// (undocumented)
update: (params: any) => void;
}
diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md
index 7c1ab11f75027..8b8678371dd83 100644
--- a/src/plugins/expressions/server/server.api.md
+++ b/src/plugins/expressions/server/server.api.md
@@ -10,7 +10,6 @@ import { Ensure } from '@kbn/utility-types';
import { EventEmitter } from 'events';
import { KibanaRequest } from 'src/core/server';
import { Observable } from 'rxjs';
-import { PersistedState } from 'src/plugins/visualizations/public';
import { Plugin as Plugin_2 } from 'src/core/server';
import { PluginInitializerContext } from 'src/core/server';
import { UnwrapPromiseOrReturn } from '@kbn/utility-types';
@@ -741,8 +740,7 @@ export interface IInterpreterRenderHandlers {
onDestroy: (fn: () => void) => void;
// (undocumented)
reload: () => void;
- // (undocumented)
- uiState?: PersistedState;
+ uiState?: unknown;
// (undocumented)
update: (params: any) => void;
}
diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts
index d223c0abcccb7..7890e4bab44a3 100644
--- a/src/plugins/ui_actions/public/index.ts
+++ b/src/plugins/ui_actions/public/index.ts
@@ -40,12 +40,6 @@ export {
export {
Trigger,
TriggerContext,
- SELECT_RANGE_TRIGGER,
- selectRangeTrigger,
- VALUE_CLICK_TRIGGER,
- valueClickTrigger,
- APPLY_FILTER_TRIGGER,
- applyFilterTrigger,
VISUALIZE_FIELD_TRIGGER,
visualizeFieldTrigger,
VISUALIZE_GEO_FIELD_TRIGGER,
diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts
index fdb75e9a426e9..84a7ae45fc7b8 100644
--- a/src/plugins/ui_actions/public/plugin.ts
+++ b/src/plugins/ui_actions/public/plugin.ts
@@ -20,14 +20,7 @@
import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public';
import { PublicMethodsOf } from '@kbn/utility-types';
import { UiActionsService } from './service';
-import {
- selectRangeTrigger,
- valueClickTrigger,
- rowClickTrigger,
- applyFilterTrigger,
- visualizeFieldTrigger,
- visualizeGeoFieldTrigger,
-} from './triggers';
+import { rowClickTrigger, visualizeFieldTrigger, visualizeGeoFieldTrigger } from './triggers';
export type UiActionsSetup = Pick<
UiActionsService,
@@ -47,10 +40,7 @@ export class UiActionsPlugin implements Plugin {
constructor(initializerContext: PluginInitializerContext) {}
public setup(core: CoreSetup): UiActionsSetup {
- this.service.registerTrigger(selectRangeTrigger);
- this.service.registerTrigger(valueClickTrigger);
this.service.registerTrigger(rowClickTrigger);
- this.service.registerTrigger(applyFilterTrigger);
this.service.registerTrigger(visualizeFieldTrigger);
this.service.registerTrigger(visualizeGeoFieldTrigger);
return this.service;
diff --git a/src/plugins/ui_actions/public/public.api.md b/src/plugins/ui_actions/public/public.api.md
index 2384dfab13c8c..808cb1f3fbca0 100644
--- a/src/plugins/ui_actions/public/public.api.md
+++ b/src/plugins/ui_actions/public/public.api.md
@@ -8,14 +8,11 @@ import { CoreSetup } from 'src/core/public';
import { CoreStart } from 'src/core/public';
import { EnvironmentMode } from '@kbn/config';
import { EuiContextMenuPanelDescriptor } from '@elastic/eui';
-import { EventEmitter } from 'events';
-import { Observable } from 'rxjs';
import { PackageInfo } from '@kbn/config';
import { Plugin } from 'src/core/public';
import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public';
import { PublicMethodsOf } from '@kbn/utility-types';
import React from 'react';
-import * as Rx from 'rxjs';
import { UiComponent } from 'src/plugins/kibana_utils/public';
// Warning: (ae-forgotten-export) The symbol "BaseContext" needs to be exported by the entry point index.d.ts
@@ -95,16 +92,6 @@ export interface ActionExecutionMeta {
// @public (undocumented)
export type ActionType = keyof ActionContextMapping;
-// Warning: (ae-missing-release-tag) "APPLY_FILTER_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const APPLY_FILTER_TRIGGER = "FILTER_TRIGGER";
-
-// Warning: (ae-missing-release-tag) "applyFilterTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'>;
-
// Warning: (ae-forgotten-export) The symbol "BuildContextMenuParams" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "buildContextMenuForActions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -148,10 +135,8 @@ export interface RowClickContext {
table: Datatable;
columns?: string[];
};
- // Warning: (ae-forgotten-export) The symbol "IEmbeddable" needs to be exported by the entry point index.d.ts
- //
// (undocumented)
- embeddable?: IEmbeddable;
+ embeddable?: unknown;
}
// Warning: (ae-missing-release-tag) "rowClickTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -159,16 +144,6 @@ export interface RowClickContext {
// @public (undocumented)
export const rowClickTrigger: Trigger<'ROW_CLICK_TRIGGER'>;
-// Warning: (ae-missing-release-tag) "SELECT_RANGE_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const SELECT_RANGE_TRIGGER = "SELECT_RANGE_TRIGGER";
-
-// Warning: (ae-missing-release-tag) "selectRangeTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'>;
-
// Warning: (ae-missing-release-tag) "Trigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
@@ -192,20 +167,8 @@ export interface TriggerContextMapping {
//
// (undocumented)
[DEFAULT_TRIGGER]: TriggerContext_2;
- // Warning: (ae-forgotten-export) The symbol "ApplyGlobalFilterActionContext" needs to be exported by the entry point index.d.ts
- //
- // (undocumented)
- [APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext;
// (undocumented)
[ROW_CLICK_TRIGGER]: RowClickContext;
- // Warning: (ae-forgotten-export) The symbol "RangeSelectContext" needs to be exported by the entry point index.d.ts
- //
- // (undocumented)
- [SELECT_RANGE_TRIGGER]: RangeSelectContext;
- // Warning: (ae-forgotten-export) The symbol "ValueClickContext" needs to be exported by the entry point index.d.ts
- //
- // (undocumented)
- [VALUE_CLICK_TRIGGER]: ValueClickContext;
// (undocumented)
[VISUALIZE_FIELD_TRIGGER]: VisualizeFieldContext;
// (undocumented)
@@ -262,35 +225,35 @@ export class UiActionsService {
//
// (undocumented)
protected readonly actions: ActionRegistry;
- readonly addTriggerAction: (triggerId: T, action: UiActionsActionDefinition | Action) => void;
+ readonly addTriggerAction: (triggerId: T, action: UiActionsActionDefinition | Action) => void;
// (undocumented)
- readonly attachAction: (triggerId: T, actionId: string) => void;
+ readonly attachAction: (triggerId: T, actionId: string) => void;
readonly clear: () => void;
// (undocumented)
readonly detachAction: (triggerId: TriggerId, actionId: string) => void;
// @deprecated (undocumented)
- readonly executeTriggerActions: (triggerId: T, context: TriggerContext) => Promise;
+ readonly executeTriggerActions: (triggerId: T, context: TriggerContext) => Promise;
// Warning: (ae-forgotten-export) The symbol "UiActionsExecutionService" needs to be exported by the entry point index.d.ts
//
// (undocumented)
readonly executionService: UiActionsExecutionService;
readonly fork: () => UiActionsService;
// (undocumented)
- readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">;
+ readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">;
// Warning: (ae-forgotten-export) The symbol "TriggerContract" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- readonly getTrigger: (triggerId: T) => TriggerContract;
+ readonly getTrigger: (triggerId: T) => TriggerContract;
// (undocumented)
- readonly getTriggerActions: (triggerId: T) => Action[];
+ readonly getTriggerActions: (triggerId: T) => Action[];
// (undocumented)
- readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>;
+ readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>;
// (undocumented)
readonly hasAction: (actionId: string) => boolean;
// Warning: (ae-forgotten-export) The symbol "ActionContext" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">;
+ readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel">;
// (undocumented)
readonly registerTrigger: (trigger: Trigger) => void;
// Warning: (ae-forgotten-export) The symbol "TriggerRegistry" needs to be exported by the entry point index.d.ts
@@ -326,16 +289,6 @@ export type UiActionsSetup = Pick;
-// Warning: (ae-missing-release-tag) "VALUE_CLICK_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const VALUE_CLICK_TRIGGER = "VALUE_CLICK_TRIGGER";
-
-// Warning: (ae-missing-release-tag) "valueClickTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'>;
-
// Warning: (ae-missing-release-tag) "VISUALIZE_FIELD_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -371,7 +324,7 @@ export const visualizeGeoFieldTrigger: Trigger<'VISUALIZE_GEO_FIELD_TRIGGER'>;
// Warnings were encountered during analysis:
//
-// src/plugins/ui_actions/public/triggers/row_click_trigger.ts:45:5 - (ae-forgotten-export) The symbol "Datatable" needs to be exported by the entry point index.d.ts
+// src/plugins/ui_actions/public/triggers/row_click_trigger.ts:46:5 - (ae-forgotten-export) The symbol "Datatable" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts
index ecbf4d1f7b988..6bba87e85eb95 100644
--- a/src/plugins/ui_actions/public/triggers/index.ts
+++ b/src/plugins/ui_actions/public/triggers/index.ts
@@ -20,10 +20,7 @@
export * from './trigger';
export * from './trigger_contract';
export * from './trigger_internal';
-export * from './select_range_trigger';
-export * from './value_click_trigger';
export * from './row_click_trigger';
-export * from './apply_filter_trigger';
export * from './visualize_field_trigger';
export * from './visualize_geo_field_trigger';
export * from './default_trigger';
diff --git a/src/plugins/ui_actions/public/triggers/row_click_trigger.ts b/src/plugins/ui_actions/public/triggers/row_click_trigger.ts
index 87bca03f8c3ba..0fc261b3e1fb3 100644
--- a/src/plugins/ui_actions/public/triggers/row_click_trigger.ts
+++ b/src/plugins/ui_actions/public/triggers/row_click_trigger.ts
@@ -18,7 +18,6 @@
*/
import { i18n } from '@kbn/i18n';
-import { IEmbeddable } from '../../../embeddable/public';
import { Trigger } from '.';
import { Datatable } from '../../../expressions';
@@ -35,7 +34,9 @@ export const rowClickTrigger: Trigger<'ROW_CLICK_TRIGGER'> = {
};
export interface RowClickContext {
- embeddable?: IEmbeddable;
+ // Need to make this unknown to prevent circular dependencies.
+ // Apps using this property will need to cast to `IEmbeddable`.
+ embeddable?: unknown;
data: {
/**
* Row index, starting from 0, where user clicked.
diff --git a/src/plugins/ui_actions/public/triggers/select_range_trigger.ts b/src/plugins/ui_actions/public/triggers/select_range_trigger.ts
deleted file mode 100644
index 312e75314bd92..0000000000000
--- a/src/plugins/ui_actions/public/triggers/select_range_trigger.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { i18n } from '@kbn/i18n';
-import { Trigger } from '.';
-
-export const SELECT_RANGE_TRIGGER = 'SELECT_RANGE_TRIGGER';
-export const selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'> = {
- id: SELECT_RANGE_TRIGGER,
- title: i18n.translate('uiActions.triggers.selectRangeTitle', {
- defaultMessage: 'Range selection',
- }),
- description: i18n.translate('uiActions.triggers.selectRangeDescription', {
- defaultMessage: 'A range of values on the visualization',
- }),
-};
diff --git a/src/plugins/ui_actions/public/triggers/trigger.ts b/src/plugins/ui_actions/public/triggers/trigger.ts
index 2c019b09881d1..1b1231c132dde 100644
--- a/src/plugins/ui_actions/public/triggers/trigger.ts
+++ b/src/plugins/ui_actions/public/triggers/trigger.ts
@@ -32,8 +32,7 @@ import { TriggerContextMapping, TriggerId } from '../types';
*/
export interface Trigger {
/**
- * Unique name of the trigger as identified in `ui_actions` plugin trigger
- * registry, such as "SELECT_RANGE_TRIGGER" or "VALUE_CLICK_TRIGGER".
+ * Unique name of the trigger as identified in `ui_actions` plugin trigger registry.
*/
id: ID;
diff --git a/src/plugins/ui_actions/public/triggers/trigger_contract.ts b/src/plugins/ui_actions/public/triggers/trigger_contract.ts
index 04a75cb3a53d0..7e7fba0ba80d3 100644
--- a/src/plugins/ui_actions/public/triggers/trigger_contract.ts
+++ b/src/plugins/ui_actions/public/triggers/trigger_contract.ts
@@ -25,8 +25,7 @@ import { TriggerId, TriggerContextMapping } from '../types';
*/
export class TriggerContract {
/**
- * Unique name of the trigger as identified in `ui_actions` plugin trigger
- * registry, such as "SELECT_RANGE_TRIGGER" or "VALUE_CLICK_TRIGGER".
+ * Unique name of the trigger as identified in `ui_actions` plugin trigger registry.
*/
public readonly id: T;
diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts
index 0266a755be926..62fac245514cd 100644
--- a/src/plugins/ui_actions/public/types.ts
+++ b/src/plugins/ui_actions/public/types.ts
@@ -20,17 +20,12 @@
import { ActionInternal } from './actions/action_internal';
import { TriggerInternal } from './triggers/trigger_internal';
import {
- SELECT_RANGE_TRIGGER,
- VALUE_CLICK_TRIGGER,
ROW_CLICK_TRIGGER,
- APPLY_FILTER_TRIGGER,
VISUALIZE_FIELD_TRIGGER,
VISUALIZE_GEO_FIELD_TRIGGER,
DEFAULT_TRIGGER,
RowClickContext,
} from './triggers';
-import type { RangeSelectContext, ValueClickContext } from '../../embeddable/public';
-import type { ApplyGlobalFilterActionContext } from '../../data/public';
export type TriggerRegistry = Map>;
export type ActionRegistry = Map;
@@ -49,10 +44,7 @@ export type TriggerContext = BaseContext;
export interface TriggerContextMapping {
[DEFAULT_TRIGGER]: TriggerContext;
- [SELECT_RANGE_TRIGGER]: RangeSelectContext;
- [VALUE_CLICK_TRIGGER]: ValueClickContext;
[ROW_CLICK_TRIGGER]: RowClickContext;
- [APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext;
[VISUALIZE_FIELD_TRIGGER]: VisualizeFieldContext;
[VISUALIZE_GEO_FIELD_TRIGGER]: VisualizeFieldContext;
}
diff --git a/src/plugins/vis_type_table/public/components/table_visualization.tsx b/src/plugins/vis_type_table/public/components/table_visualization.tsx
index 2d38acc57519f..a1e4fad719dc4 100644
--- a/src/plugins/vis_type_table/public/components/table_visualization.tsx
+++ b/src/plugins/vis_type_table/public/components/table_visualization.tsx
@@ -23,6 +23,7 @@ import classNames from 'classnames';
import { CoreStart } from 'kibana/public';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
+import type { PersistedState } from 'src/plugins/visualizations/public';
import { KibanaContextProvider } from '../../../kibana_react/public';
import { TableVisConfig } from '../types';
import { TableContext } from '../table_vis_response_handler';
@@ -47,7 +48,7 @@ const TableVisualizationComponent = ({
handlers.done();
}, [handlers]);
- const uiStateProps = useUiState(handlers.uiState);
+ const uiStateProps = useUiState(handlers.uiState as PersistedState);
const className = classNames('tbvChart', {
// eslint-disable-next-line @typescript-eslint/naming-convention
diff --git a/src/plugins/vis_type_table/public/utils/use/use_ui_state.ts b/src/plugins/vis_type_table/public/utils/use/use_ui_state.ts
index 68bad16972ec2..cc43fc6bcb582 100644
--- a/src/plugins/vis_type_table/public/utils/use/use_ui_state.ts
+++ b/src/plugins/vis_type_table/public/utils/use/use_ui_state.ts
@@ -19,7 +19,7 @@
import { debounce, isEqual } from 'lodash';
import { useCallback, useEffect, useRef, useState } from 'react';
-import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
+import type { PersistedState } from 'src/plugins/visualizations/public';
import { ColumnWidthData, TableVisUiState, TableVisUseUiStateProps } from '../../types';
@@ -28,9 +28,7 @@ const defaultSort = {
direction: null,
};
-export const useUiState = (
- uiState: IInterpreterRenderHandlers['uiState']
-): TableVisUseUiStateProps => {
+export const useUiState = (uiState: PersistedState): TableVisUseUiStateProps => {
const [sort, setSortState] = useState(
uiState?.get('vis.params.sort') || defaultSort
);
diff --git a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx
index 67ed487d29378..dec169c59c6ef 100644
--- a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx
+++ b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx
@@ -21,6 +21,7 @@ import React, { lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { IUiSettingsClient } from 'kibana/public';
+import type { PersistedState } from '../../visualizations/public';
import { VisualizationContainer } from '../../visualizations/public';
import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers';
import { TimeseriesRenderValue, TimeseriesVisParams } from './metrics_fn';
@@ -63,7 +64,7 @@ export const getTimeseriesVisRenderer: (deps: {
handlers={handlers}
model={config.visParams}
visData={config.visData as TimeseriesVisData}
- uiState={handlers.uiState!}
+ uiState={handlers.uiState! as PersistedState}
/>
,
domNode
diff --git a/src/plugins/vis_type_vislib/public/vis_controller.tsx b/src/plugins/vis_type_vislib/public/vis_controller.tsx
index 1804d0d52ae7a..2a32d19874c22 100644
--- a/src/plugins/vis_type_vislib/public/vis_controller.tsx
+++ b/src/plugins/vis_type_vislib/public/vis_controller.tsx
@@ -22,7 +22,7 @@ import React, { RefObject } from 'react';
import { mountReactNode } from '../../../core/public/utils';
import { ChartsPluginSetup } from '../../charts/public';
-import { PersistedState } from '../../visualizations/public';
+import type { PersistedState } from '../../visualizations/public';
import { IInterpreterRenderHandlers } from '../../expressions/public';
import { VisTypeVislibCoreSetup } from './plugin';
@@ -115,7 +115,7 @@ export const createVislibVisController = (
})
.addClass((legendClassName as any)[visParams.legendPosition]);
- this.mountLegend(esResponse, visParams, fireEvent, uiState);
+ this.mountLegend(esResponse, visParams, fireEvent, uiState as PersistedState);
}
this.vislibVis.render(esResponse, uiState);
@@ -128,7 +128,7 @@ export const createVislibVisController = (
CUSTOM_LEGEND_VIS_TYPES.includes(this.vislibVis.visConfigArgs.type)
) {
this.unmountLegend?.();
- this.mountLegend(esResponse, visParams, fireEvent, uiState);
+ this.mountLegend(esResponse, visParams, fireEvent, uiState as PersistedState);
this.vislibVis.render(esResponse, uiState);
}
}
diff --git a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx b/src/plugins/vis_type_vislib/public/vis_wrapper.tsx
index 280a957396c34..b8dbd0f945c32 100644
--- a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx
+++ b/src/plugins/vis_type_vislib/public/vis_wrapper.tsx
@@ -22,6 +22,7 @@ import { EuiResizeObserver } from '@elastic/eui';
import { debounce } from 'lodash';
import { IInterpreterRenderHandlers } from '../../expressions/public';
+import type { PersistedState } from '../../visualizations/public';
import { ChartsPluginSetup } from '../../charts/public';
import { VislibRenderValue } from './vis_type_vislib_vis_fn';
@@ -66,10 +67,12 @@ const VislibWrapper = ({ core, charts, visData, visConfig, handlers }: VislibWra
useEffect(() => {
if (handlers.uiState) {
- handlers.uiState.on('change', updateChart);
+ const uiState = handlers.uiState as PersistedState;
+
+ uiState.on('change', updateChart);
return () => {
- handlers.uiState?.off('change', updateChart);
+ uiState?.off('change', updateChart);
};
}
}, [handlers.uiState, updateChart]);
diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx
index d87500218975a..dcf9f69cc291d 100644
--- a/src/plugins/vis_type_xy/public/vis_component.tsx
+++ b/src/plugins/vis_type_xy/public/vis_component.tsx
@@ -50,6 +50,7 @@ import {
ClickTriggerEvent,
} from '../../charts/public';
import { Datatable, IInterpreterRenderHandlers } from '../../expressions/public';
+import type { PersistedState } from '../../visualizations/public';
import { VisParams } from './types';
import {
@@ -77,7 +78,7 @@ import {
export interface VisComponentProps {
visParams: VisParams;
visData: Datatable;
- uiState: IInterpreterRenderHandlers['uiState'];
+ uiState: PersistedState;
fireEvent: IInterpreterRenderHandlers['event'];
renderComplete: IInterpreterRenderHandlers['done'];
}
diff --git a/src/plugins/vis_type_xy/public/vis_renderer.tsx b/src/plugins/vis_type_xy/public/vis_renderer.tsx
index 4ac7c23c30893..16463abb07631 100644
--- a/src/plugins/vis_type_xy/public/vis_renderer.tsx
+++ b/src/plugins/vis_type_xy/public/vis_renderer.tsx
@@ -24,6 +24,7 @@ import { I18nProvider } from '@kbn/i18n/react';
import { ExpressionRenderDefinition } from '../../expressions/public';
import { VisualizationContainer } from '../../visualizations/public';
+import type { PersistedState } from '../../visualizations/public';
import { XyVisType } from '../common';
import { SplitChartWarning } from './components/split_chart_warning';
@@ -59,7 +60,7 @@ export const xyVisRenderer: ExpressionRenderDefinition = {
visData={visData}
renderComplete={handlers.done}
fireEvent={handlers.event}
- uiState={handlers.uiState}
+ uiState={handlers.uiState as PersistedState}
/>
>
diff --git a/src/plugins/visualizations/public/embeddable/events.ts b/src/plugins/visualizations/public/embeddable/events.ts
index 41e52c3ac1327..7065d758bbc49 100644
--- a/src/plugins/visualizations/public/embeddable/events.ts
+++ b/src/plugins/visualizations/public/embeddable/events.ts
@@ -17,12 +17,9 @@
* under the License.
*/
-import {
- APPLY_FILTER_TRIGGER,
- SELECT_RANGE_TRIGGER,
- VALUE_CLICK_TRIGGER,
- ROW_CLICK_TRIGGER,
-} from '../../../ui_actions/public';
+import { ROW_CLICK_TRIGGER } from '../../../ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../../../../plugins/data/public';
+import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER } from '../../../../plugins/embeddable/public';
export interface VisEventToTrigger {
['applyFilter']: typeof APPLY_FILTER_TRIGGER;
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
index f7592977858d2..5661acc26fdb6 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -71,6 +71,9 @@ export interface VisualizeInput extends EmbeddableInput {
};
savedVis?: SerializedVis;
table?: unknown;
+ query?: Query;
+ filters?: Filter[];
+ timeRange?: TimeRange;
}
export interface VisualizeOutput extends EmbeddableOutput {
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx
index 48b64a1c84588..27c63aba791dd 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx
@@ -18,10 +18,12 @@ import {
EuiFlexGroup,
} from '@elastic/eui';
import { SampleMlJob, SampleApp1ClickContext } from '../../triggers';
-import { EmbeddableRoot } from '../../../../../../src/plugins/embeddable/public';
+import {
+ EmbeddableRoot,
+ VALUE_CLICK_TRIGGER,
+} from '../../../../../../src/plugins/embeddable/public';
import { ButtonEmbeddable } from '../../embeddables/button_embeddable';
import { useUiActions } from '../../context';
-import { VALUE_CLICK_TRIGGER } from '../../../../../../src/plugins/ui_actions/public';
export const job: SampleMlJob = {
job_id: '123',
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_drilldown/index.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_drilldown/index.tsx
index a7324f5530130..50ad350cd90b9 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_drilldown/index.tsx
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_drilldown/index.tsx
@@ -8,12 +8,12 @@ import React from 'react';
import { EuiFormRow, EuiFieldText } from '@elastic/eui';
import { reactToUiComponent } from '../../../../../../src/plugins/kibana_react/public';
import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '../../../../../plugins/ui_actions_enhanced/public';
-import { ChartActionContext } from '../../../../../../src/plugins/embeddable/public';
-import { CollectConfigProps } from '../../../../../../src/plugins/kibana_utils/public';
import {
+ ChartActionContext,
SELECT_RANGE_TRIGGER,
VALUE_CLICK_TRIGGER,
-} from '../../../../../../src/plugins/ui_actions/public';
+} from '../../../../../../src/plugins/embeddable/public';
+import { CollectConfigProps } from '../../../../../../src/plugins/kibana_utils/public';
export type ActionContext = ChartActionContext;
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_only_range_select_drilldown/index.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_only_range_select_drilldown/index.tsx
index 24385bd6baa42..4e5b3187af42b 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_only_range_select_drilldown/index.tsx
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_hello_world_only_range_select_drilldown/index.tsx
@@ -8,9 +8,11 @@ import React from 'react';
import { EuiFormRow, EuiFieldText } from '@elastic/eui';
import { reactToUiComponent } from '../../../../../../src/plugins/kibana_react/public';
import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '../../../../../plugins/ui_actions_enhanced/public';
-import { RangeSelectContext } from '../../../../../../src/plugins/embeddable/public';
+import {
+ RangeSelectContext,
+ SELECT_RANGE_TRIGGER,
+} from '../../../../../../src/plugins/embeddable/public';
import { CollectConfigProps } from '../../../../../../src/plugins/kibana_utils/public';
-import { SELECT_RANGE_TRIGGER } from '../../../../../../src/plugins/ui_actions/public';
import { BaseActionFactoryContext } from '../../../../../plugins/ui_actions_enhanced/public/dynamic_actions';
export type Config = {
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx
index 9cda534a340d6..2f161efe6f388 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx
@@ -13,7 +13,7 @@ import { CollectConfigContainer } from './collect_config_container';
import { SAMPLE_DASHBOARD_TO_DISCOVER_DRILLDOWN } from './constants';
import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '../../../../../plugins/ui_actions_enhanced/public';
import { txtGoToDiscover } from './i18n';
-import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/data/public';
const isOutputWithIndexPatterns = (
output: unknown
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/types.ts b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/types.ts
index f0497780430d4..7c8915c3f143f 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/types.ts
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/types.ts
@@ -6,8 +6,9 @@
import { CollectConfigProps as CollectConfigPropsBase } from '../../../../../../src/plugins/kibana_utils/public';
import { ApplyGlobalFilterActionContext } from '../../../../../../src/plugins/data/public';
+import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public';
-export type ActionContext = ApplyGlobalFilterActionContext;
+export type ActionContext = ApplyGlobalFilterActionContext & { embeddable: IEmbeddable };
export type Config = {
/**
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/embeddables/button_embeddable/button_embeddable.ts b/x-pack/examples/ui_actions_enhanced_examples/public/embeddables/button_embeddable/button_embeddable.ts
index fcd0c9b94c988..e58ab5b8ea9d2 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/embeddables/button_embeddable/button_embeddable.ts
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/embeddables/button_embeddable/button_embeddable.ts
@@ -7,9 +7,12 @@
import { createElement } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { AdvancedUiActionsStart } from '../../../../../plugins/ui_actions_enhanced/public';
-import { Embeddable, EmbeddableInput } from '../../../../../../src/plugins/embeddable/public';
+import {
+ Embeddable,
+ EmbeddableInput,
+ VALUE_CLICK_TRIGGER,
+} from '../../../../../../src/plugins/embeddable/public';
import { ButtonEmbeddableComponent } from './button_embeddable_component';
-import { VALUE_CLICK_TRIGGER } from '../../../../../../src/plugins/ui_actions/public';
export const BUTTON_EMBEDDABLE = 'BUTTON_EMBEDDABLE';
diff --git a/x-pack/plugins/dashboard_enhanced/kibana.json b/x-pack/plugins/dashboard_enhanced/kibana.json
index b24c0b6983f40..d3a1f29f0c7ff 100644
--- a/x-pack/plugins/dashboard_enhanced/kibana.json
+++ b/x-pack/plugins/dashboard_enhanced/kibana.json
@@ -9,7 +9,6 @@
"embeddable",
"kibanaUtils",
"embeddableEnhanced",
- "kibanaReact",
- "uiActions"
+ "kibanaReact"
]
}
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts
index 7f5137812ee32..d2d3c37a69287 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts
@@ -5,7 +5,7 @@
*/
import { UiActionsEnhancedBaseActionFactoryContext } from '../../../../../ui_actions_enhanced/public';
-import { APPLY_FILTER_TRIGGER } from '../../../../../../../src/plugins/ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../../../../../../../src/plugins/data/public';
import { DrilldownConfig } from '../../../../common';
export type Config = DrilldownConfig;
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts
index e1d8a2b3671a2..ff79cda1bb215 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts
@@ -4,12 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { APPLY_FILTER_TRIGGER } from '../../../../../../../src/plugins/data/public';
import {
- APPLY_FILTER_TRIGGER,
SELECT_RANGE_TRIGGER,
- TriggerId,
VALUE_CLICK_TRIGGER,
-} from '../../../../../../../src/plugins/ui_actions/public';
+} from '../../../../../../../src/plugins/embeddable/public';
+import { TriggerId } from '../../../../../../../src/plugins/ui_actions/public';
/**
* We know that VALUE_CLICK_TRIGGER and SELECT_RANGE_TRIGGER are also triggering APPLY_FILTER_TRIGGER.
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx
index 921c2aed00624..c2bf48188c313 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx
@@ -4,17 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- TriggerContextMapping,
- APPLY_FILTER_TRIGGER,
-} from '../../../../../../../src/plugins/ui_actions/public';
+import { TriggerContextMapping } from '../../../../../../../src/plugins/ui_actions/public';
import { DashboardUrlGeneratorState } from '../../../../../../../src/plugins/dashboard/public';
import {
+ APPLY_FILTER_TRIGGER,
esFilters,
+ Filter,
isFilters,
isQuery,
isTimeRange,
+ Query,
+ TimeRange,
} from '../../../../../../../src/plugins/data/public';
+import { IEmbeddable, EmbeddableInput } from '../../../../../../../src/plugins/embeddable/public';
import {
AbstractDashboardDrilldown,
AbstractDashboardDrilldownParams,
@@ -24,6 +26,12 @@ import { KibanaURL } from '../../../../../../../src/plugins/share/public';
import { EMBEDDABLE_TO_DASHBOARD_DRILLDOWN } from './constants';
import { createExtract, createInject } from '../../../../common';
+interface EmbeddableQueryInput extends EmbeddableInput {
+ query?: Query;
+ filters?: Filter[];
+ timeRange?: TimeRange;
+}
+
type Trigger = typeof APPLY_FILTER_TRIGGER;
type Context = TriggerContextMapping[Trigger];
export type Params = AbstractDashboardDrilldownParams;
@@ -46,7 +54,8 @@ export class EmbeddableToDashboardDrilldown extends AbstractDashboardDrilldown;
+ const input = embeddable.getInput();
if (isQuery(input.query) && config.useCurrentFilters) state.query = input.query;
// if useCurrentDashboardDataRange is enabled, then preserve current time range
diff --git a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts
index 52946b3dca7a1..2f983ba247625 100644
--- a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts
+++ b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts
@@ -13,11 +13,14 @@ import {
ApplyGlobalFilterActionContext,
esFilters,
} from '../../../../../../src/plugins/data/public';
+import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public';
import { KibanaURL } from '../../../../../../src/plugins/share/public';
import * as shared from './shared';
import { AbstractExploreDataAction } from './abstract_explore_data_action';
-export type ExploreDataChartActionContext = ApplyGlobalFilterActionContext;
+export interface ExploreDataChartActionContext extends ApplyGlobalFilterActionContext {
+ embeddable?: IEmbeddable;
+}
export const ACTION_EXPLORE_DATA_CHART = 'ACTION_EXPLORE_DATA_CHART';
diff --git a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts
index fdd096e718339..fda07fb47ab0d 100644
--- a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts
+++ b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts
@@ -5,12 +5,25 @@
*/
import { Action } from '../../../../../../src/plugins/ui_actions/public';
-import { EmbeddableContext } from '../../../../../../src/plugins/embeddable/public';
+import {
+ EmbeddableContext,
+ EmbeddableInput,
+ IEmbeddable,
+} from '../../../../../../src/plugins/embeddable/public';
+import { Query, Filter, TimeRange } from '../../../../../../src/plugins/data/public';
import { DiscoverUrlGeneratorState } from '../../../../../../src/plugins/discover/public';
import { KibanaURL } from '../../../../../../src/plugins/share/public';
import * as shared from './shared';
import { AbstractExploreDataAction } from './abstract_explore_data_action';
+interface EmbeddableQueryInput extends EmbeddableInput {
+ query?: Query;
+ filters?: Filter[];
+ timeRange?: TimeRange;
+}
+
+type EmbeddableQueryContext = EmbeddableContext>;
+
export const ACTION_EXPLORE_DATA = 'ACTION_EXPLORE_DATA';
/**
@@ -18,15 +31,15 @@ export const ACTION_EXPLORE_DATA = 'ACTION_EXPLORE_DATA';
* menu of a dashboard panel.
*/
export class ExploreDataContextMenuAction
- extends AbstractExploreDataAction
- implements Action {
+ extends AbstractExploreDataAction
+ implements Action {
public readonly id = ACTION_EXPLORE_DATA;
public readonly type = ACTION_EXPLORE_DATA;
public readonly order = 200;
- protected readonly getUrl = async (context: EmbeddableContext): Promise => {
+ protected readonly getUrl = async (context: EmbeddableQueryContext): Promise => {
const { plugins } = this.params.start();
const { urlGenerator } = plugins.discover;
diff --git a/x-pack/plugins/discover_enhanced/public/plugin.ts b/x-pack/plugins/discover_enhanced/public/plugin.ts
index f1273ab00bdd4..78f3464484ccf 100644
--- a/x-pack/plugins/discover_enhanced/public/plugin.ts
+++ b/x-pack/plugins/discover_enhanced/public/plugin.ts
@@ -6,11 +6,8 @@
import { CoreSetup, CoreStart, Plugin } from 'kibana/public';
import { PluginInitializerContext } from 'kibana/public';
-import {
- UiActionsSetup,
- UiActionsStart,
- APPLY_FILTER_TRIGGER,
-} from '../../../../src/plugins/ui_actions/public';
+import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../../../../src/plugins/data/public';
import { createStartServicesGetter } from '../../../../src/plugins/kibana_utils/public';
import { DiscoverSetup, DiscoverStart } from '../../../../src/plugins/discover/public';
import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public';
diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/test/data.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/test/data.ts
index e0627c521bb79..23e73bea5dfec 100644
--- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/test/data.ts
+++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/test/data.ts
@@ -5,6 +5,7 @@
*/
import { DatatableColumnType } from '../../../../../../../src/plugins/expressions/common';
+import { Query, Filter, TimeRange } from '../../../../../../../src/plugins/data/public';
import {
Embeddable,
EmbeddableInput,
@@ -159,6 +160,9 @@ export const rowClickData = {
interface TestInput extends EmbeddableInput {
savedObjectId?: string;
+ query?: Query;
+ filters?: Filter[];
+ timeRange?: TimeRange;
}
interface TestOutput extends EmbeddableOutput {
diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts
index 24406cefce7a2..e3730084d7020 100644
--- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts
+++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts
@@ -6,14 +6,11 @@
import { IExternalUrl } from 'src/core/public';
import { UrlDrilldown, ActionContext, Config } from './url_drilldown';
-import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public/lib/embeddables';
+import { IEmbeddable, VALUE_CLICK_TRIGGER } from '../../../../../../src/plugins/embeddable/public';
import { DatatableColumnType } from '../../../../../../src/plugins/expressions/common';
import { of } from '../../../../../../src/plugins/kibana_utils';
import { createPoint, rowClickData, TestEmbeddable } from './test/data';
-import {
- VALUE_CLICK_TRIGGER,
- ROW_CLICK_TRIGGER,
-} from '../../../../../../src/plugins/ui_actions/public';
+import { ROW_CLICK_TRIGGER } from '../../../../../../src/plugins/ui_actions/public';
const mockDataPoints = [
{
diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx
index 8dfb2c54c5ab0..bfeab263d20e3 100644
--- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx
+++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx
@@ -12,13 +12,13 @@ import {
ChartActionContext,
CONTEXT_MENU_TRIGGER,
IEmbeddable,
-} from '../../../../../../src/plugins/embeddable/public';
-import { CollectConfigProps as CollectConfigPropsBase } from '../../../../../../src/plugins/kibana_utils/public';
-import {
- ROW_CLICK_TRIGGER,
+ EmbeddableInput,
SELECT_RANGE_TRIGGER,
VALUE_CLICK_TRIGGER,
-} from '../../../../../../src/plugins/ui_actions/public';
+} from '../../../../../../src/plugins/embeddable/public';
+import { ROW_CLICK_TRIGGER } from '../../../../../../src/plugins/ui_actions/public';
+import { Query, Filter, TimeRange } from '../../../../../../src/plugins/data/public';
+import { CollectConfigProps as CollectConfigPropsBase } from '../../../../../../src/plugins/kibana_utils/public';
import {
UiActionsEnhancedDrilldownDefinition as Drilldown,
UrlDrilldownGlobalScope,
@@ -31,6 +31,15 @@ import {
import { getPanelVariables, getEventScope, getEventVariableList } from './url_drilldown_scope';
import { txtUrlDrilldownDisplayName } from './i18n';
+interface EmbeddableQueryInput extends EmbeddableInput {
+ query?: Query;
+ filters?: Filter[];
+ timeRange?: TimeRange;
+}
+
+/** @internal */
+export type EmbeddableWithQueryInput = IEmbeddable;
+
interface UrlDrilldownDeps {
externalUrl: IExternalUrl;
getGlobalScope: () => UrlDrilldownGlobalScope;
@@ -39,7 +48,7 @@ interface UrlDrilldownDeps {
getVariablesHelpDocsLink: () => string;
}
-export type ActionContext = ChartActionContext;
+export type ActionContext = ChartActionContext;
export type Config = UrlDrilldownConfig;
export type UrlTrigger =
| typeof VALUE_CLICK_TRIGGER
@@ -48,7 +57,7 @@ export type UrlTrigger =
| typeof CONTEXT_MENU_TRIGGER;
export interface ActionFactoryContext extends BaseActionFactoryContext {
- embeddable?: IEmbeddable;
+ embeddable?: EmbeddableWithQueryInput;
}
export type CollectConfigProps = CollectConfigPropsBase;
diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts
index 3e5fc0a968d39..12b74d475e932 100644
--- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts
+++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts
@@ -11,20 +11,24 @@
import type { Filter, Query, TimeRange } from '../../../../../../src/plugins/data/public';
import {
- IEmbeddable,
isRangeSelectTriggerContext,
isValueClickTriggerContext,
isRowClickTriggerContext,
isContextMenuTriggerContext,
RangeSelectContext,
+ SELECT_RANGE_TRIGGER,
ValueClickContext,
+ VALUE_CLICK_TRIGGER,
+ EmbeddableInput,
EmbeddableOutput,
} from '../../../../../../src/plugins/embeddable/public';
-import type { ActionContext, ActionFactoryContext } from './url_drilldown';
+import type {
+ ActionContext,
+ ActionFactoryContext,
+ EmbeddableWithQueryInput,
+} from './url_drilldown';
import {
- SELECT_RANGE_TRIGGER,
RowClickContext,
- VALUE_CLICK_TRIGGER,
ROW_CLICK_TRIGGER,
} from '../../../../../../src/plugins/ui_actions/public';
@@ -32,7 +36,7 @@ import {
* Part of context scope extracted from an embeddable
* Expose on the scope as: `{{context.panel.id}}`, `{{context.panel.filters.[0]}}`
*/
-interface EmbeddableUrlDrilldownContextScope {
+interface EmbeddableUrlDrilldownContextScope extends EmbeddableInput {
/**
* ID of the embeddable panel.
*/
@@ -59,10 +63,8 @@ interface EmbeddableUrlDrilldownContextScope {
savedObjectId?: string;
}
-export function getPanelVariables(contextScopeInput: {
- embeddable?: IEmbeddable;
-}): EmbeddableUrlDrilldownContextScope {
- function hasEmbeddable(val: unknown): val is { embeddable: IEmbeddable } {
+export function getPanelVariables(contextScopeInput: unknown): EmbeddableUrlDrilldownContextScope {
+ function hasEmbeddable(val: unknown): val is { embeddable: EmbeddableWithQueryInput } {
if (val && typeof val === 'object' && 'embeddable' in val) return true;
return false;
}
@@ -99,7 +101,7 @@ function getIndexPatternIds(output: EmbeddableOutput): string[] {
}
export function getEmbeddableVariables(
- embeddable: IEmbeddable
+ embeddable: EmbeddableWithQueryInput
): EmbeddableUrlDrilldownContextScope {
const input = embeddable.getInput();
const output = embeddable.getOutput();
@@ -195,10 +197,10 @@ function getEventScopeFromValueClickTriggerContext(
});
}
-function getEventScopeFromRowClickTriggerContext({
- embeddable,
- data,
-}: RowClickContext): RowClickTriggerEventScope {
+function getEventScopeFromRowClickTriggerContext(ctx: RowClickContext): RowClickTriggerEventScope {
+ const { data } = ctx;
+ const embeddable = ctx.embeddable as EmbeddableWithQueryInput;
+
const { rowIndex } = data;
const columns = data.columns || data.table.columns.map(({ id }) => id);
const values: Primitive[] = [];
diff --git a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts
index 9856d3a558e24..8361b002c8206 100644
--- a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts
+++ b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts
@@ -12,7 +12,7 @@ import {
import { UiActionsEnhancedSerializedEvent } from '../../../ui_actions_enhanced/public';
import { of } from '../../../../../src/plugins/kibana_utils/public';
// use real const to make test fail in case someone accidentally changes it
-import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/data/public';
class TestEmbeddable extends Embeddable {
public readonly type = 'test';
diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
index 190470e6c4235..b00760e9664f3 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
@@ -53,11 +53,17 @@ import { LensAttributeService } from '../../lens_attribute_service';
export type LensSavedObjectAttributes = Omit;
+interface LensBaseEmbeddableInput extends EmbeddableInput {
+ filters?: Filter[];
+ query?: Query;
+ timeRange?: TimeRange;
+}
+
export type LensByValueInput = {
attributes: LensSavedObjectAttributes;
-} & EmbeddableInput;
+} & LensBaseEmbeddableInput;
-export type LensByReferenceInput = SavedObjectEmbeddableInput & EmbeddableInput;
+export type LensByReferenceInput = SavedObjectEmbeddableInput & LensBaseEmbeddableInput;
export type LensEmbeddableInput = (LensByValueInput | LensByReferenceInput) & {
palette?: PaletteOutput;
renderMode?: RenderMode;
diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts
index d5283822107e4..57308f9c18b40 100644
--- a/x-pack/plugins/lens/public/types.ts
+++ b/x-pack/plugins/lens/public/types.ts
@@ -20,12 +20,11 @@ import { DragContextState } from './drag_drop';
import { Document } from './persistence';
import { DateRange } from '../common';
import { Query, Filter, SavedQuery, IFieldFormat } from '../../../../src/plugins/data/public';
+import { TriggerContext, VisualizeFieldContext } from '../../../../src/plugins/ui_actions/public';
import {
SELECT_RANGE_TRIGGER,
- TriggerContext,
VALUE_CLICK_TRIGGER,
- VisualizeFieldContext,
-} from '../../../../src/plugins/ui_actions/public';
+} from '../../../../src/plugins/embeddable/public';
import type {
LensSortActionData,
LENS_EDIT_SORT_ACTION,
diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
index 7aaabc427790a..5e5d431c7c212 100644
--- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
+++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
@@ -14,15 +14,15 @@ import {
Embeddable,
IContainer,
ReferenceOrValueEmbeddable,
+ VALUE_CLICK_TRIGGER,
} from '../../../../../src/plugins/embeddable/public';
-import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../src/plugins/data/public';
import {
- APPLY_FILTER_TRIGGER,
- VALUE_CLICK_TRIGGER,
ActionExecutionContext,
TriggerContextMapping,
} from '../../../../../src/plugins/ui_actions/public';
import {
+ ACTION_GLOBAL_APPLY_FILTER,
+ APPLY_FILTER_TRIGGER,
esFilters,
TimeRange,
Filter,
diff --git a/x-pack/plugins/maps/public/embeddable/types.ts b/x-pack/plugins/maps/public/embeddable/types.ts
index a401cafcff8ea..417991ea2d367 100644
--- a/x-pack/plugins/maps/public/embeddable/types.ts
+++ b/x-pack/plugins/maps/public/embeddable/types.ts
@@ -10,7 +10,7 @@ import {
EmbeddableOutput,
SavedObjectEmbeddableInput,
} from '../../../../../src/plugins/embeddable/public';
-import { RefreshInterval } from '../../../../../src/plugins/data/common';
+import { RefreshInterval, Query, Filter, TimeRange } from '../../../../../src/plugins/data/common';
import { MapCenterAndZoom } from '../../common/descriptor_types';
import { MapSavedObjectAttributes } from '../../common/map_saved_object_type';
@@ -30,6 +30,9 @@ interface MapEmbeddableState {
mapCenter?: MapCenterAndZoom;
hiddenLayers?: string[];
hideFilterActions?: boolean;
+ filters?: Filter[];
+ query?: Query;
+ timeRange?: TimeRange;
}
export type MapByValueInput = {
attributes: MapSavedObjectAttributes;
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 1023c7ac7d889..e38058a312f3c 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -3490,12 +3490,12 @@
"uiActions.actionPanel.more": "詳細",
"uiActions.actionPanel.title": "オプション",
"uiActions.errors.incompatibleAction": "操作に互換性がありません",
- "uiActions.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。",
- "uiActions.triggers.applyFilterTitle": "フィルターを適用",
- "uiActions.triggers.selectRangeDescription": "ビジュアライゼーションでの値の範囲",
- "uiActions.triggers.selectRangeTitle": "範囲選択",
- "uiActions.triggers.valueClickDescription": "ビジュアライゼーションでデータポイントをクリック",
- "uiActions.triggers.valueClickTitle": "シングルクリック",
+ "data.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。",
+ "data.triggers.applyFilterTitle": "フィルターを適用",
+ "embeddableApi.selectRangeTrigger.description": "ビジュアライゼーションでの値の範囲",
+ "embeddableApi.selectRangeTrigger.title": "範囲選択",
+ "embeddableApi.valueClickTrigger.description": "ビジュアライゼーションでデータポイントをクリック",
+ "embeddableApi.valueClickTrigger.title": "シングルクリック",
"usageCollection.stats.notReadyMessage": "まだ統計が準備できていません。しばらくたってから再試行してください。",
"visDefaultEditor.advancedToggle.advancedLinkLabel": "高度な設定",
"visDefaultEditor.agg.toggleEditorButtonAriaLabel": "{schema} エディターを切り替える",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index f803ff48df76d..17bcd61898efe 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -3491,12 +3491,12 @@
"uiActions.actionPanel.more": "更多",
"uiActions.actionPanel.title": "选项",
"uiActions.errors.incompatibleAction": "操作不兼容",
- "uiActions.triggers.applyFilterDescription": "应用 kibana 筛选时。可能是单个值或范围筛选。",
- "uiActions.triggers.applyFilterTitle": "应用筛选",
- "uiActions.triggers.selectRangeDescription": "可视化上的一组值",
- "uiActions.triggers.selectRangeTitle": "范围选择",
- "uiActions.triggers.valueClickDescription": "可视化上的数据点单击",
- "uiActions.triggers.valueClickTitle": "单击",
+ "data.triggers.applyFilterDescription": "应用 kibana 筛选时。可能是单个值或范围筛选。",
+ "data.triggers.applyFilterTitle": "应用筛选",
+ "embeddableApi.selectRangeTrigger.description": "可视化上的一组值",
+ "embeddableApi.selectRangeTrigger.title": "范围选择",
+ "embeddableApi.valueClickTrigger.description": "可视化上的数据点单击",
+ "embeddableApi.valueClickTrigger.title": "单击",
"usageCollection.stats.notReadyMessage": "统计信息尚未准备就绪。请稍后重试。",
"visDefaultEditor.advancedToggle.advancedLinkLabel": "高级",
"visDefaultEditor.agg.toggleEditorButtonAriaLabel": "切换 {schema} 编辑器",
diff --git a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx
index 9bb506b3ebf14..77362752f6960 100644
--- a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx
+++ b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx
@@ -11,13 +11,12 @@ import { ActionWizard } from './action_wizard';
import { ActionFactory, ActionFactoryDefinition, BaseActionConfig } from '../../dynamic_actions';
import { CollectConfigProps } from '../../../../../../src/plugins/kibana_utils/public';
import { licensingMock } from '../../../../licensing/public/mocks';
+import { Trigger, TriggerId } from '../../../../../../src/plugins/ui_actions/public';
+import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/data/public';
import {
- APPLY_FILTER_TRIGGER,
SELECT_RANGE_TRIGGER,
- Trigger,
- TriggerId,
VALUE_CLICK_TRIGGER,
-} from '../../../../../../src/plugins/ui_actions/public';
+} from '../../../../../../src/plugins/embeddable/public';
export const dashboards = [
{ id: 'dashboard1', title: 'Dashboard 1' },
From e8b21bc6c12cfd793c46e1d86577d5e5ec8a71f8 Mon Sep 17 00:00:00 2001
From: Thomas Watson
Date: Sat, 19 Dec 2020 13:10:11 +0100
Subject: [PATCH 28/40] Upgrade to hapi version 20 (#85406)
---
...in-core-server.legacyelasticsearcherror.md | 2 +-
package.json | 35 +-
.../server/elasticsearch/legacy/errors.ts | 4 +-
src/core/server/http/http_server.test.ts | 4 +-
src/core/server/http/http_server.ts | 58 +-
.../server/http/lifecycle/on_pre_response.ts | 12 +-
src/core/server/http/router/error_wrapper.ts | 2 +-
.../server/http/router/response_adapter.ts | 7 +-
src/core/server/http/router/router.ts | 5 +-
.../saved_objects/service/lib/errors.ts | 2 +-
src/core/server/server.api.md | 2 +-
.../server/routes/fields.ts | 2 +-
.../apm/server/routes/create_api/index.ts | 2 +-
.../plugins/case/server/routes/api/utils.ts | 2 +-
.../fleet/server/errors/handlers.test.ts | 4 +-
.../plugins/fleet/server/errors/handlers.ts | 2 +-
.../epm/elasticsearch/template/install.ts | 4 +-
.../server/services/epm/packages/install.ts | 2 +-
.../lens/server/routes/existing_fields.ts | 2 +-
.../ml/common/types/data_frame_analytics.ts | 2 +-
.../ml/common/util/errors/errors.test.ts | 3 +-
x-pack/plugins/ml/common/util/errors/types.ts | 9 +-
.../plugins/ml/server/client/error_wrapper.ts | 2 +-
x-pack/plugins/monitoring/server/plugin.ts | 2 +-
.../reporting/server/routes/generation.ts | 4 +-
.../authentication_service.test.ts | 2 +-
.../authentication/providers/kerberos.ts | 4 +-
.../lib/detection_engine/routes/utils.test.ts | 6 +-
.../lib/create_empty_failure_response.ts | 4 +-
x-pack/plugins/spaces/server/lib/errors.ts | 2 +-
.../server/routes/api/error_utils.ts | 5 +-
yarn.lock | 579 ++++++++----------
32 files changed, 349 insertions(+), 428 deletions(-)
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md b/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md
index 40fc1a8e05a68..7c53356615ee9 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md
@@ -9,7 +9,7 @@
Signature:
```typescript
-export interface LegacyElasticsearchError extends Boom
+export interface LegacyElasticsearchError extends Boom.Boom
```
## Properties
diff --git a/package.json b/package.json
index 8e99b5c693cec..252c8130c38d6 100644
--- a/package.json
+++ b/package.json
@@ -73,10 +73,6 @@
"url": "https://github.com/elastic/kibana.git"
},
"resolutions": {
- "**/@hapi/iron": "^5.1.4",
- "**/@types/hapi__boom": "^7.4.1",
- "**/@types/hapi__hapi": "^18.2.6",
- "**/@types/hapi__mimos": "4.1.0",
"**/@types/node": "14.14.14",
"**/chokidar": "^3.4.3",
"**/cross-fetch/node-fetch": "^2.6.1",
@@ -115,17 +111,17 @@
"@elastic/request-crypto": "1.1.4",
"@elastic/safer-lodash-set": "link:packages/elastic-safer-lodash-set",
"@elastic/search-ui-app-search-connector": "^1.5.0",
- "@hapi/boom": "^7.4.11",
- "@hapi/cookie": "^10.1.2",
- "@hapi/good-squeeze": "5.2.1",
- "@hapi/h2o2": "^8.3.2",
- "@hapi/hapi": "^18.4.1",
- "@hapi/hoek": "^8.5.1",
- "@hapi/inert": "^5.2.2",
- "@hapi/podium": "^3.4.3",
- "@hapi/statehood": "^6.1.2",
- "@hapi/vision": "^5.5.4",
- "@hapi/wreck": "^15.0.2",
+ "@hapi/boom": "^9.1.1",
+ "@hapi/cookie": "^11.0.2",
+ "@hapi/good-squeeze": "6.0.0",
+ "@hapi/h2o2": "^9.0.2",
+ "@hapi/hapi": "^20.0.3",
+ "@hapi/hoek": "^9.1.0",
+ "@hapi/inert": "^6.0.3",
+ "@hapi/podium": "^4.1.1",
+ "@hapi/statehood": "^7.0.3",
+ "@hapi/vision": "^6.0.1",
+ "@hapi/wreck": "^17.1.0",
"@kbn/ace": "link:packages/kbn-ace",
"@kbn/analytics": "link:packages/kbn-analytics",
"@kbn/apm-config-loader": "link:packages/kbn-apm-config-loader",
@@ -452,14 +448,11 @@
"@types/graphql": "^0.13.2",
"@types/gulp": "^4.0.6",
"@types/gulp-zip": "^4.0.1",
- "@types/hapi__boom": "^7.4.1",
"@types/hapi__cookie": "^10.1.1",
- "@types/hapi__h2o2": "8.3.0",
- "@types/hapi__hapi": "^18.2.6",
- "@types/hapi__hoek": "^6.2.0",
- "@types/hapi__inert": "^5.2.1",
+ "@types/hapi__h2o2": "^8.3.2",
+ "@types/hapi__hapi": "^20.0.2",
+ "@types/hapi__inert": "^5.2.2",
"@types/hapi__podium": "^3.4.1",
- "@types/hapi__wreck": "^15.0.1",
"@types/has-ansi": "^3.0.0",
"@types/he": "^1.1.1",
"@types/history": "^4.7.3",
diff --git a/src/core/server/elasticsearch/legacy/errors.ts b/src/core/server/elasticsearch/legacy/errors.ts
index e557e7395fe56..adc1fa0728784 100644
--- a/src/core/server/elasticsearch/legacy/errors.ts
+++ b/src/core/server/elasticsearch/legacy/errors.ts
@@ -30,7 +30,7 @@ enum ErrorCode {
* @deprecated. The new elasticsearch client doesn't wrap errors anymore.
* @public
* */
-export interface LegacyElasticsearchError extends Boom {
+export interface LegacyElasticsearchError extends Boom.Boom {
[code]?: string;
}
@@ -86,7 +86,7 @@ export class LegacyElasticsearchErrorHelpers {
const decoratedError = decorate(error, ErrorCode.NOT_AUTHORIZED, 401, reason);
const wwwAuthHeader = get(error, 'body.error.header[WWW-Authenticate]') as string;
- decoratedError.output.headers['WWW-Authenticate'] =
+ (decoratedError.output.headers as { [key: string]: string })['WWW-Authenticate'] =
wwwAuthHeader || 'Basic realm="Authorization Required"';
return decoratedError;
diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts
index 71040598d34b1..cbb60480c4cf1 100644
--- a/src/core/server/http/http_server.test.ts
+++ b/src/core/server/http/http_server.test.ts
@@ -1214,7 +1214,7 @@ describe('timeout options', () => {
router.get(
{
path: '/',
- validate: { body: schema.any() },
+ validate: { body: schema.maybe(schema.any()) },
},
(context, req, res) => {
return res.ok({
@@ -1247,7 +1247,7 @@ describe('timeout options', () => {
router.get(
{
path: '/',
- validate: { body: schema.any() },
+ validate: { body: schema.maybe(schema.any()) },
options: { timeout: { idleSocket: 12000 } },
},
(context, req, res) => {
diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts
index 43f5264ff22e3..42e89b66d9c51 100644
--- a/src/core/server/http/http_server.ts
+++ b/src/core/server/http/http_server.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Server } from '@hapi/hapi';
+import { Server, ServerRoute } from '@hapi/hapi';
import HapiStaticFiles from '@hapi/inert';
import url from 'url';
import uuid from 'uuid';
@@ -167,8 +167,6 @@ export class HttpServer {
for (const router of this.registeredRouters) {
for (const route of router.getRoutes()) {
this.log.debug(`registering route handler for [${route.path}]`);
- // Hapi does not allow payload validation to be specified for 'head' or 'get' requests
- const validate = isSafeMethod(route.method) ? undefined : { payload: true };
const { authRequired, tags, body = {}, timeout } = route.options;
const { accepts: allow, maxBytes, output, parse } = body;
@@ -176,57 +174,45 @@ export class HttpServer {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
};
- // To work around https://github.com/hapijs/hapi/issues/4122 until v20, set the socket
- // timeout on the route to a fake timeout only when the payload timeout is specified.
- // Within the onPreAuth lifecycle of the route itself, we'll override the timeout with the
- // real socket timeout.
- const fakeSocketTimeout = timeout?.payload ? timeout.payload + 1 : undefined;
-
- this.server.route({
+ const routeOpts: ServerRoute = {
handler: route.handler,
method: route.method,
path: route.path,
options: {
auth: this.getAuthOption(authRequired),
app: kibanaRouteOptions,
- ext: {
- onPreAuth: {
- method: (request, h) => {
- // At this point, the socket timeout has only been set to work-around the HapiJS bug.
- // We need to either set the real per-route timeout or use the default idle socket timeout
- if (timeout?.idleSocket) {
- request.raw.req.socket.setTimeout(timeout.idleSocket);
- } else if (fakeSocketTimeout) {
- // NodeJS uses a socket timeout of `0` to denote "no timeout"
- request.raw.req.socket.setTimeout(this.config!.socketTimeout ?? 0);
- }
-
- return h.continue;
- },
- },
- },
tags: tags ? Array.from(tags) : undefined,
- // TODO: This 'validate' section can be removed once the legacy platform is completely removed.
- // We are telling Hapi that NP routes can accept any payload, so that it can bypass the default
- // validation applied in ./http_tools#getServerOptions
- // (All NP routes are already required to specify their own validation in order to access the payload)
- validate,
- payload: [allow, maxBytes, output, parse, timeout?.payload].some(
- (v) => typeof v !== 'undefined'
- )
+ // @ts-expect-error Types are outdated and doesn't allow `payload.multipart` to be `true`
+ payload: [allow, maxBytes, output, parse, timeout?.payload].some((x) => x !== undefined)
? {
allow,
maxBytes,
output,
parse,
timeout: timeout?.payload,
+ multipart: true,
}
: undefined,
timeout: {
- socket: fakeSocketTimeout,
+ socket: timeout?.idleSocket ?? this.config!.socketTimeout,
},
},
- });
+ };
+
+ // Hapi does not allow payload validation to be specified for 'head' or 'get' requests
+ if (!isSafeMethod(route.method)) {
+ // TODO: This 'validate' section can be removed once the legacy platform is completely removed.
+ // We are telling Hapi that NP routes can accept any payload, so that it can bypass the default
+ // validation applied in ./http_tools#getServerOptions
+ // (All NP routes are already required to specify their own validation in order to access the payload)
+ // TODO: Move the setting of the validate option back up to being set at `routeOpts` creation-time once
+ // https://github.com/hapijs/hoek/pull/365 is merged and released in @hapi/hoek v9.1.1. At that point I
+ // imagine the ts-error below will go away as well.
+ // @ts-expect-error "Property 'validate' does not exist on type 'RouteOptions'" <-- ehh?!? yes it does!
+ routeOpts.options!.validate = { payload: true };
+ }
+
+ this.server.route(routeOpts);
}
}
diff --git a/src/core/server/http/lifecycle/on_pre_response.ts b/src/core/server/http/lifecycle/on_pre_response.ts
index 42179374ec672..9efcf46148e1f 100644
--- a/src/core/server/http/lifecycle/on_pre_response.ts
+++ b/src/core/server/http/lifecycle/on_pre_response.ts
@@ -142,7 +142,11 @@ export function adoptToHapiOnPreResponseFormat(fn: OnPreResponseHandler, log: Lo
if (preResponseResult.isNext(result)) {
if (result.headers) {
if (isBoom(response)) {
- findHeadersIntersection(response.output.headers, result.headers, log);
+ findHeadersIntersection(
+ response.output.headers as { [key: string]: string },
+ result.headers,
+ log
+ );
// hapi wraps all error response in Boom object internally
response.output.headers = {
...response.output.headers,
@@ -157,7 +161,7 @@ export function adoptToHapiOnPreResponseFormat(fn: OnPreResponseHandler, log: Lo
const overriddenResponse = responseToolkit.response(result.body).code(statusCode);
const originalHeaders = isBoom(response) ? response.output.headers : response.headers;
- setHeaders(overriddenResponse, originalHeaders);
+ setHeaders(overriddenResponse, originalHeaders as { [key: string]: string });
if (result.headers) {
setHeaders(overriddenResponse, result.headers);
}
@@ -178,8 +182,8 @@ export function adoptToHapiOnPreResponseFormat(fn: OnPreResponseHandler, log: Lo
};
}
-function isBoom(response: any): response is Boom {
- return response instanceof Boom;
+function isBoom(response: any): response is Boom.Boom {
+ return response instanceof Boom.Boom;
}
function setHeaders(response: ResponseObject, headers: ResponseHeaders) {
diff --git a/src/core/server/http/router/error_wrapper.ts b/src/core/server/http/router/error_wrapper.ts
index 5a4b7e9f77582..7d141e81ddf36 100644
--- a/src/core/server/http/router/error_wrapper.ts
+++ b/src/core/server/http/router/error_wrapper.ts
@@ -29,7 +29,7 @@ export const wrapErrors: RequestHandlerWrapper = (handler) => {
return response.customError({
body: e.output.payload,
statusCode: e.output.statusCode,
- headers: e.output.headers,
+ headers: e.output.headers as { [key: string]: string },
});
}
throw e;
diff --git a/src/core/server/http/router/response_adapter.ts b/src/core/server/http/router/response_adapter.ts
index 63acd2207ac3a..d80c21bde8de8 100644
--- a/src/core/server/http/router/response_adapter.ts
+++ b/src/core/server/http/router/response_adapter.ts
@@ -56,7 +56,7 @@ export class HapiResponseAdapter {
}
public toInternalError() {
- const error = new Boom('', {
+ const error = new Boom.Boom('', {
statusCode: 500,
});
@@ -129,7 +129,7 @@ export class HapiResponseAdapter {
}
// we use for BWC with Boom payload for error responses - {error: string, message: string, statusCode: string}
- const error = new Boom('', {
+ const error = new Boom.Boom('', {
statusCode: kibanaResponse.status,
});
@@ -142,8 +142,7 @@ export class HapiResponseAdapter {
const headers = kibanaResponse.options.headers;
if (headers) {
- // Hapi typings for header accept only strings, although string[] is a valid value
- error.output.headers = headers as any;
+ error.output.headers = headers;
}
return error;
diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts
index b1e092ba5786a..ebc41a793f3b3 100644
--- a/src/core/server/http/router/router.ts
+++ b/src/core/server/http/router/router.ts
@@ -44,7 +44,10 @@ interface RouterRoute {
method: RouteMethod;
path: string;
options: RouteConfigOptions;
- handler: (req: Request, responseToolkit: ResponseToolkit) => Promise>;
+ handler: (
+ req: Request,
+ responseToolkit: ResponseToolkit
+ ) => Promise>;
}
/**
diff --git a/src/core/server/saved_objects/service/lib/errors.ts b/src/core/server/saved_objects/service/lib/errors.ts
index e8836dbd8f7a1..c6c8eee003e4e 100644
--- a/src/core/server/saved_objects/service/lib/errors.ts
+++ b/src/core/server/saved_objects/service/lib/errors.ts
@@ -44,7 +44,7 @@ const CODE_GENERAL_ERROR = 'SavedObjectsClient/generalError';
const code = Symbol('SavedObjectsClientErrorCode');
-export interface DecoratedError extends Boom {
+export interface DecoratedError extends Boom.Boom {
[code]?: string;
}
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 5f07a4b523056..cef5f33726ed5 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -1541,7 +1541,7 @@ export type LegacyElasticsearchClientConfig = Pick {
return res.customError({
body: err.output.payload,
statusCode: err.output.statusCode,
- headers: err.output.headers,
+ headers: err.output.headers as { [key: string]: string },
});
}
diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts
index ef445617e9295..94711cf76c145 100644
--- a/x-pack/plugins/apm/server/routes/create_api/index.ts
+++ b/x-pack/plugins/apm/server/routes/create_api/index.ts
@@ -140,7 +140,7 @@ export function createApi() {
}
function convertBoomToKibanaResponse(
- error: Boom,
+ error: Boom.Boom,
response: KibanaResponseFactory
) {
const opts = { body: error.message };
diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts
index c8753772648c2..917afb487b1f4 100644
--- a/x-pack/plugins/case/server/routes/api/utils.ts
+++ b/x-pack/plugins/case/server/routes/api/utils.ts
@@ -96,7 +96,7 @@ export function wrapError(error: any): CustomHttpResponseOptions
const boom = isBoom(error) ? error : boomify(error, options);
return {
body: boom,
- headers: boom.output.headers,
+ headers: boom.output.headers as { [key: string]: string },
statusCode: boom.output.statusCode,
};
}
diff --git a/x-pack/plugins/fleet/server/errors/handlers.test.ts b/x-pack/plugins/fleet/server/errors/handlers.test.ts
index 4f8eff6c6c75a..98139e9802fec 100644
--- a/x-pack/plugins/fleet/server/errors/handlers.test.ts
+++ b/x-pack/plugins/fleet/server/errors/handlers.test.ts
@@ -162,7 +162,7 @@ describe('defaultIngestErrorHandler', () => {
describe('Boom', () => {
it('500: constructor - one arg', async () => {
- const error = new Boom('bam');
+ const error = new Boom.Boom('bam');
const response = httpServerMock.createResponseFactory();
await defaultIngestErrorHandler({ error, response });
@@ -181,7 +181,7 @@ describe('defaultIngestErrorHandler', () => {
});
it('custom: constructor - 2 args', async () => {
- const error = new Boom('Problem doing something', {
+ const error = new Boom.Boom('Problem doing something', {
statusCode: 456,
});
const response = httpServerMock.createResponseFactory();
diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts
index fecfcf145ca99..e0b05d972bd38 100644
--- a/x-pack/plugins/fleet/server/errors/handlers.ts
+++ b/x-pack/plugins/fleet/server/errors/handlers.ts
@@ -27,7 +27,7 @@ type IngestErrorHandler = (
params: IngestErrorHandlerParams
) => IKibanaResponse | Promise;
interface IngestErrorHandlerParams {
- error: IngestManagerError | Boom | Error;
+ error: IngestManagerError | Boom.Boom | Error;
response: KibanaResponseFactory;
request?: KibanaRequest;
context?: RequestHandlerContext;
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts
index 8b018f4a2a906..ceedc87275393 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts
@@ -109,7 +109,7 @@ const installPreBuiltTemplates = async (paths: string[], callCluster: CallESAsCu
try {
return await Promise.all(templateInstallPromises);
} catch (e) {
- throw new Boom(`Error installing prebuilt index templates ${e.message}`, {
+ throw new Boom.Boom(`Error installing prebuilt index templates ${e.message}`, {
statusCode: 400,
});
}
@@ -144,7 +144,7 @@ const installPreBuiltComponentTemplates = async (
try {
return await Promise.all(templateInstallPromises);
} catch (e) {
- throw new Boom(`Error installing prebuilt component templates ${e.message}`, {
+ throw new Boom.Boom(`Error installing prebuilt component templates ${e.message}`, {
statusCode: 400,
});
}
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts
index 29300818288b4..48dd589dd0b8f 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts
@@ -122,7 +122,7 @@ export async function handleInstallPackageFailure({
callCluster,
}: {
savedObjectsClient: SavedObjectsClientContract;
- error: IngestManagerError | Boom | Error;
+ error: IngestManagerError | Boom.Boom | Error;
pkgName: string;
pkgVersion: string;
installedPkg: SavedObject | undefined;
diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts
index 43c56af7f71bc..3de3c6c7187c6 100644
--- a/x-pack/plugins/lens/server/routes/existing_fields.ts
+++ b/x-pack/plugins/lens/server/routes/existing_fields.ts
@@ -14,7 +14,7 @@ import { BASE_API_URL } from '../../common';
import { UI_SETTINGS } from '../../../../../src/plugins/data/server';
import { PluginStartContract } from '../plugin';
-export function isBoomError(error: { isBoom?: boolean }): error is Boom {
+export function isBoomError(error: { isBoom?: boolean }): error is Boom.Boom {
return error.isBoom === true;
}
diff --git a/x-pack/plugins/ml/common/types/data_frame_analytics.ts b/x-pack/plugins/ml/common/types/data_frame_analytics.ts
index 3455b833c404a..e87e29a36084b 100644
--- a/x-pack/plugins/ml/common/types/data_frame_analytics.ts
+++ b/x-pack/plugins/ml/common/types/data_frame_analytics.ts
@@ -11,7 +11,7 @@ import { DATA_FRAME_TASK_STATE } from '../constants/data_frame_analytics';
export interface DeleteDataFrameAnalyticsWithIndexStatus {
success: boolean;
- error?: EsErrorBody | Boom;
+ error?: EsErrorBody | Boom.Boom;
}
export type IndexName = string;
diff --git a/x-pack/plugins/ml/common/util/errors/errors.test.ts b/x-pack/plugins/ml/common/util/errors/errors.test.ts
index 166264ebddee1..6f07c0150630d 100644
--- a/x-pack/plugins/ml/common/util/errors/errors.test.ts
+++ b/x-pack/plugins/ml/common/util/errors/errors.test.ts
@@ -76,8 +76,9 @@ describe('ML - error message utils', () => {
expect(extractErrorMessage(bodyWithAttributes)).toBe(testMsg);
// boom error
- const boomError: Boom = {
+ const boomError: Boom.Boom = {
message: '',
+ typeof: Boom.Boom.constructor,
reformat: () => '',
name: '',
data: [],
diff --git a/x-pack/plugins/ml/common/util/errors/types.ts b/x-pack/plugins/ml/common/util/errors/types.ts
index 667e6e34a5640..b6d43d8e0cf92 100644
--- a/x-pack/plugins/ml/common/util/errors/types.ts
+++ b/x-pack/plugins/ml/common/util/errors/types.ts
@@ -45,7 +45,12 @@ export interface MLHttpFetchError extends HttpFetchError {
body: T;
}
-export type ErrorType = MLHttpFetchError | EsErrorBody | Boom | string | undefined;
+export type ErrorType =
+ | MLHttpFetchError
+ | EsErrorBody
+ | Boom.Boom
+ | string
+ | undefined;
export function isEsErrorBody(error: any): error is EsErrorBody {
return error && error.error?.reason !== undefined;
@@ -63,6 +68,6 @@ export function isMLResponseError(error: any): error is MLResponseError {
return typeof error.body === 'object' && 'message' in error.body;
}
-export function isBoomError(error: any): error is Boom {
+export function isBoomError(error: any): error is Boom.Boom {
return error.isBoom === true;
}
diff --git a/x-pack/plugins/ml/server/client/error_wrapper.ts b/x-pack/plugins/ml/server/client/error_wrapper.ts
index e2e97e3a87696..fb41d30e34fae 100644
--- a/x-pack/plugins/ml/server/client/error_wrapper.ts
+++ b/x-pack/plugins/ml/server/client/error_wrapper.ts
@@ -17,7 +17,7 @@ export function wrapError(error: any): CustomHttpResponseOptions
message: boom,
...(statusCode !== 500 && error.body ? { attributes: { body: error.body } } : {}),
},
- headers: boom.output.headers,
+ headers: boom.output.headers as { [key: string]: string },
statusCode,
};
}
diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts
index 9478e24c9560f..22ea6c31dbe69 100644
--- a/x-pack/plugins/monitoring/server/plugin.ts
+++ b/x-pack/plugins/monitoring/server/plugin.ts
@@ -59,7 +59,7 @@ const wrapError = (error: any): CustomHttpResponseOptions => {
const boom = Boom.isBoom(error) ? error : Boom.boomify(error, options);
return {
body: boom,
- headers: boom.output.headers,
+ headers: boom.output.headers as { [key: string]: string },
statusCode: boom.output.statusCode,
};
};
diff --git a/x-pack/plugins/reporting/server/routes/generation.ts b/x-pack/plugins/reporting/server/routes/generation.ts
index ba69b97bee7f1..064cdf0b28eb9 100644
--- a/x-pack/plugins/reporting/server/routes/generation.ts
+++ b/x-pack/plugins/reporting/server/routes/generation.ts
@@ -68,8 +68,8 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo
/*
* Error should already have been logged by the time we get here
*/
- function handleError(res: typeof kibanaResponseFactory, err: Error | Boom) {
- if (err instanceof Boom) {
+ function handleError(res: typeof kibanaResponseFactory, err: Error | Boom.Boom) {
+ if (err instanceof Boom.Boom) {
return res.customError({
statusCode: err.output.statusCode,
body: err.output.payload.message,
diff --git a/x-pack/plugins/security/server/authentication/authentication_service.test.ts b/x-pack/plugins/security/server/authentication/authentication_service.test.ts
index d81702691a3a1..244cf1d0a8f51 100644
--- a/x-pack/plugins/security/server/authentication/authentication_service.test.ts
+++ b/x-pack/plugins/security/server/authentication/authentication_service.test.ts
@@ -243,7 +243,7 @@ describe('AuthenticationService', () => {
it('includes `WWW-Authenticate` header if `authenticate` fails to authenticate user and provides challenges', async () => {
const mockResponse = httpServerMock.createLifecycleResponseFactory();
const originalError = Boom.unauthorized('some message');
- originalError.output.headers['WWW-Authenticate'] = [
+ (originalError.output.headers as { [key: string]: string })['WWW-Authenticate'] = [
'Basic realm="Access to prod", charset="UTF-8"',
'Basic',
'Negotiate',
diff --git a/x-pack/plugins/security/server/authentication/providers/kerberos.ts b/x-pack/plugins/security/server/authentication/providers/kerberos.ts
index 9bf419c7dacaa..b7abed979164e 100644
--- a/x-pack/plugins/security/server/authentication/providers/kerberos.ts
+++ b/x-pack/plugins/security/server/authentication/providers/kerberos.ts
@@ -333,7 +333,9 @@ export class KerberosAuthenticationProvider extends BaseAuthenticationProvider {
* @param error Error to extract challenges from.
*/
private getNegotiateChallenge(error: LegacyElasticsearchError) {
- const challenges = ([] as string[]).concat(error.output.headers[WWWAuthenticateHeaderName]);
+ const challenges = ([] as string[]).concat(
+ (error.output.headers as { [key: string]: string })[WWWAuthenticateHeaderName]
+ );
const negotiateChallenge = challenges.find((challenge) =>
challenge.toLowerCase().startsWith('negotiate')
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts
index 36131c2e2844d..32c1d8d3cdf56 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts
@@ -35,7 +35,7 @@ let alertsClient: ReturnType;
describe('utils', () => {
describe('transformError', () => {
test('returns transformed output error from boom object with a 500 and payload of internal server error', () => {
- const boom = new Boom('some boom message');
+ const boom = new Boom.Boom('some boom message');
const transformed = transformError(boom);
expect(transformed).toEqual({
message: 'An internal server error occurred',
@@ -124,7 +124,7 @@ describe('utils', () => {
describe('transformBulkError', () => {
test('returns transformed object if it is a boom object', () => {
- const boom = new Boom('some boom message', { statusCode: 400 });
+ const boom = new Boom.Boom('some boom message', { statusCode: 400 });
const transformed = transformBulkError('rule-1', boom);
const expected: BulkError = {
rule_id: 'rule-1',
@@ -252,7 +252,7 @@ describe('utils', () => {
describe('transformImportError', () => {
test('returns transformed object if it is a boom object', () => {
- const boom = new Boom('some boom message', { statusCode: 400 });
+ const boom = new Boom.Boom('some boom message', { statusCode: 400 });
const transformed = transformImportError('rule-1', boom, {
success_count: 1,
success: false,
diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts
index 764ae5a87ec0e..3980eef7caac2 100644
--- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts
+++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts
@@ -6,10 +6,10 @@
import Boom, { Payload } from '@hapi/boom';
import { SavedObjectsImportError } from 'src/core/server';
-export const createEmptyFailureResponse = (errors?: Array) => {
+export const createEmptyFailureResponse = (errors?: Array) => {
const errorMessages: Array = (errors || []).map((error) => {
if (Boom.isBoom(error as any)) {
- return (error as Boom).output.payload as Payload;
+ return (error as Boom.Boom).output.payload as Payload;
}
return error as SavedObjectsImportError;
});
diff --git a/x-pack/plugins/spaces/server/lib/errors.ts b/x-pack/plugins/spaces/server/lib/errors.ts
index 13a5c2440877a..0f6bf0f1d56b4 100644
--- a/x-pack/plugins/spaces/server/lib/errors.ts
+++ b/x-pack/plugins/spaces/server/lib/errors.ts
@@ -11,7 +11,7 @@ export function wrapError(error: any): CustomHttpResponseOptions
const boom = isBoom(error) ? error : boomify(error);
return {
body: boom,
- headers: boom.output.headers,
+ headers: boom.output.headers as { [key: string]: string },
statusCode: boom.output.statusCode,
};
}
diff --git a/x-pack/plugins/transform/server/routes/api/error_utils.ts b/x-pack/plugins/transform/server/routes/api/error_utils.ts
index 4986eb718dc2c..356158913eb92 100644
--- a/x-pack/plugins/transform/server/routes/api/error_utils.ts
+++ b/x-pack/plugins/transform/server/routes/api/error_utils.ts
@@ -79,7 +79,7 @@ export function wrapError(error: any): CustomHttpResponseOptions
const boom = Boom.isBoom(error) ? error : Boom.boomify(error, { statusCode: error.statusCode });
return {
body: boom,
- headers: boom.output.headers,
+ headers: boom.output.headers as { [key: string]: string },
statusCode: boom.output.statusCode,
};
}
@@ -130,7 +130,6 @@ export function wrapEsError(err: any, statusCodeToMessageMap: Record
Date: Sat, 19 Dec 2020 18:38:46 -0500
Subject: [PATCH 29/40] [Security Solutions] fix timeline tabs + layout
(#86581)
* fix timeline tabs + fix screenreader
* review
* fix jest tests
---
.../components/accessibility/helpers.test.ts | 21 ------
.../components/accessibility/helpers.test.tsx | 70 +++++++++++++++++++
.../components/accessibility/helpers.ts | 36 ++++++++++
.../index.test.tsx | 40 +++++++++++
.../tooltip_with_keyboard_shortcut/index.tsx | 8 +--
.../draggable_wrapper_hover_content.test.tsx | 48 ++++++-------
.../draggable_wrapper_hover_content.tsx | 1 +
.../events_viewer/events_viewer.tsx | 7 +-
.../public/common/components/page/index.tsx | 10 ++-
.../notes/note_cards/index.test.tsx | 36 +++++++---
.../components/notes/note_cards/index.tsx | 30 +++-----
.../open_timeline/note_previews/index.tsx | 12 +++-
.../note_previews/translations.ts | 13 ++++
.../__snapshots__/index.test.tsx.snap | 7 ++
.../body/data_driven_columns/index.test.tsx | 2 +
.../body/data_driven_columns/index.tsx | 32 +++++++--
.../body/data_driven_columns/translations.ts | 14 ++++
.../body/events/event_column_view.test.tsx | 2 +
.../body/events/event_column_view.tsx | 12 +++-
.../components/timeline/body/events/index.tsx | 8 +--
.../timeline/body/events/stateful_event.tsx | 36 ++++++++--
.../events/stateful_row_renderer/index.tsx | 57 +++++++++------
.../components/timeline/body/helpers.tsx | 6 ++
.../components/timeline/body/index.test.tsx | 2 +-
.../components/timeline/body/index.tsx | 13 ++--
.../body/renderers/get_row_renderer.test.tsx | 14 ++--
.../body/renderers/get_row_renderer.ts | 14 +---
.../timeline/body/renderers/index.ts | 2 -
.../timeline/pinned_tab_content/index.tsx | 3 +-
.../timeline/query_tab_content/index.tsx | 1 +
.../timeline/tabs_content/index.tsx | 2 +-
.../timeline/tabs_content/translations.ts | 6 +-
32 files changed, 409 insertions(+), 156 deletions(-)
delete mode 100644 x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.ts
create mode 100644 x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.tsx
create mode 100644 x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.test.tsx
diff --git a/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.ts
deleted file mode 100644
index c099a1413a88f..0000000000000
--- a/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { ariaIndexToArrayIndex, arrayIndexToAriaIndex } from './helpers';
-
-describe('helpers', () => {
- describe('ariaIndexToArrayIndex', () => {
- it('returns the expected array index', () => {
- expect(ariaIndexToArrayIndex(1)).toEqual(0);
- });
- });
-
- describe('arrayIndexToAriaIndex', () => {
- it('returns the expected aria index', () => {
- expect(arrayIndexToAriaIndex(0)).toEqual(1);
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.tsx
new file mode 100644
index 0000000000000..48db4b1f261b6
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.test.tsx
@@ -0,0 +1,70 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { mount } from 'enzyme';
+import React from 'react';
+
+import {
+ ariaIndexToArrayIndex,
+ arrayIndexToAriaIndex,
+ getNotesContainerClassName,
+ getRowRendererClassName,
+ isArrowRight,
+} from './helpers';
+
+describe('helpers', () => {
+ describe('ariaIndexToArrayIndex', () => {
+ test('it returns the expected array index', () => {
+ expect(ariaIndexToArrayIndex(1)).toEqual(0);
+ });
+ });
+
+ describe('arrayIndexToAriaIndex', () => {
+ test('it returns the expected aria index', () => {
+ expect(arrayIndexToAriaIndex(0)).toEqual(1);
+ });
+ });
+
+ describe('isArrowRight', () => {
+ test('it returns true if the right arrow key was pressed', () => {
+ let result = false;
+ const onKeyDown = (keyboardEvent: React.KeyboardEvent) => {
+ result = isArrowRight(keyboardEvent);
+ };
+
+ const wrapper = mount(
);
+ wrapper.find('div').simulate('keydown', { key: 'ArrowRight' });
+ wrapper.update();
+
+ expect(result).toBe(true);
+ });
+
+ test('it returns false if another key was pressed', () => {
+ let result = false;
+ const onKeyDown = (keyboardEvent: React.KeyboardEvent) => {
+ result = isArrowRight(keyboardEvent);
+ };
+
+ const wrapper = mount(
);
+ wrapper.find('div').simulate('keydown', { key: 'Enter' });
+ wrapper.update();
+
+ expect(result).toBe(false);
+ });
+ });
+
+ describe('getRowRendererClassName', () => {
+ test('it returns the expected class name', () => {
+ expect(getRowRendererClassName(2)).toBe('row-renderer-2');
+ });
+ });
+
+ describe('getNotesContainerClassName', () => {
+ test('it returns the expected class name', () => {
+ expect(getNotesContainerClassName(2)).toBe('notes-container-2');
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.ts b/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.ts
index d8603c9d02fcb..8fc535c680b26 100644
--- a/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.ts
+++ b/x-pack/plugins/security_solution/public/common/components/accessibility/helpers.ts
@@ -5,6 +5,11 @@
*/
import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '../drag_and_drop/helpers';
+import {
+ NOTES_CONTAINER_CLASS_NAME,
+ NOTE_CONTENT_CLASS_NAME,
+ ROW_RENDERER_CLASS_NAME,
+} from '../../../timelines/components/timeline/body/helpers';
import { HOVER_ACTIONS_ALWAYS_SHOW_CLASS_NAME } from '../with_hover_actions';
/**
@@ -63,6 +68,9 @@ export const isArrowDownOrArrowUp = (event: React.KeyboardEvent): boolean =>
export const isArrowKey = (event: React.KeyboardEvent): boolean =>
isArrowRightOrArrowLeft(event) || isArrowDownOrArrowUp(event);
+/** Returns `true` if the right arrow key was pressed */
+export const isArrowRight = (event: React.KeyboardEvent): boolean => event.key === 'ArrowRight';
+
/** Returns `true` if the escape key was pressed */
export const isEscape = (event: React.KeyboardEvent): boolean => event.key === 'Escape';
@@ -284,6 +292,12 @@ export type OnColumnFocused = ({
newFocusedColumnAriaColindex: number | null;
}) => void;
+export const getRowRendererClassName = (ariaRowindex: number) =>
+ `${ROW_RENDERER_CLASS_NAME}-${ariaRowindex}`;
+
+export const getNotesContainerClassName = (ariaRowindex: number) =>
+ `${NOTES_CONTAINER_CLASS_NAME}-${ariaRowindex}`;
+
/**
* This function implements arrow key support for the `onKeyDownFocusHandler`.
*
@@ -312,6 +326,28 @@ export const onArrowKeyDown = ({
onColumnFocused?: OnColumnFocused;
rowindexAttribute: string;
}) => {
+ if (isArrowDown(event) && event.shiftKey) {
+ const firstRowRendererDraggable = containerElement?.querySelector(
+ `.${getRowRendererClassName(focusedAriaRowindex)} .${DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME}`
+ );
+
+ if (firstRowRendererDraggable) {
+ firstRowRendererDraggable.focus();
+ return;
+ }
+ }
+
+ if (isArrowRight(event) && event.shiftKey) {
+ const firstNoteContent = containerElement?.querySelector(
+ `.${getNotesContainerClassName(focusedAriaRowindex)} .${NOTE_CONTENT_CLASS_NAME}`
+ );
+
+ if (firstNoteContent) {
+ firstNoteContent.focus();
+ return;
+ }
+ }
+
const ariaColindex = isArrowRightOrArrowLeft(event)
? getNewAriaColindex({
focusedAriaColindex,
diff --git a/x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.test.tsx
new file mode 100644
index 0000000000000..773fc3eeff483
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.test.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { mount } from 'enzyme';
+import React from 'react';
+
+import { TooltipWithKeyboardShortcut } from '.';
+
+const props = {
+ content: {'To pay respect'}
,
+ shortcut: 'F',
+ showShortcut: true,
+};
+
+describe('TooltipWithKeyboardShortcut', () => {
+ test('it renders the provided content', () => {
+ const wrapper = mount( );
+
+ expect(wrapper.find('[data-test-subj="content"]').text()).toBe('To pay respect');
+ });
+
+ test('it renders the additionalScreenReaderOnlyContext', () => {
+ const wrapper = mount(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="additionalScreenReaderOnlyContext"]').text()).toBe(
+ 'field.name'
+ );
+ });
+
+ test('it renders the expected shortcut', () => {
+ const wrapper = mount( );
+
+ expect(wrapper.find('[data-test-subj="shortcut"]').first().text()).toBe('Press\u00a0F');
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.tsx b/x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.tsx
index 807953c51a42c..ab6f90c8fec81 100644
--- a/x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/accessibility/tooltip_with_keyboard_shortcut/index.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiScreenReaderOnly, EuiText } from '@elastic/eui';
+import { EuiText, EuiScreenReaderOnly } from '@elastic/eui';
import React from 'react';
import * as i18n from './translations';
@@ -23,14 +23,14 @@ const TooltipWithKeyboardShortcutComponent = ({
showShortcut,
}: Props) => (
<>
- {content}
+ {content}
{additionalScreenReaderOnlyContext !== '' && (
-
+
{additionalScreenReaderOnlyContext}
)}
{showShortcut && (
-
+
{i18n.PRESS}
{'\u00a0'}
{shortcut}
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx
index 1cf03225cec03..9ce5778fb72e5 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx
@@ -14,7 +14,6 @@ import '../../mock/match_media';
import { useKibana } from '../../lib/kibana';
import { TestProviders } from '../../mock';
import { FilterManager } from '../../../../../../../src/plugins/data/public';
-import { useAddToTimeline } from '../../hooks/use_add_to_timeline';
import { useSourcererScope } from '../../containers/sourcerer';
import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content';
import {
@@ -41,8 +40,14 @@ jest.mock('uuid', () => {
v4: jest.fn(() => 'uuid.v4()'),
};
});
-
-jest.mock('../../hooks/use_add_to_timeline');
+const mockStartDragToTimeline = jest.fn();
+jest.mock('../../hooks/use_add_to_timeline', () => {
+ const original = jest.requireActual('../../hooks/use_add_to_timeline');
+ return {
+ ...original,
+ useAddToTimeline: () => ({ startDragToTimeline: mockStartDragToTimeline }),
+ };
+});
const mockAddFilters = jest.fn();
const mockGetTimelineFilterManager = jest.fn().mockReturnValue({
addFilters: mockAddFilters,
@@ -78,8 +83,7 @@ const defaultProps = {
describe('DraggableWrapperHoverContent', () => {
beforeAll(() => {
- // our mock implementation of the useAddToTimeline hook returns a mock startDragToTimeline function:
- (useAddToTimeline as jest.Mock).mockReturnValue({ startDragToTimeline: jest.fn() });
+ mockStartDragToTimeline.mockReset();
(useSourcererScope as jest.Mock).mockReturnValue({
browserFields: mockBrowserFields,
selectedPatterns: [],
@@ -376,7 +380,7 @@ describe('DraggableWrapperHoverContent', () => {
});
});
- test('when clicked, it invokes the `startDragToTimeline` function returned by the `useAddToTimeline` hook', () => {
+ test('when clicked, it invokes the `startDragToTimeline` function returned by the `useAddToTimeline` hook', async () => {
const wrapper = mount(
{
);
- // The following "startDragToTimeline" function returned by our mock
- // useAddToTimeline hook is called when the user clicks the
- // Add to timeline investigation action:
- const { startDragToTimeline } = useAddToTimeline({
- draggableId,
- fieldName: aggregatableStringField,
- });
-
wrapper.find('[data-test-subj="add-to-timeline"]').first().simulate('click');
- wrapper.update();
- waitFor(() => {
- expect(startDragToTimeline).toHaveBeenCalled();
+ await waitFor(() => {
+ wrapper.update();
+ expect(mockStartDragToTimeline).toHaveBeenCalled();
});
});
});
describe('Top N', () => {
- test(`it renders the 'Show top field' button when showTopN is false and an aggregatable string field is provided`, async () => {
+ test(`it renders the 'Show top field' button when showTopN is false and an aggregatable string field is provided`, () => {
const aggregatableStringField = 'cloud.account.id';
const wrapper = mount(
@@ -425,7 +421,7 @@ describe('DraggableWrapperHoverContent', () => {
expect(wrapper.find('[data-test-subj="show-top-field"]').first().exists()).toBe(true);
});
- test(`it renders the 'Show top field' button when showTopN is false and a allowlisted signal field is provided`, async () => {
+ test(`it renders the 'Show top field' button when showTopN is false and a allowlisted signal field is provided`, () => {
const allowlistedField = 'signal.rule.name';
const wrapper = mount(
@@ -443,7 +439,7 @@ describe('DraggableWrapperHoverContent', () => {
expect(wrapper.find('[data-test-subj="show-top-field"]').first().exists()).toBe(true);
});
- test(`it does NOT render the 'Show top field' button when showTopN is false and a field not known to BrowserFields is provided`, async () => {
+ test(`it does NOT render the 'Show top field' button when showTopN is false and a field not known to BrowserFields is provided`, () => {
const notKnownToBrowserFields = 'unknown.field';
const wrapper = mount(