From 91a23b7df05ca532bc482d1cf3835917130e3f1b Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Fri, 15 Jan 2021 12:27:38 +0100 Subject: [PATCH 01/38] [Security Solution] Fixes and unskips cypress tests (#87972) * waits for the timeline modal before doing validations * unskips search bar test * unskips URL compatibility tests * unksips attach timeline to a case tests * unksips sourcerer test * unskips pagination * unskips inspect tests * unskips fields browsers tests * unskips toogle column in timeline tests * unskips persistent timeline test * removes comment * fixes drag and drop test * fixes adds a field to the timeline when the user drags and drops a field test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../cypress/integration/fields_browser.spec.ts | 4 ++-- .../security_solution/cypress/integration/inspect.spec.ts | 2 +- .../cypress/integration/pagination.spec.ts | 2 +- .../cypress/integration/search_bar.spec.ts | 2 +- .../security_solution/cypress/integration/sourcerer.spec.ts | 2 +- .../cypress/integration/timeline_attach_to_case.spec.ts | 3 +-- .../cypress/integration/timeline_creation.spec.ts | 2 ++ .../cypress/integration/timeline_local_storage.spec.ts | 3 +-- .../cypress/integration/timeline_toggle_column.spec.ts | 4 ++-- .../cypress/integration/url_compatibility.spec.ts | 2 +- .../security_solution/cypress/screens/fields_browser.ts | 2 +- .../plugins/security_solution/cypress/screens/timeline.ts | 2 ++ .../security_solution/cypress/tasks/fields_browser.ts | 4 +++- x-pack/plugins/security_solution/cypress/tasks/timeline.ts | 6 +++--- 14 files changed, 22 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts index e65cbf85e6e73..00ce40b10fd7c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts @@ -47,7 +47,7 @@ const defaultHeaders = [ ]; describe('Fields Browser', () => { - context.skip('Fields Browser rendering', () => { + context('Fields Browser rendering', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); @@ -154,7 +154,7 @@ describe('Fields Browser', () => { cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('exist'); }); - it.skip('adds a field to the timeline when the user drags and drops a field', () => { + it('adds a field to the timeline when the user drags and drops a field', () => { const filterInput = 'host.geo.c'; filterFieldsBrowser(filterInput); diff --git a/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts b/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts index 98891e65771ce..6321be1e26151 100644 --- a/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts @@ -18,7 +18,7 @@ import { executeTimelineKQL, openTimelineInspectButton } from '../tasks/timeline import { HOSTS_URL, NETWORK_URL } from '../urls/navigation'; -describe.skip('Inspect', () => { +describe('Inspect', () => { context('Hosts stats and tables', () => { before(() => { cleanKibana(); diff --git a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts index 95cbf8220402f..2896b2dbc36c6 100644 --- a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts @@ -17,7 +17,7 @@ import { refreshPage } from '../tasks/security_header'; import { HOSTS_PAGE_TAB_URLS } from '../urls/navigation'; -describe.skip('Pagination', () => { +describe('Pagination', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_PAGE_TAB_URLS.uncommonProcesses); diff --git a/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts b/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts index e5e74f6eb0cac..7fcbc10f88b44 100644 --- a/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts @@ -13,7 +13,7 @@ import { HOSTS_URL } from '../urls/navigation'; import { waitForAllHostsToBeLoaded } from '../tasks/hosts/all_hosts'; import { cleanKibana } from '../tasks/common'; -describe.skip('SearchBar', () => { +describe('SearchBar', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); diff --git a/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts index 96007ca0326d1..91695e3f53fbb 100644 --- a/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts @@ -28,7 +28,7 @@ import { populateTimeline } from '../tasks/timeline'; import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; import { cleanKibana } from '../tasks/common'; -describe.skip('Sourcerer', () => { +describe('Sourcerer', () => { before(() => { cleanKibana(); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts index 56b2ef00169dc..745fa9085698f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts @@ -18,8 +18,7 @@ import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { createCase } from '../tasks/api_calls/cases'; -// https://github.com/elastic/kibana/issues/86959 -describe.skip('attach timeline to case', () => { +describe('attach timeline to case', () => { context('without cases created', () => { beforeEach(() => { cleanKibana(); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts index cacf2802b6d71..0025516c75714 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts @@ -19,6 +19,7 @@ import { // TIMELINE_FILTER, TIMELINE_QUERY, TIMELINE_TITLE, + OPEN_TIMELINE_MODAL, } from '../screens/timeline'; import { TIMELINES_DESCRIPTION, @@ -79,6 +80,7 @@ describe('Timelines', () => { closeTimeline(); openTimelineFromSettings(); + cy.get(OPEN_TIMELINE_MODAL).should('be.visible'); cy.contains(timeline.title).should('exist'); cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description); cy.get(TIMELINES_PINNED_EVENT_COUNT).first().should('have.text', '1'); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts index f5091dd893446..155b0b6660998 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts @@ -13,8 +13,7 @@ import { TABLE_COLUMN_EVENTS_MESSAGE } from '../screens/hosts/external_events'; import { waitsForEventsToBeLoaded } from '../tasks/hosts/events'; import { removeColumn } from '../tasks/timeline'; -// Failing: See https://github.com/elastic/kibana/issues/75794 -describe.skip('persistent timeline', () => { +describe('persistent timeline', () => { beforeEach(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts index 705aff7b14c6c..b00739cbf17c2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts @@ -26,7 +26,7 @@ import { import { HOSTS_URL } from '../urls/navigation'; -describe.skip('toggle column in timeline', () => { +describe('toggle column in timeline', () => { before(() => { cleanKibana(); cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export'); @@ -67,7 +67,7 @@ describe.skip('toggle column in timeline', () => { cy.get(ID_HEADER_FIELD).should('exist'); }); - it.skip('adds the _id field to the timeline via drag and drop', () => { + it('adds the _id field to the timeline via drag and drop', () => { expandFirstTimelineEventDetails(); dragAndDropIdToggleFieldToTimeline(); diff --git a/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts index cf433891ac929..58ef4cd2d96ba 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts @@ -19,7 +19,7 @@ const ABSOLUTE_DATE = { startTime: '2019-08-01T20:03:29.186Z', }; -describe.skip('URL compatibility', () => { +describe('URL compatibility', () => { before(() => { cleanKibana(); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts b/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts index 5489a3a83cb79..845c54cd4a4cc 100644 --- a/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts +++ b/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts @@ -18,7 +18,7 @@ export const FIELDS_BROWSER_FIELDS_COUNT = `${FIELDS_BROWSER_CONTAINER} [data-te export const FIELDS_BROWSER_FILTER_INPUT = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="field-search"]`; -export const FIELDS_BROWSER_HEADER_DROP_AREA = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="headers-group"]`; +export const FIELDS_BROWSER_HEADER_DROP_AREA = '[data-test-subj="headers-group"]'; export const FIELDS_BROWSER_HOST_CATEGORIES_COUNT = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="host-category-count"]`; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 159d768a26140..689a65470030f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -70,6 +70,8 @@ export const NOTES_COUNT = '[data-test-subj="timeline-notes-count"]'; export const OPEN_TIMELINE_ICON = '[data-test-subj="open-timeline-button"]'; +export const OPEN_TIMELINE_MODAL = '[data-test-subj="open-timeline-modal"]'; + export const OPEN_TIMELINE_TEMPLATE_ICON = '[data-test-subj="open-timeline-modal-body-filter-template"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts b/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts index eb709d2dd5778..823a370ff33ce 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts @@ -32,7 +32,9 @@ export const addsHostGeoCountryNameToTimelineDraggingIt = () => { cy.get(FIELDS_BROWSER_DRAGGABLE_HOST_GEO_COUNTRY_NAME_HEADER).should('exist'); cy.get(FIELDS_BROWSER_DRAGGABLE_HOST_GEO_COUNTRY_NAME_HEADER).then((field) => drag(field)); - cy.get(FIELDS_BROWSER_HEADER_DROP_AREA).then((headersDropArea) => drop(headersDropArea)); + cy.get(FIELDS_BROWSER_HEADER_DROP_AREA) + .first() + .then((headersDropArea) => drop(headersDropArea)); }; export const clearFieldsBrowser = () => { diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 47c1fd237432c..ee876abd8e8d3 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -183,9 +183,9 @@ export const dragAndDropIdToggleFieldToTimeline = () => { cy.get(ID_FIELD).then((field) => drag(field)); - cy.get(`[data-test-subj="timeline"] [data-test-subj="headers-group"]`).then((headersDropArea) => - drop(headersDropArea) - ); + cy.get(`[data-test-subj="timeline"] [data-test-subj="headers-group"]`) + .first() + .then((headersDropArea) => drop(headersDropArea)); }; export const removeColumn = (column: number) => { From abfd8bb9d3d2db7728c6eec7fa2dcbddc528d708 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Fri, 15 Jan 2021 13:31:51 +0100 Subject: [PATCH 02/38] Use documentation links service provided by the core directly. (#88158) --- ...-plugin-core-public.doclinksstart.links.md | 30 ++- ...kibana-plugin-core-public.doclinksstart.md | 2 +- .../public/doc_links/doc_links_service.ts | 32 ++- src/core/public/public.api.md | 30 ++- .../api_keys_grid_page.test.tsx.snap | 197 +++++++++--------- .../api_keys_grid/api_keys_grid_page.test.tsx | 36 ++-- .../api_keys_grid/api_keys_grid_page.tsx | 13 +- .../empty_prompt/empty_prompt.tsx | 116 +++++------ .../api_keys_grid/not_enabled/not_enabled.tsx | 59 +++--- .../api_keys/api_keys_management_app.test.tsx | 2 +- .../api_keys/api_keys_management_app.tsx | 24 +-- .../api_keys/documentation_links.ts | 25 --- .../no_compatible_realms.tsx | 59 +++--- .../role_mappings/documentation_links.ts | 37 ---- .../edit_role_mapping_page.test.tsx | 133 +++--------- .../edit_role_mapping_page.tsx | 9 +- .../mapping_info_panel.test.tsx | 13 +- .../mapping_info_panel/mapping_info_panel.tsx | 6 +- .../json_rule_editor.test.tsx | 50 ++--- .../rule_editor_panel/json_rule_editor.tsx | 6 +- .../rule_editor_panel.test.tsx | 24 ++- .../rule_editor_panel/rule_editor_panel.tsx | 7 +- .../role_mappings_grid_page.test.tsx | 104 +++------ .../role_mappings_grid_page.tsx | 14 +- .../role_mappings_management_app.test.tsx | 20 +- .../role_mappings_management_app.tsx | 45 ++-- .../management/roles/documentation_links.ts | 31 --- .../roles/edit_role/edit_role_page.test.tsx | 3 +- .../roles/edit_role/edit_role_page.tsx | 5 +- .../es/elasticsearch_privileges.test.tsx | 3 +- .../es/elasticsearch_privileges.tsx | 10 +- .../roles/roles_management_app.test.tsx | 10 +- .../management/roles/roles_management_app.tsx | 3 +- .../components/insecure_cluster_alert.tsx | 10 +- .../security_checkup/documentation_links.ts | 19 -- .../security_checkup_service.tsx | 9 +- 36 files changed, 504 insertions(+), 692 deletions(-) delete mode 100644 x-pack/plugins/security/public/management/api_keys/documentation_links.ts delete mode 100644 x-pack/plugins/security/public/management/role_mappings/documentation_links.ts delete mode 100644 x-pack/plugins/security/public/management/roles/documentation_links.ts delete mode 100644 x-pack/plugins/security/public/security_checkup/documentation_links.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 0e23064385a63..ff2a8a2b5f75f 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -108,12 +108,38 @@ readonly links: { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; - readonly apis: Record; + readonly apis: Readonly<{ + createIndex: string; + createSnapshotLifecyclePolicy: string; + createRoleMapping: string; + createRoleMappingTemplates: string; + createApiKey: string; + createPipeline: string; + createTransformRequest: string; + executeWatchActionModes: string; + openIndex: string; + putComponentTemplate: string; + painlessExecute: string; + putComponentTemplateMetadata: string; + putWatch: string; + updateTransform: string; + }>; readonly observability: Record; readonly alerting: Record; readonly maps: Record; readonly monitoring: Record; - readonly security: Record; + readonly security: Readonly<{ + apiKeyServiceSettings: string; + clusterPrivileges: string; + elasticsearchSettings: string; + elasticsearchEnableSecurity: string; + indicesPrivileges: string; + kibanaTLS: string; + kibanaPrivileges: string; + mappingRoles: string; + mappingRolesFieldRules: string; + runAsPrivilege: string; + }>; readonly watcher: Record; readonly ccs: Record; }; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 3ad747a42f84e..8404326f773e6 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: string;
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Record<string, string>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Record<string, string>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: string;
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
executeWatchActionModes: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
putComponentTemplateMetadata: string;
putWatch: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
} | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 43960ce7db467..6c0ced7022cb2 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -220,7 +220,7 @@ export class DocLinksService { }, apis: { createIndex: `${ELASTICSEARCH_DOCS}indices-create-index.html`, - createSnapshotLifecylePolicy: `${ELASTICSEARCH_DOCS}slm-api-put-policy.html`, + createSnapshotLifecyclePolicy: `${ELASTICSEARCH_DOCS}slm-api-put-policy.html`, createRoleMapping: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html`, createRoleMappingTemplates: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html#_role_templates`, createApiKey: `${ELASTICSEARCH_DOCS}security-api-create-api-key.html`, @@ -344,12 +344,38 @@ export interface DocLinksStart { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; - readonly apis: Record; + readonly apis: Readonly<{ + createIndex: string; + createSnapshotLifecyclePolicy: string; + createRoleMapping: string; + createRoleMappingTemplates: string; + createApiKey: string; + createPipeline: string; + createTransformRequest: string; + executeWatchActionModes: string; + openIndex: string; + putComponentTemplate: string; + painlessExecute: string; + putComponentTemplateMetadata: string; + putWatch: string; + updateTransform: string; + }>; readonly observability: Record; readonly alerting: Record; readonly maps: Record; readonly monitoring: Record; - readonly security: Record; + readonly security: Readonly<{ + apiKeyServiceSettings: string; + clusterPrivileges: string; + elasticsearchSettings: string; + elasticsearchEnableSecurity: string; + indicesPrivileges: string; + kibanaTLS: string; + kibanaPrivileges: string; + mappingRoles: string; + mappingRolesFieldRules: string; + runAsPrivilege: string; + }>; readonly watcher: Record; readonly ccs: Record; }; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index c5b49519ef7b2..2f4c871c33431 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -587,12 +587,38 @@ export interface DocLinksStart { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; - readonly apis: Record; + readonly apis: Readonly<{ + createIndex: string; + createSnapshotLifecyclePolicy: string; + createRoleMapping: string; + createRoleMappingTemplates: string; + createApiKey: string; + createPipeline: string; + createTransformRequest: string; + executeWatchActionModes: string; + openIndex: string; + putComponentTemplate: string; + painlessExecute: string; + putComponentTemplateMetadata: string; + putWatch: string; + updateTransform: string; + }>; readonly observability: Record; readonly alerting: Record; readonly maps: Record; readonly monitoring: Record; - readonly security: Record; + readonly security: Readonly<{ + apiKeyServiceSettings: string; + clusterPrivileges: string; + elasticsearchSettings: string; + elasticsearchEnableSecurity: string; + indicesPrivileges: string; + kibanaTLS: string; + kibanaPrivileges: string; + mappingRoles: string; + mappingRolesFieldRules: string; + runAsPrivilege: string; + }>; readonly watcher: Record; readonly ccs: Record; }; diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap index 3c6458a6d2467..44b6c081bcfba 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap @@ -1,132 +1,123 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`APIKeysGridPage renders a callout when API keys are not enabled 1`] = ` - } > - - } +
- - + + -
- - - , - } - } - > - Contact your system administrator and refer to the - - + +
+ + -
-
- - + (opens in a new tab or window) + + + + + + to enable API keys. + +
+ + +
`; exports[`APIKeysGridPage renders permission denied if user does not have required permissions 1`] = ` diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx index 68daf427677ff..a55d872ca60f9 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx @@ -10,10 +10,10 @@ import { ReactWrapper } from 'enzyme'; import { EuiCallOut } from '@elastic/eui'; import type { PublicMethodsOf } from '@kbn/utility-types'; +import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; import { NotEnabled } from './not_enabled'; import { PermissionDenied } from './permission_denied'; import { APIKeysAPIClient } from '../api_keys_api_client'; -import { DocumentationLinksService } from '../documentation_links'; import { APIKeysGridPage } from './api_keys_grid_page'; import { coreMock } from '../../../../../../../src/core/public/mocks'; @@ -66,21 +66,16 @@ describe('APIKeysGridPage', () => { }); const coreStart = coreMock.createStart(); - - const getViewProperties = () => { - const { docLinks, notifications, application } = coreStart; - return { - docLinks: new DocumentationLinksService(docLinks), - navigateToApp: application.navigateToApp, - notifications, - apiKeysAPIClient: apiClientMock, - }; + const renderView = () => { + return mountWithIntl( + + + + ); }; it('renders a loading state when fetching API keys', async () => { - const wrapper = mountWithIntl(); - - expect(wrapper.find('[data-test-subj="apiKeysSectionLoading"]')).toHaveLength(1); + expect(renderView().find('[data-test-subj="apiKeysSectionLoading"]')).toHaveLength(1); }); it('renders a callout when API keys are not enabled', async () => { @@ -90,13 +85,12 @@ describe('APIKeysGridPage', () => { areApiKeysEnabled: false, }); - const wrapper = mountWithIntl(); - + const wrapper = renderView(); await waitForRender(wrapper, (updatedWrapper) => { return updatedWrapper.find(NotEnabled).length > 0; }); - expect(wrapper.find(NotEnabled)).toMatchSnapshot(); + expect(wrapper.find(NotEnabled).find(EuiCallOut)).toMatchSnapshot(); }); it('renders permission denied if user does not have required permissions', async () => { @@ -106,8 +100,7 @@ describe('APIKeysGridPage', () => { areApiKeysEnabled: true, }); - const wrapper = mountWithIntl(); - + const wrapper = renderView(); await waitForRender(wrapper, (updatedWrapper) => { return updatedWrapper.find(PermissionDenied).length > 0; }); @@ -118,8 +111,7 @@ describe('APIKeysGridPage', () => { it('renders error callout if error fetching API keys', async () => { apiClientMock.getApiKeys.mockRejectedValue(mock500()); - const wrapper = mountWithIntl(); - + const wrapper = renderView(); await waitForRender(wrapper, (updatedWrapper) => { return updatedWrapper.find(EuiCallOut).length > 0; }); @@ -130,7 +122,7 @@ describe('APIKeysGridPage', () => { describe('Admin view', () => { let wrapper: ReactWrapper; beforeEach(() => { - wrapper = mountWithIntl(); + wrapper = renderView(); }); it('renders a callout indicating the user is an administrator', async () => { @@ -165,7 +157,7 @@ describe('APIKeysGridPage', () => { areApiKeysEnabled: true, }); - wrapper = mountWithIntl(); + wrapper = renderView(); }); it('does NOT render a callout indicating the user is an administrator', async () => { diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx index b4ea91ea024f9..c208502fc6a37 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx @@ -28,11 +28,10 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { PublicMethodsOf } from '@kbn/utility-types'; import moment from 'moment-timezone'; -import { ApplicationStart, NotificationsStart } from 'src/core/public'; +import type { NotificationsStart } from 'src/core/public'; import { SectionLoading } from '../../../../../../../src/plugins/es_ui_shared/public'; import { ApiKey, ApiKeyToInvalidate } from '../../../../common/model'; import { APIKeysAPIClient } from '../api_keys_api_client'; -import { DocumentationLinksService } from '../documentation_links'; import { PermissionDenied } from './permission_denied'; import { EmptyPrompt } from './empty_prompt'; import { NotEnabled } from './not_enabled'; @@ -40,9 +39,7 @@ import { InvalidateProvider } from './invalidate_provider'; interface Props { notifications: NotificationsStart; - docLinks: DocumentationLinksService; apiKeysAPIClient: PublicMethodsOf; - navigateToApp: ApplicationStart['navigateToApp']; } interface State { @@ -132,7 +129,7 @@ export class APIKeysGridPage extends Component { if (!areApiKeysEnabled) { return ( - + ); } @@ -140,11 +137,7 @@ export class APIKeysGridPage extends Component { if (!isLoadingTable && apiKeys && apiKeys.length === 0) { return ( - + ); } diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx index 9b2ccfcb99ef3..39a34efb9f934 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx @@ -5,72 +5,70 @@ */ import React, { Fragment } from 'react'; -import { ApplicationStart } from 'kibana/public'; import { EuiEmptyPrompt, EuiButton, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { DocumentationLinksService } from '../../documentation_links'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; interface Props { isAdmin: boolean; - docLinks: DocumentationLinksService; - navigateToApp: ApplicationStart['navigateToApp']; } -export const EmptyPrompt: React.FunctionComponent = ({ - isAdmin, - docLinks, - navigateToApp, -}) => ( - - {isAdmin ? ( +export const EmptyPrompt: React.FunctionComponent = ({ isAdmin }) => { + const { services } = useKibana(); + const application = services.application!; + const docLinks = services.docLinks!; + return ( + + {isAdmin ? ( + + ) : ( + + )} + + } + body={ + +

+ + + + ), + }} + /> +

+
+ } + actions={ + application.navigateToApp('dev_tools')} + data-test-subj="goToConsoleButton" + > - ) : ( - - )} - - } - body={ - -

- - - - ), - }} - /> -

-
- } - actions={ - navigateToApp('dev_tools')} - data-test-subj="goToConsoleButton" - > - - - } - data-test-subj="emptyPrompt" - /> -); +
+ } + data-test-subj="emptyPrompt" + /> + ); +}; diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx index 08fe542557757..54ea724cfef1d 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx @@ -7,36 +7,35 @@ import React from 'react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { DocumentationLinksService } from '../../documentation_links'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -interface Props { - docLinks: DocumentationLinksService; -} - -export const NotEnabled: React.FunctionComponent = ({ docLinks }) => ( - { + const docLinks = useKibana().services.docLinks!; + return ( + + } + color="danger" + iconType="alert" + > + + + ), + }} /> - } - color="danger" - iconType="alert" - > - - - - ), - }} - /> - -); + + ); +}; diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx index 2b43bc7ebc20d..e9901b8f7847c 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx @@ -43,7 +43,7 @@ describe('apiKeysManagementApp', () => { expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: '/', text: 'API Keys' }]); expect(container).toMatchInlineSnapshot(`
- Page: {"notifications":{"toasts":{}},"docLinks":{"apiKeySettings":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings","createApiKey":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-create-api-key.html"},"apiKeysAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}}} + Page: {"notifications":{"toasts":{}},"apiKeysAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}}}
`); diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx index 6ff91852d0a3e..241bb43828814 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx @@ -9,8 +9,8 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { i18n } from '@kbn/i18n'; import { StartServicesAccessor } from 'src/core/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; +import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { PluginStartDependencies } from '../../plugin'; -import { DocumentationLinksService } from './documentation_links'; interface CreateParams { getStartServices: StartServicesAccessor; @@ -35,25 +35,21 @@ export const apiKeysManagementApp = Object.freeze({ }, ]); - const [ - [{ docLinks, http, notifications, i18n: i18nStart, application }], - { APIKeysGridPage }, - { APIKeysAPIClient }, - ] = await Promise.all([ + const [[core], { APIKeysGridPage }, { APIKeysAPIClient }] = await Promise.all([ getStartServices(), import('./api_keys_grid'), import('./api_keys_api_client'), ]); render( - - - , + + + + + , element ); diff --git a/x-pack/plugins/security/public/management/api_keys/documentation_links.ts b/x-pack/plugins/security/public/management/api_keys/documentation_links.ts deleted file mode 100644 index 66a54f1cb7b72..0000000000000 --- a/x-pack/plugins/security/public/management/api_keys/documentation_links.ts +++ /dev/null @@ -1,25 +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 { DocLinksStart } from 'src/core/public'; - -export class DocumentationLinksService { - private readonly apiKeySettings: string; - private readonly createApiKey: string; - - constructor(docLinks: DocLinksStart) { - this.apiKeySettings = `${docLinks.links.security.apiKeyServiceSettings}`; - this.createApiKey = `${docLinks.links.apis.createApiKey}`; - } - - public getApiKeyServiceSettingsDocUrl() { - return `${this.apiKeySettings}`; - } - - public getCreateApiKeyDocUrl() { - return `${this.createApiKey}`; - } -} diff --git a/x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx b/x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx index 5e14b0c179bfd..a3a7377b0d395 100644 --- a/x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx @@ -7,36 +7,35 @@ import React from 'react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { DocumentationLinksService } from '../../documentation_links'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -interface Props { - docLinks: DocumentationLinksService; -} - -export const NoCompatibleRealms: React.FunctionComponent = ({ docLinks }: Props) => ( - { + const docLinks = useKibana().services.docLinks!; + return ( + + } + color="warning" + iconType="alert" + > + + + ), + }} /> - } - color="warning" - iconType="alert" - > - - - - ), - }} - /> - -); + + ); +}; diff --git a/x-pack/plugins/security/public/management/role_mappings/documentation_links.ts b/x-pack/plugins/security/public/management/role_mappings/documentation_links.ts deleted file mode 100644 index 2098d5c71ee7a..0000000000000 --- a/x-pack/plugins/security/public/management/role_mappings/documentation_links.ts +++ /dev/null @@ -1,37 +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 { DocLinksStart } from 'src/core/public'; - -export class DocumentationLinksService { - private readonly mappingRoles: string; - private readonly createRoleMapping: string; - private readonly createRoleMappingTemplates: string; - private readonly roleMappingFieldRules: string; - - constructor(docLinks: DocLinksStart) { - this.mappingRoles = `${docLinks.links.security.mappingRoles}`; - this.createRoleMapping = `${docLinks.links.apis.createRoleMapping}`; - this.createRoleMappingTemplates = `${docLinks.links.apis.createRoleMappingTemplates}`; - this.roleMappingFieldRules = `${docLinks.links.security.mappingRolesFieldRules}`; - } - - public getRoleMappingDocUrl() { - return `${this.mappingRoles}`; - } - - public getRoleMappingAPIDocUrl() { - return `${this.createRoleMapping}`; - } - - public getRoleMappingTemplateDocUrl() { - return `${this.createRoleMappingTemplates}`; - } - - public getRoleMappingFieldRulesDocUrl() { - return `${this.roleMappingFieldRules}`; - } -} diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx index c7c40cea63e2e..64ef1459a5246 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx @@ -20,7 +20,7 @@ import { VisualRuleEditor } from './rule_editor_panel/visual_rule_editor'; import { JSONRuleEditor } from './rule_editor_panel/json_rule_editor'; import { RolesAPIClient } from '../../roles'; import { Role } from '../../../../common/model'; -import { DocumentationLinksService } from '../documentation_links'; +import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks'; import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock'; @@ -31,6 +31,25 @@ describe('EditRoleMappingPage', () => { const history = scopedHistoryMock.create(); let rolesAPI: PublicMethodsOf; + const renderView = ( + roleMappingsAPI: ReturnType, + name?: string + ) => { + const coreStart = coreMock.createStart(); + return mountWithIntl( + + + + ); + }; + beforeEach(() => { rolesAPI = rolesAPIClientMock.create(); (rolesAPI as jest.Mocked).getRoles.mockResolvedValue([ @@ -50,17 +69,7 @@ describe('EditRoleMappingPage', () => { canUseStoredScripts: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); - + const wrapper = renderView(roleMappingsAPI); await nextTick(); wrapper.update(); @@ -112,18 +121,7 @@ describe('EditRoleMappingPage', () => { canUseStoredScripts: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); - + const wrapper = renderView(roleMappingsAPI, 'foo'); await nextTick(); wrapper.update(); @@ -161,16 +159,7 @@ describe('EditRoleMappingPage', () => { hasCompatibleRealms: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(PermissionDenied)).toHaveLength(0); @@ -189,16 +178,7 @@ describe('EditRoleMappingPage', () => { hasCompatibleRealms: false, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0); @@ -226,18 +206,7 @@ describe('EditRoleMappingPage', () => { canUseStoredScripts: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); - + const wrapper = renderView(roleMappingsAPI, 'foo'); expect(findTestSubject(wrapper, 'deprecatedRolesAssigned')).toHaveLength(0); await nextTick(); @@ -267,18 +236,7 @@ describe('EditRoleMappingPage', () => { canUseStoredScripts: false, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); - + const wrapper = renderView(roleMappingsAPI, 'foo'); expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0); @@ -310,18 +268,7 @@ describe('EditRoleMappingPage', () => { canUseStoredScripts: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); - + const wrapper = renderView(roleMappingsAPI, 'foo'); expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0); @@ -365,18 +312,7 @@ describe('EditRoleMappingPage', () => { canUseStoredScripts: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); - + const wrapper = renderView(roleMappingsAPI, 'foo'); await nextTick(); wrapper.update(); @@ -421,18 +357,7 @@ describe('EditRoleMappingPage', () => { canUseStoredScripts: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); - + const wrapper = renderView(roleMappingsAPI, 'foo'); await nextTick(); wrapper.update(); diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx index 30584348960a5..3de11e84e042b 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx @@ -20,7 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import { NotificationsStart, ScopedHistory } from 'src/core/public'; +import type { NotificationsStart, ScopedHistory, DocLinksStart } from 'src/core/public'; import { RoleMapping } from '../../../../common/model'; import { RuleEditorPanel } from './rule_editor_panel'; import { @@ -32,7 +32,6 @@ import { import { RolesAPIClient } from '../../roles'; import { validateRoleMappingForSave } from './services/role_mapping_validation'; import { MappingInfoPanel } from './mapping_info_panel'; -import { DocumentationLinksService } from '../documentation_links'; import { RoleMappingsAPIClient } from '../role_mappings_api_client'; interface State { @@ -54,7 +53,7 @@ interface Props { roleMappingsAPI: PublicMethodsOf; rolesAPIClient: PublicMethodsOf; notifications: NotificationsStart; - docLinks: DocumentationLinksService; + docLinks: DocLinksStart; history: ScopedHistory; } @@ -163,7 +162,7 @@ export class EditRoleMappingPage extends Component { values={{ learnMoreLink: ( @@ -180,7 +179,7 @@ export class EditRoleMappingPage extends Component { {!this.state.hasCompatibleRealms && ( <> - + )} diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx index dbd034ff3f764..f9201a8dbf249 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx @@ -10,7 +10,6 @@ import { mountWithIntl } from '@kbn/test/jest'; import { findTestSubject } from '@kbn/test/jest'; import { Role, RoleMapping } from '../../../../../common/model'; import { RolesAPIClient } from '../../../roles'; -import { DocumentationLinksService } from '../../documentation_links'; import { RoleSelector } from '../role_selector'; import { RoleTemplateEditor } from '../role_selector/role_template_editor'; import { MappingInfoPanel } from '.'; @@ -39,7 +38,7 @@ describe('MappingInfoPanel', () => { metadata: {}, } as RoleMapping, mode: 'create', - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), + docLinks: coreMock.createStart().docLinks, rolesAPIClient: rolesAPI, } as MappingInfoPanel['props']; @@ -86,7 +85,7 @@ describe('MappingInfoPanel', () => { metadata: {}, } as RoleMapping, mode: 'edit', - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), + docLinks: coreMock.createStart().docLinks, rolesAPIClient: rolesAPI, } as MappingInfoPanel['props']; @@ -112,7 +111,7 @@ describe('MappingInfoPanel', () => { canUseInlineScripts: true, canUseStoredScripts: false, validateForm: false, - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), + docLinks: coreMock.createStart().docLinks, rolesAPIClient: rolesAPI, }; @@ -153,7 +152,7 @@ describe('MappingInfoPanel', () => { canUseInlineScripts: false, canUseStoredScripts: true, validateForm: false, - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), + docLinks: coreMock.createStart().docLinks, rolesAPIClient: rolesAPI, }; @@ -194,7 +193,7 @@ describe('MappingInfoPanel', () => { canUseInlineScripts: false, canUseStoredScripts: false, validateForm: false, - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), + docLinks: coreMock.createStart().docLinks, rolesAPIClient: rolesAPI, }; @@ -219,7 +218,7 @@ describe('MappingInfoPanel', () => { metadata: {}, } as RoleMapping, mode: 'edit', - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), + docLinks: coreMock.createStart().docLinks, rolesAPIClient: rolesAPI, } as MappingInfoPanel['props']; diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx index faf0278e5d8f3..8176950a46339 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { PublicMethodsOf } from '@kbn/utility-types'; +import type { DocLinksStart } from 'src/core/public'; import { RoleMapping } from '../../../../../common/model'; import { RolesAPIClient } from '../../../roles'; import { @@ -28,7 +29,6 @@ import { validateRoleMappingRoleTemplates, } from '../services/role_mapping_validation'; import { RoleSelector } from '../role_selector'; -import { DocumentationLinksService } from '../../documentation_links'; interface Props { roleMapping: RoleMapping; @@ -38,7 +38,7 @@ interface Props { canUseInlineScripts: boolean; canUseStoredScripts: boolean; rolesAPIClient: PublicMethodsOf; - docLinks: DocumentationLinksService; + docLinks: DocLinksStart; } interface State { @@ -205,7 +205,7 @@ export class MappingInfoPanel extends Component { defaultMessage="Create templates that describe the roles to assign to your users." />{' '} diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.test.tsx index bae41b31cdcc1..5c7c6ed5b3ca1 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.test.tsx @@ -17,20 +17,24 @@ import { act } from 'react-dom/test-utils'; import { mountWithIntl } from '@kbn/test/jest'; import { JSONRuleEditor } from './json_rule_editor'; import { EuiCodeEditor } from '@elastic/eui'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { AllRule, AnyRule, FieldRule, ExceptAnyRule, ExceptAllRule } from '../../model'; -import { DocumentationLinksService } from '../../documentation_links'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; describe('JSONRuleEditor', () => { + const renderView = (props: React.ComponentProps) => { + const coreStart = coreMock.createStart(); + return mountWithIntl( + + + + ); + }; + it('renders an empty rule set', () => { - const props = { - rules: null, - onChange: jest.fn(), - onValidityChange: jest.fn(), - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), - }; - const wrapper = mountWithIntl(); + const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() }; + const wrapper = renderView(props); expect(props.onChange).not.toHaveBeenCalled(); expect(props.onValidityChange).not.toHaveBeenCalled(); @@ -50,9 +54,8 @@ describe('JSONRuleEditor', () => { ]), onChange: jest.fn(), onValidityChange: jest.fn(), - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), }; - const wrapper = mountWithIntl(); + const wrapper = renderView(props); const { value } = wrapper.find(EuiCodeEditor).props(); expect(JSON.parse(value as string)).toEqual({ @@ -80,13 +83,8 @@ describe('JSONRuleEditor', () => { }); it('notifies when input contains invalid JSON', () => { - const props = { - rules: null, - onChange: jest.fn(), - onValidityChange: jest.fn(), - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), - }; - const wrapper = mountWithIntl(); + const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() }; + const wrapper = renderView(props); const allRule = JSON.stringify(new AllRule().toRaw()); act(() => { @@ -99,13 +97,8 @@ describe('JSONRuleEditor', () => { }); it('notifies when input contains an invalid rule set, even if it is valid JSON', () => { - const props = { - rules: null, - onChange: jest.fn(), - onValidityChange: jest.fn(), - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), - }; - const wrapper = mountWithIntl(); + const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() }; + const wrapper = renderView(props); const invalidRule = JSON.stringify({ all: [ @@ -127,13 +120,8 @@ describe('JSONRuleEditor', () => { }); it('fires onChange when a valid rule set is provided after being previously invalidated', () => { - const props = { - rules: null, - onChange: jest.fn(), - onValidityChange: jest.fn(), - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), - }; - const wrapper = mountWithIntl(); + const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() }; + const wrapper = renderView(props); const allRule = JSON.stringify(new AllRule().toRaw()); act(() => { diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.tsx index e7a9149513d20..931803ddc6ffe 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.tsx @@ -11,17 +11,17 @@ import 'brace/theme/github'; import { EuiCodeEditor, EuiFormRow, EuiButton, EuiSpacer, EuiLink, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { DocumentationLinksService } from '../../documentation_links'; import { Rule, RuleBuilderError, generateRulesFromRaw } from '../../model'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; interface Props { rules: Rule | null; onChange: (updatedRules: Rule | null) => void; onValidityChange: (isValid: boolean) => void; - docLinks: DocumentationLinksService; } export const JSONRuleEditor = (props: Props) => { + const docLinks = useKibana().services.docLinks!; const [rawRules, setRawRules] = useState( JSON.stringify(props.rules ? props.rules.toRaw() : {}, null, 2) ); @@ -108,7 +108,7 @@ export const JSONRuleEditor = (props: Props) => { values={{ roleMappingAPI: ( diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.test.tsx index ac31900b11a67..5a44a7c05c193 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.test.tsx @@ -17,20 +17,28 @@ import '@kbn/test/target/jest/utils/stub_web_worker'; import { AllRule, FieldRule } from '../../model'; import { EuiErrorBoundary } from '@elastic/eui'; -import { DocumentationLinksService } from '../../documentation_links'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; describe('RuleEditorPanel', () => { + const renderView = (props: Omit, 'docLinks'>) => { + const coreStart = coreMock.createStart(); + const viewProps = { ...props, docLinks: coreStart.docLinks }; + return mountWithIntl( + + + + ); + }; it('renders the visual editor when no rules are defined', () => { const props = { rawRules: {}, onChange: jest.fn(), onValidityChange: jest.fn(), validateForm: false, - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), }; - const wrapper = mountWithIntl(); + const wrapper = renderView(props); expect(wrapper.find(VisualRuleEditor)).toHaveLength(1); expect(wrapper.find(JSONRuleEditor)).toHaveLength(0); }); @@ -49,9 +57,9 @@ describe('RuleEditorPanel', () => { onChange: jest.fn(), onValidityChange: jest.fn(), validateForm: false, - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), + docLinks: coreMock.createStart().docLinks, }; - const wrapper = mountWithIntl(); + const wrapper = renderView(props); expect(wrapper.find(VisualRuleEditor)).toHaveLength(1); expect(wrapper.find(JSONRuleEditor)).toHaveLength(0); @@ -73,9 +81,8 @@ describe('RuleEditorPanel', () => { onChange: jest.fn(), onValidityChange: jest.fn(), validateForm: false, - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), }; - const wrapper = mountWithIntl(); + const wrapper = renderView(props); findTestSubject(wrapper, 'roleMappingsJSONRuleEditorButton').simulate('click'); @@ -109,9 +116,8 @@ describe('RuleEditorPanel', () => { onChange: jest.fn(), onValidityChange: jest.fn(), validateForm: false, - docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), }; - const wrapper = mountWithIntl(); + const wrapper = renderView(props); wrapper.find(VisualRuleEditor).simulateError(new Error('Something awful happened here.')); diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx index 6e6641caa1f39..1b5cda1df5b52 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx @@ -22,12 +22,12 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import type { DocLinksStart } from 'src/core/public'; import { RoleMapping } from '../../../../../common/model'; import { VisualRuleEditor } from './visual_rule_editor'; import { JSONRuleEditor } from './json_rule_editor'; import { VISUAL_MAX_RULE_DEPTH } from '../services/role_mapping_constants'; import { Rule, generateRulesFromRaw } from '../../model'; -import { DocumentationLinksService } from '../../documentation_links'; import { validateRoleMappingRules } from '../services/role_mapping_validation'; interface Props { @@ -35,7 +35,7 @@ interface Props { onChange: (rawRules: RoleMapping['rules']) => void; onValidityChange: (isValid: boolean) => void; validateForm: boolean; - docLinks: DocumentationLinksService; + docLinks: DocLinksStart; } interface State { @@ -92,7 +92,7 @@ export class RuleEditorPanel extends Component { values={{ learnMoreLink: ( @@ -215,7 +215,6 @@ export class RuleEditorPanel extends Component { rules={this.state.rules} onChange={this.onRuleChange} onValidityChange={this.onValidityChange} - docLinks={this.props.docLinks} /> ); default: diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx index a8001c1b775ea..05edd3a370092 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx @@ -13,7 +13,7 @@ import { EmptyPrompt } from './empty_prompt'; import { findTestSubject } from '@kbn/test/jest'; import { EuiLink } from '@elastic/eui'; import { act } from '@testing-library/react'; -import { DocumentationLinksService } from '../documentation_links'; +import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks'; import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock'; @@ -23,6 +23,24 @@ describe('RoleMappingsGridPage', () => { let history: ScopedHistory; let coreStart: CoreStart; + const renderView = ( + roleMappingsAPI: ReturnType, + rolesAPI: ReturnType = rolesAPIClientMock.create() + ) => { + return mountWithIntl( + + + + ); + }; + beforeEach(() => { history = scopedHistoryMock.create(); coreStart = coreMock.createStart(); @@ -36,17 +54,7 @@ describe('RoleMappingsGridPage', () => { hasCompatibleRealms: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(EmptyPrompt)).toHaveLength(0); @@ -65,17 +73,7 @@ describe('RoleMappingsGridPage', () => { hasCompatibleRealms: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(PermissionDenied)).toHaveLength(0); @@ -102,17 +100,7 @@ describe('RoleMappingsGridPage', () => { hasCompatibleRealms: false, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0); @@ -138,17 +126,7 @@ describe('RoleMappingsGridPage', () => { hasCompatibleRealms: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); await nextTick(); wrapper.update(); @@ -172,17 +150,7 @@ describe('RoleMappingsGridPage', () => { hasCompatibleRealms: true, }); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); await nextTick(); wrapper.update(); @@ -212,17 +180,7 @@ describe('RoleMappingsGridPage', () => { }, ]); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI); await nextTick(); wrapper.update(); @@ -275,17 +233,7 @@ describe('RoleMappingsGridPage', () => { }, ]); - const { docLinks, notifications } = coreMock.createStart(); - const wrapper = mountWithIntl( - - ); + const wrapper = renderView(roleMappingsAPI, roleAPIClient); await nextTick(); wrapper.update(); diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx index 7e83c3654cac3..2d225f5ebd084 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx @@ -25,7 +25,12 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import { NotificationsStart, ApplicationStart, ScopedHistory } from 'src/core/public'; +import type { + NotificationsStart, + ApplicationStart, + DocLinksStart, + ScopedHistory, +} from 'src/core/public'; import { RoleMapping, Role } from '../../../../common/model'; import { EmptyPrompt } from './empty_prompt'; import { @@ -35,7 +40,6 @@ import { SectionLoading, } from '../components'; import { EDIT_ROLE_MAPPING_PATH, getEditRoleMappingHref } from '../../management_urls'; -import { DocumentationLinksService } from '../documentation_links'; import { RoleMappingsAPIClient } from '../role_mappings_api_client'; import { RoleTableDisplay } from '../../role_table_display'; import { RolesAPIClient } from '../../roles'; @@ -46,7 +50,7 @@ interface Props { rolesAPIClient: PublicMethodsOf; roleMappingsAPI: PublicMethodsOf; notifications: NotificationsStart; - docLinks: DocumentationLinksService; + docLinks: DocLinksStart; history: ScopedHistory; navigateToApp: ApplicationStart['navigateToApp']; } @@ -148,7 +152,7 @@ export class RoleMappingsGridPage extends Component { values={{ learnMoreLink: ( @@ -179,7 +183,7 @@ export class RoleMappingsGridPage extends Component { {!this.state.hasCompatibleRealms && ( <> - + )} diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx index c72aeac5ba6f2..0d7443bad5f32 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx @@ -5,11 +5,21 @@ */ jest.mock('./role_mappings_grid', () => ({ - RoleMappingsGridPage: (props: any) => `Role Mappings Page: ${JSON.stringify(props)}`, + RoleMappingsGridPage: (props: any) => + // `docLinks` object is too big to include into test snapshot, so we just check its existence. + `Role Mappings Page: ${JSON.stringify({ + ...props, + docLinks: props.docLinks ? {} : undefined, + })}`, })); jest.mock('./edit_role_mapping', () => ({ - EditRoleMappingPage: (props: any) => `Role Mapping Edit Page: ${JSON.stringify(props)}`, + EditRoleMappingPage: (props: any) => + // `docLinks` object is too big to include into test snapshot, so we just check its existence. + `Role Mapping Edit Page: ${JSON.stringify({ + ...props, + docLinks: props.docLinks ? {} : undefined, + })}`, })); import { roleMappingsManagementApp } from './role_mappings_management_app'; @@ -54,7 +64,7 @@ describe('roleMappingsManagementApp', () => { expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Role Mappings' }]); expect(container).toMatchInlineSnapshot(`
- Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"docLinks":{"mappingRoles":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/mapping-roles.html","createRoleMapping":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html","createRoleMappingTemplates":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html#_role_templates","roleMappingFieldRules":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/role-mapping-resources.html#mapping-roles-rule-field"},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}} + Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}}
`); @@ -73,7 +83,7 @@ describe('roleMappingsManagementApp', () => { ]); expect(container).toMatchInlineSnapshot(`
- Role Mapping Edit Page: {"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{"mappingRoles":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/mapping-roles.html","createRoleMapping":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html","createRoleMappingTemplates":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html#_role_templates","roleMappingFieldRules":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/role-mapping-resources.html#mapping-roles-rule-field"},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}} + Role Mapping Edit Page: {"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
`); @@ -94,7 +104,7 @@ describe('roleMappingsManagementApp', () => { ]); expect(container).toMatchInlineSnapshot(`
- Role Mapping Edit Page: {"name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{"mappingRoles":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/mapping-roles.html","createRoleMapping":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html","createRoleMappingTemplates":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html#_role_templates","roleMappingFieldRules":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/role-mapping-resources.html#mapping-roles-rule-field"},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}}} + Role Mapping Edit Page: {"name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}}}
`); diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx index ce4ded5a9acbc..28b452c10c237 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx @@ -11,8 +11,8 @@ import { i18n } from '@kbn/i18n'; import { StartServicesAccessor } from 'src/core/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { PluginStartDependencies } from '../../plugin'; -import { DocumentationLinksService } from './documentation_links'; import { tryDecodeURIComponent } from '../url_utils'; +import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; interface CreateParams { getStartServices: StartServicesAccessor; @@ -39,7 +39,7 @@ export const roleMappingsManagementApp = Object.freeze({ ]; const [ - [{ docLinks, http, notifications, i18n: i18nStart }], + [core], { RoleMappingsGridPage }, { EditRoleMappingPage }, { RoleMappingsAPIClient }, @@ -52,16 +52,15 @@ export const roleMappingsManagementApp = Object.freeze({ import('../roles'), ]); - const roleMappingsAPIClient = new RoleMappingsAPIClient(http); - const dockLinksService = new DocumentationLinksService(docLinks); + const roleMappingsAPIClient = new RoleMappingsAPIClient(core.http); const RoleMappingsGridPageWithBreadcrumbs = () => { setBreadcrumbs(roleMappingsBreadcrumbs); return ( @@ -90,27 +89,29 @@ export const roleMappingsManagementApp = Object.freeze({ ); }; render( - - - - - - - - - - - - , + + + + + + + + + + + + + + , element ); diff --git a/x-pack/plugins/security/public/management/roles/documentation_links.ts b/x-pack/plugins/security/public/management/roles/documentation_links.ts deleted file mode 100644 index aa19fbecb9c7b..0000000000000 --- a/x-pack/plugins/security/public/management/roles/documentation_links.ts +++ /dev/null @@ -1,31 +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 { DocLinksStart } from 'src/core/public'; - -export class DocumentationLinksService { - private readonly esClusterPrivileges: string; - private readonly esRunAsPrivilege: string; - private readonly esIndicesPrivileges: string; - - constructor(docLinks: DocLinksStart) { - this.esClusterPrivileges = `${docLinks.links.security.clusterPrivileges}`; - this.esRunAsPrivilege = `${docLinks.links.security.runAsPrivilege}`; - this.esIndicesPrivileges = `${docLinks.links.security.indicesPrivileges}`; - } - - public getESClusterPrivilegesDocUrl() { - return `${this.esClusterPrivileges}`; - } - - public getESRunAsPrivilegesDocUrl() { - return `${this.esRunAsPrivilege}`; - } - - public getESIndicesPrivilegesDocUrl() { - return `${this.esIndicesPrivileges}`; - } -} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index b86fa1f175e96..e431b49bf2f84 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -11,7 +11,6 @@ import { mountWithIntl, nextTick } from '@kbn/test/jest'; import { Capabilities } from 'src/core/public'; import { KibanaFeature } from '../../../../../features/public'; import { Role } from '../../../../common/model'; -import { DocumentationLinksService } from '../documentation_links'; import { EditRolePage } from './edit_role_page'; import { SimplePrivilegeSection } from './privileges/kibana/simple_privilege_section'; @@ -184,7 +183,7 @@ function getProps({ userAPIClient, getFeatures: () => Promise.resolve(buildFeatures()), notifications, - docLinks: new DocumentationLinksService(docLinks), + docLinks, fatalErrors, uiCapabilities: buildUICapabilities(canManageSpaces), history: scopedHistoryMock.create(), diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index d24191c54bd94..c750ec373b9f7 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -38,7 +38,7 @@ import { IHttpFetchError, NotificationsStart, } from 'src/core/public'; -import { ScopedHistory } from 'kibana/public'; +import type { DocLinksStart, ScopedHistory } from 'kibana/public'; import { FeaturesPluginStart } from '../../../../../features/public'; import { KibanaFeature } from '../../../../../features/common'; import { IndexPatternsContract } from '../../../../../../../src/plugins/data/public'; @@ -61,7 +61,6 @@ import { ElasticsearchPrivileges, KibanaPrivilegesRegion } from './privileges'; import { ReservedRoleBadge } from './reserved_role_badge'; import { SecurityLicense } from '../../../../common/licensing'; import { UserAPIClient } from '../../users'; -import { DocumentationLinksService } from '../documentation_links'; import { IndicesAPIClient } from '../indices_api_client'; import { RolesAPIClient } from '../roles_api_client'; import { PrivilegesAPIClient } from '../privileges_api_client'; @@ -77,7 +76,7 @@ interface Props { rolesAPIClient: PublicMethodsOf; privilegesAPIClient: PublicMethodsOf; getFeatures: FeaturesPluginStart['getFeatures']; - docLinks: DocumentationLinksService; + docLinks: DocLinksStart; http: HttpStart; license: SecurityLicense; uiCapabilities: Capabilities; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx index 316822f7db024..4a29c5a5b134c 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; -import { DocumentationLinksService } from '../../../documentation_links'; import { RoleValidator } from '../../validate_role'; import { ClusterPrivileges } from './cluster_privileges'; import { ElasticsearchPrivileges } from './elasticsearch_privileges'; @@ -45,7 +44,7 @@ function getProps() { index: ['all', 'read', 'write', 'index'], }, indicesAPIClient: indicesAPIClientMock.create(), - docLinks: new DocumentationLinksService(docLinks), + docLinks, license, }; } diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx index 8fc09ce167400..ca7a086639051 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { PublicMethodsOf } from '@kbn/utility-types'; import React, { Component, Fragment } from 'react'; +import type { DocLinksStart } from 'src/core/public'; import { Role, BuiltinESPrivileges } from '../../../../../../common/model'; import { SecurityLicense } from '../../../../../../common/licensing'; import { IndicesAPIClient } from '../../../indices_api_client'; @@ -26,13 +27,12 @@ import { RoleValidator } from '../../validate_role'; import { CollapsiblePanel } from '../../collapsible_panel'; import { ClusterPrivileges } from './cluster_privileges'; import { IndexPrivileges } from './index_privileges'; -import { DocumentationLinksService } from '../../../documentation_links'; interface Props { role: Role; editable: boolean; indicesAPIClient: PublicMethodsOf; - docLinks: DocumentationLinksService; + docLinks: DocLinksStart; license: SecurityLicense; onChange: (role: Role) => void; runAsUsers: string[]; @@ -90,7 +90,7 @@ export class ElasticsearchPrivileges extends Component { id="xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription" defaultMessage="Manage the actions this role can perform against your cluster. " /> - {this.learnMore(docLinks.getESClusterPrivilegesDocUrl())} + {this.learnMore(docLinks.links.security.clusterPrivileges)}

} > @@ -120,7 +120,7 @@ export class ElasticsearchPrivileges extends Component { id="xpack.security.management.editRole.elasticSearchPrivileges.howToBeSubmittedOnBehalfOfOtherUsersDescription" defaultMessage="Allow requests to be submitted on the behalf of other users. " /> - {this.learnMore(docLinks.getESRunAsPrivilegesDocUrl())} + {this.learnMore(docLinks.links.security.runAsPrivilege)}

} > @@ -164,7 +164,7 @@ export class ElasticsearchPrivileges extends Component { id="xpack.security.management.editRole.elasticSearchPrivileges.controlAccessToClusterDataDescription" defaultMessage="Control access to the data in your cluster. " /> - {this.learnMore(docLinks.getESIndicesPrivilegesDocUrl())} + {this.learnMore(docLinks.links.security.indicesPrivileges)}

diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx index 8003b21f5d906..12c1951fc60f0 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx @@ -11,7 +11,9 @@ jest.mock('./roles_grid', () => ({ })); jest.mock('./edit_role', () => ({ - EditRolePage: (props: any) => `Role Edit Page: ${JSON.stringify(props)}`, + // `docLinks` object is too big to include into test snapshot, so we just check its existence. + EditRolePage: (props: any) => + `Role Edit Page: ${JSON.stringify({ ...props, docLinks: props.docLinks ? {} : undefined })}`, })); import { rolesManagementApp } from './roles_management_app'; @@ -87,7 +89,7 @@ describe('rolesManagementApp', () => { expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }, { text: 'Create' }]); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esClusterPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-cluster","esRunAsPrivilege":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#_run_as_privilege","esIndicesPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-indices"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}} + Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
`); @@ -108,7 +110,7 @@ describe('rolesManagementApp', () => { ]); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esClusterPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-cluster","esRunAsPrivilege":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#_run_as_privilege","esIndicesPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-indices"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}} + Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}}
`); @@ -126,7 +128,7 @@ describe('rolesManagementApp', () => { expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }, { text: 'Create' }]); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esClusterPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-cluster","esRunAsPrivilege":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#_run_as_privilege","esIndicesPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-indices"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}} + Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}}
`); diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx index d5b3b4998a09d..26c679516b46e 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx @@ -12,7 +12,6 @@ import { StartServicesAccessor, FatalErrorsSetup } from 'src/core/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { SecurityLicense } from '../../../common/licensing'; import { PluginStartDependencies } from '../../plugin'; -import { DocumentationLinksService } from './documentation_links'; import { tryDecodeURIComponent } from '../url_utils'; interface CreateParams { @@ -97,7 +96,7 @@ export const rolesManagementApp = Object.freeze({ notifications={notifications} fatalErrors={fatalErrors} license={license} - docLinks={new DocumentationLinksService(docLinks)} + docLinks={docLinks} uiCapabilities={application.capabilities} indexPatterns={data.indexPatterns} history={history} diff --git a/x-pack/plugins/security/public/security_checkup/components/insecure_cluster_alert.tsx b/x-pack/plugins/security/public/security_checkup/components/insecure_cluster_alert.tsx index 310caeac91dc1..844444c0e64d5 100644 --- a/x-pack/plugins/security/public/security_checkup/components/insecure_cluster_alert.tsx +++ b/x-pack/plugins/security/public/security_checkup/components/insecure_cluster_alert.tsx @@ -7,7 +7,7 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { MountPoint } from 'kibana/public'; +import type { DocLinksStart, MountPoint } from 'kibana/public'; import { EuiCheckbox, EuiText, @@ -16,7 +16,6 @@ import { EuiFlexItem, EuiButton, } from '@elastic/eui'; -import { DocumentationLinksService } from '../documentation_links'; export const insecureClusterAlertTitle = i18n.translate( 'xpack.security.checkup.insecureClusterTitle', @@ -24,12 +23,15 @@ export const insecureClusterAlertTitle = i18n.translate( ); export const insecureClusterAlertText = ( - getDocLinksService: () => DocumentationLinksService, + getDocLinks: () => DocLinksStart, onDismiss: (persist: boolean) => void ) => ((e) => { const AlertText = () => { const [persist, setPersist] = useState(false); + const enableSecurityDocLink = `${ + getDocLinks().links.security.elasticsearchEnableSecurity + }?blade=kibanasecuritymessage`; return ( @@ -56,7 +58,7 @@ export const insecureClusterAlertText = ( size="s" color="primary" fill - href={getDocLinksService().getEnableSecurityDocUrl()} + href={enableSecurityDocLink} target="_blank" data-test-subj="learnMoreButton" > diff --git a/x-pack/plugins/security/public/security_checkup/documentation_links.ts b/x-pack/plugins/security/public/security_checkup/documentation_links.ts deleted file mode 100644 index 4a2a2bc968cc6..0000000000000 --- a/x-pack/plugins/security/public/security_checkup/documentation_links.ts +++ /dev/null @@ -1,19 +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 { DocLinksStart } from 'src/core/public'; - -export class DocumentationLinksService { - private readonly esEnableSecurity: string; - - constructor(docLinks: DocLinksStart) { - this.esEnableSecurity = `${docLinks.links.security.elasticsearchEnableSecurity}`; - } - - public getEnableSecurityDocUrl() { - return `${this.esEnableSecurity}?blade=kibanasecuritymessage`; - } -} diff --git a/x-pack/plugins/security/public/security_checkup/security_checkup_service.tsx b/x-pack/plugins/security/public/security_checkup/security_checkup_service.tsx index a0ea194170dff..bda9f210b02ec 100644 --- a/x-pack/plugins/security/public/security_checkup/security_checkup_service.tsx +++ b/x-pack/plugins/security/public/security_checkup/security_checkup_service.tsx @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DocLinksStart } from 'kibana/public'; +import type { DocLinksStart } from 'kibana/public'; import { SecurityOssPluginSetup, SecurityOssPluginStart, } from '../../../../../src/plugins/security_oss/public'; import { insecureClusterAlertTitle, insecureClusterAlertText } from './components'; -import { DocumentationLinksService } from './documentation_links'; interface SetupDeps { securityOssSetup: SecurityOssPluginSetup; @@ -25,13 +24,13 @@ interface StartDeps { export class SecurityCheckupService { private securityOssStart?: SecurityOssPluginStart; - private docLinksService?: DocumentationLinksService; + private docLinks?: DocLinksStart; public setup({ securityOssSetup }: SetupDeps) { securityOssSetup.insecureCluster.setAlertTitle(insecureClusterAlertTitle); securityOssSetup.insecureCluster.setAlertText( insecureClusterAlertText( - () => this.docLinksService!, + () => this.docLinks!, (persist: boolean) => this.onDismiss(persist) ) ); @@ -39,7 +38,7 @@ export class SecurityCheckupService { public start({ securityOssStart, docLinks }: StartDeps) { this.securityOssStart = securityOssStart; - this.docLinksService = new DocumentationLinksService(docLinks); + this.docLinks = docLinks; } private onDismiss(persist: boolean) { From 3dcb98ec670e4b1b44febdf632523c9e5dbea955 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 15 Jan 2021 13:32:16 +0100 Subject: [PATCH 03/38] [ILM] Absolute to relative time conversion (#87822) * cleaning up unused types and legacy logic * added new relative age logic with unit tests * export the calculate relative timing function that returns millisecond values * added exports to index.ts file * copy update and test update Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/types/policies.ts | 19 - .../data_tiers/determine_allocation_type.ts | 34 +- ...absolute_timing_to_relative_timing.test.ts | 507 ++++++++++++++++++ .../lib/absolute_timing_to_relative_timing.ts | 185 +++++++ .../sections/edit_policy/lib/index.ts | 12 + 5 files changed, 705 insertions(+), 52 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts diff --git a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts index 58468f06e3b2d..1f4b06e80c49f 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts @@ -154,25 +154,6 @@ export interface PhaseWithMinAge { selectedMinimumAgeUnits: string; } -/** - * Different types of allocation markers we use in deserialized policies. - * - * default - use data tier based data allocation based on node roles -- this is ES best practice mode. - * custom - use node_attrs to allocate data to specific nodes - * none - do not move data anywhere when entering a phase - */ -export type DataTierAllocationType = 'default' | 'custom' | 'none'; - -export interface PhaseWithAllocationAction { - selectedNodeAttrs: string; - selectedReplicaCount: string; - /** - * A string value indicating allocation type. If unspecified we assume the user - * wants to use default allocation. - */ - dataTierAllocationType: DataTierAllocationType; -} - export interface PhaseWithIndexPriority { phaseIndexPriority: string; } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/determine_allocation_type.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/determine_allocation_type.ts index 20ac439e9964f..6dde03ec4593f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/determine_allocation_type.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/determine_allocation_type.ts @@ -4,39 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DataTierAllocationType, AllocateAction, MigrateAction } from '../../../../common/types'; - -/** - * Determine what deserialized state the policy config represents. - * - * See {@DataTierAllocationType} for more information. - */ -export const determineDataTierAllocationTypeLegacy = ( - actions: { - allocate?: AllocateAction; - migrate?: MigrateAction; - } = {} -): DataTierAllocationType => { - const { allocate, migrate } = actions; - - if (migrate?.enabled === false) { - return 'none'; - } - - if (!allocate) { - return 'default'; - } - - if ( - (allocate.require && Object.keys(allocate.require).length) || - (allocate.include && Object.keys(allocate.include).length) || - (allocate.exclude && Object.keys(allocate.exclude).length) - ) { - return 'custom'; - } - - return 'default'; -}; +import { AllocateAction, MigrateAction } from '../../../../common/types'; export const determineDataTierAllocationType = ( actions: { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts new file mode 100644 index 0000000000000..28910871fa33b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts @@ -0,0 +1,507 @@ +/* + * 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 { deserializer } from '../form'; + +import { + absoluteTimingToRelativeTiming, + calculateRelativeTimingMs, +} from './absolute_timing_to_relative_timing'; + +describe('Conversion of absolute policy timing to relative timing', () => { + describe('calculateRelativeTimingMs', () => { + describe('policy that never deletes data (keep forever)', () => { + test('always hot', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + }, + }) + ) + ).toEqual({ total: Infinity, phases: { hot: Infinity, warm: undefined, cold: undefined } }); + }); + + test('hot, then always warm', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + actions: {}, + }, + }, + }) + ) + ).toEqual({ total: Infinity, phases: { hot: 0, warm: Infinity, cold: undefined } }); + }); + + test('hot, then warm, then always cold', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '1M', + actions: {}, + }, + cold: { + min_age: '34d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: Infinity, + phases: { + hot: 2592000000, + warm: 345600000, + cold: Infinity, + }, + }); + }); + + test('hot, then always cold', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + cold: { + min_age: '34d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: Infinity, + phases: { hot: 2937600000, warm: undefined, cold: Infinity }, + }); + }); + }); + + describe('policy that deletes data', () => { + test('hot, then delete', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + delete: { + min_age: '1M', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: 2592000000, + phases: { + hot: 2592000000, + warm: undefined, + cold: undefined, + }, + }); + }); + + test('hot, then warm, then delete', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '24d', + actions: {}, + }, + delete: { + min_age: '1M', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: 2592000000, + phases: { + hot: 2073600000, + warm: 518400000, + cold: undefined, + }, + }); + }); + + test('hot, then warm, then cold, then delete', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '24d', + actions: {}, + }, + cold: { + min_age: '2M', + actions: {}, + }, + delete: { + min_age: '2d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: 5270400000, + phases: { + hot: 2073600000, + warm: 3196800000, + cold: 0, + }, + }); + }); + + test('hot, then cold, then delete', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + cold: { + min_age: '2M', + actions: {}, + }, + delete: { + min_age: '2d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: 5270400000, + phases: { + hot: 5270400000, + warm: undefined, + cold: 0, + }, + }); + }); + + test('hot, then long warm, then short cold, then delete', () => { + expect( + calculateRelativeTimingMs( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '2M', + actions: {}, + }, + cold: { + min_age: '1d', + actions: {}, + }, + delete: { + min_age: '2d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: 5270400000, + phases: { + hot: 5270400000, + warm: 0, + cold: 0, + }, + }); + }); + }); + }); + + describe('absoluteTimingToRelativeTiming', () => { + describe('policy that never deletes data (keep forever)', () => { + test('always hot', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + }, + }) + ) + ).toEqual({ total: 'Forever', hot: 'Forever', warm: undefined, cold: undefined }); + }); + + test('hot, then always warm', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + actions: {}, + }, + }, + }) + ) + ).toEqual({ total: 'Forever', hot: 'Less than a day', warm: 'Forever', cold: undefined }); + }); + + test('hot, then warm, then always cold', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '1M', + actions: {}, + }, + cold: { + min_age: '34d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: 'Forever', + hot: '30 days', + warm: '4 days', + cold: 'Forever', + }); + }); + + test('hot, then always cold', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + cold: { + min_age: '34d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ total: 'Forever', hot: '34 days', warm: undefined, cold: 'Forever' }); + }); + }); + + describe('policy that deletes data', () => { + test('hot, then delete', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + delete: { + min_age: '1M', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: '30 days', + hot: '30 days', + warm: undefined, + cold: undefined, + }); + }); + + test('hot, then warm, then delete', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '24d', + actions: {}, + }, + delete: { + min_age: '1M', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: '30 days', + hot: '24 days', + warm: '6 days', + cold: undefined, + }); + }); + + test('hot, then warm, then cold, then delete', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '24d', + actions: {}, + }, + cold: { + min_age: '2M', + actions: {}, + }, + delete: { + min_age: '2d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: '61 days', + hot: '24 days', + warm: '37 days', + cold: 'Less than a day', + }); + }); + + test('hot, then cold, then delete', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + cold: { + min_age: '2M', + actions: {}, + }, + delete: { + min_age: '2d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: '61 days', + hot: '61 days', + warm: undefined, + cold: 'Less than a day', + }); + }); + + test('hot, then long warm, then short cold, then delete', () => { + expect( + absoluteTimingToRelativeTiming( + deserializer({ + name: 'test', + phases: { + hot: { + min_age: '0ms', + actions: {}, + }, + warm: { + min_age: '2M', + actions: {}, + }, + cold: { + min_age: '1d', + actions: {}, + }, + delete: { + min_age: '2d', + actions: {}, + }, + }, + }) + ) + ).toEqual({ + total: '61 days', + hot: '61 days', + warm: 'Less than a day', + cold: 'Less than a day', + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts new file mode 100644 index 0000000000000..c77b171a56bed --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts @@ -0,0 +1,185 @@ +/* + * 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. + */ + +/** + * READ ME: + * + * ILM policies express data age thresholds as minimum age from an absolute point of reference. + * The absolute point of reference could be when data was created, but it could also be when + * rollover has occurred. This is useful for configuring a policy, but when trying to understand + * how long data will be in a specific phase, when thinking of data tiers, it is not as useful. + * + * This code converts the absolute timings to _relative_ timings of the form: 30 days in hot phase, + * 40 days in warm phase then forever in cold phase. + */ + +import moment from 'moment'; +import { flow } from 'fp-ts/lib/function'; +import { i18n } from '@kbn/i18n'; + +import { splitSizeAndUnits } from '../../../lib/policies'; + +import { FormInternal } from '../types'; + +type MinAgePhase = 'warm' | 'cold' | 'delete'; + +type Phase = 'hot' | MinAgePhase; + +const i18nTexts = { + forever: i18n.translate('xpack.indexLifecycleMgmt.relativeTiming.Forever', { + defaultMessage: 'Forever', + }), + lessThanADay: i18n.translate('xpack.indexLifecycleMgmt.relativeTiming.lessThanADay', { + defaultMessage: 'Less than a day', + }), + day: i18n.translate('xpack.indexLifecycleMgmt.relativeTiming.day', { + defaultMessage: 'day', + }), + days: i18n.translate('xpack.indexLifecycleMgmt.relativeTiming.days', { + defaultMessage: 'days', + }), +}; + +interface AbsoluteTimings { + hot: { + min_age: undefined; + }; + warm?: { + min_age: string; + }; + cold?: { + min_age: string; + }; + delete?: { + min_age: string; + }; +} + +export interface PhaseAgeInMilliseconds { + total: number; + phases: { + hot: number; + warm?: number; + cold?: number; + }; +} + +const phaseOrder: Phase[] = ['hot', 'warm', 'cold', 'delete']; + +const getMinAge = (phase: MinAgePhase, formData: FormInternal) => ({ + min_age: formData.phases[phase]?.min_age + ? formData.phases[phase]!.min_age! + formData._meta[phase].minAgeUnit + : '0ms', +}); + +const formDataToAbsoluteTimings = (formData: FormInternal): AbsoluteTimings => { + const { _meta } = formData; + if (!_meta) { + return { hot: { min_age: undefined } }; + } + return { + hot: { min_age: undefined }, + warm: _meta.warm.enabled ? getMinAge('warm', formData) : undefined, + cold: _meta.cold.enabled ? getMinAge('cold', formData) : undefined, + delete: _meta.delete.enabled ? getMinAge('delete', formData) : undefined, + }; +}; + +/** + * See https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math + * for all date math values. ILM policies also support "micros" and "nanos". + */ +const getPhaseMinAgeInMilliseconds = (phase: { min_age: string }): number => { + let milliseconds: number; + const { units, size } = splitSizeAndUnits(phase.min_age); + if (units === 'micros') { + milliseconds = parseInt(size, 10) / 1e3; + } else if (units === 'nanos') { + milliseconds = parseInt(size, 10) / 1e6; + } else { + milliseconds = moment.duration(size, units as any).asMilliseconds(); + } + return milliseconds; +}; + +/** + * Given a set of phase minimum age absolute timings, like hot phase 0ms and warm phase 3d, work out + * the number of milliseconds data will reside in phase. + */ +const calculateMilliseconds = (inputs: AbsoluteTimings): PhaseAgeInMilliseconds => { + return phaseOrder.reduce( + (acc, phaseName, idx) => { + // Delete does not have an age associated with it + if (phaseName === 'delete') { + return acc; + } + const phase = inputs[phaseName]; + if (!phase) { + return acc; + } + const nextPhase = phaseOrder + .slice(idx + 1) + .find((nextPhaseName) => Boolean(inputs[nextPhaseName])); // find the next existing phase + + let nextPhaseMinAge = Infinity; + + // If we have a next phase, calculate the timing between this phase and the next + if (nextPhase && inputs[nextPhase]?.min_age) { + nextPhaseMinAge = getPhaseMinAgeInMilliseconds(inputs[nextPhase] as { min_age: string }); + } + + return { + // data will be the age of the phase with the highest min age requirement + total: Math.max(acc.total, nextPhaseMinAge), + phases: { + ...acc.phases, + [phaseName]: Math.max(nextPhaseMinAge - acc.total, 0), // get the max age for the current phase, take 0 if negative number + }, + }; + }, + { + total: 0, + phases: { + hot: 0, + warm: inputs.warm ? 0 : undefined, + cold: inputs.cold ? 0 : undefined, + }, + } + ); +}; + +const millisecondsToDays = (milliseconds?: number): string | undefined => { + if (milliseconds == null) { + return; + } + if (!isFinite(milliseconds)) { + return i18nTexts.forever; + } + const days = milliseconds / 8.64e7; + return days < 1 + ? i18nTexts.lessThanADay + : `${Math.floor(days)} ${days === 1 ? i18nTexts.day : i18nTexts.days}`; +}; + +export const normalizeTimingsToHumanReadable = ({ + total, + phases, +}: PhaseAgeInMilliseconds): { total?: string; hot?: string; warm?: string; cold?: string } => { + return { + total: millisecondsToDays(total), + hot: millisecondsToDays(phases.hot), + warm: millisecondsToDays(phases.warm), + cold: millisecondsToDays(phases.cold), + }; +}; + +export const calculateRelativeTimingMs = flow(formDataToAbsoluteTimings, calculateMilliseconds); + +export const absoluteTimingToRelativeTiming = flow( + formDataToAbsoluteTimings, + calculateMilliseconds, + normalizeTimingsToHumanReadable +); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts new file mode 100644 index 0000000000000..9593fcc810a6f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { + absoluteTimingToRelativeTiming, + calculateRelativeTimingMs, + normalizeTimingsToHumanReadable, + PhaseAgeInMilliseconds, +} from './absolute_timing_to_relative_timing'; From 701bd0998d70ccacdd5118cd573a2c6cfb29d664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Fri, 15 Jan 2021 13:39:31 +0100 Subject: [PATCH 04/38] Bump backport to 5.6.4 (#88428) --- package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index df8a41f29bf79..07984e5a237b0 100644 --- a/package.json +++ b/package.json @@ -589,7 +589,7 @@ "babel-plugin-require-context-hook": "^1.0.0", "babel-plugin-styled-components": "^1.10.7", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", - "backport": "^5.6.1", + "backport": "^5.6.4", "base64-js": "^1.3.1", "base64url": "^3.0.1", "broadcast-channel": "^3.0.3", diff --git a/yarn.lock b/yarn.lock index 5a39eacf3e68b..8595ed43bf6d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8194,7 +8194,7 @@ bach@^1.0.0: async-settle "^1.0.0" now-and-later "^2.0.0" -backport@^5.6.1: +backport@^5.6.4: version "5.6.4" resolved "https://registry.yarnpkg.com/backport/-/backport-5.6.4.tgz#8cf4bc750b26d27161306858ee9069218ad7cdfd" integrity sha512-ZhuZcGxOBHBXFBCwweVf02b+KhWe0tdgg71jPSl583YYxlru+JBRH+TFM8S0J6/6YUuTWO81M9funjehJ18jqg== From 686ece9aeade49dd4a4a6e82b63781a92a72fea5 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 15 Jan 2021 12:57:41 +0000 Subject: [PATCH 05/38] [ML] Improving existing job check in anomaly detection wizard (#87674) * [ML] Improving existing job check in anomaly detection wizard * fixing job id validation * allow group ids to be reused * updating module exists endpoint * fixing issuse with job without group list * fixing test and translation ids * fixing validator when model plot is disabled * changes based on review * adding group id check to edit job flyout * small refactor and fixing edit job issue Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/ml/common/types/job_service.ts | 24 +++ .../edit_job_flyout/edit_job_flyout.js | 27 ++-- .../common/job_validator/job_validator.ts | 34 +++-- .../jobs/new_job/common/job_validator/util.ts | 32 ---- .../common/job_validator/validators.ts | 138 ++++++++++++++---- .../components/groups/groups_input.tsx | 20 ++- .../components/job_id/job_id_input.tsx | 27 ++-- .../job_details_step/job_details.tsx | 2 + .../jobs/new_job/pages/new_job/page.tsx | 2 +- .../services/ml_api_service/jobs.ts | 17 ++- .../models/data_recognizer/data_recognizer.ts | 4 +- .../ml/server/models/job_service/groups.ts | 2 +- .../ml/server/models/job_service/jobs.ts | 35 +++-- .../models/results_service/results_service.ts | 2 +- .../apis/ml/jobs/jobs_exist_spaces.ts | 15 +- 15 files changed, 252 insertions(+), 129 deletions(-) create mode 100644 x-pack/plugins/ml/common/types/job_service.ts diff --git a/x-pack/plugins/ml/common/types/job_service.ts b/x-pack/plugins/ml/common/types/job_service.ts new file mode 100644 index 0000000000000..3121c3a82a387 --- /dev/null +++ b/x-pack/plugins/ml/common/types/job_service.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Job, JobStats } from './anomaly_detection_jobs'; + +export interface MlJobsResponse { + jobs: Job[]; + count: number; +} + +export interface MlJobsStatsResponse { + jobs: JobStats[]; + count: number; +} + +export interface JobsExistResponse { + [jobId: string]: { + exists: boolean; + isGroup: boolean; + }; +} diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js index bd781d32a6b06..97f9515810a60 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js @@ -29,6 +29,7 @@ import { saveJob } from './edit_utils'; import { loadFullJob } from '../utils'; import { validateModelMemoryLimit, validateGroupNames, isValidCustomUrls } from '../validate_job'; import { toastNotificationServiceProvider } from '../../../../services/toast_notification_service'; +import { ml } from '../../../../services/ml_api_service'; import { withKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; import { collapseLiteralStrings } from '../../../../../../shared_imports'; import { DATAFEED_STATE } from '../../../../../../common/constants/states'; @@ -195,16 +196,24 @@ export class EditJobFlyoutUI extends Component { } if (jobDetails.jobGroups !== undefined) { - if (jobDetails.jobGroups.some((j) => this.props.allJobIds.includes(j))) { - jobGroupsValidationError = i18n.translate( - 'xpack.ml.jobsList.editJobFlyout.groupsAndJobsHasSameIdErrorMessage', - { - defaultMessage: - 'A job with this ID already exists. Groups and jobs cannot use the same ID.', + jobGroupsValidationError = validateGroupNames(jobDetails.jobGroups).message; + if (jobGroupsValidationError === '') { + ml.jobs.jobsExist(jobDetails.jobGroups, true).then((resp) => { + const groups = Object.values(resp); + const valid = groups.some((g) => g.exists === true && g.isGroup === false) === false; + if (valid === false) { + this.setState({ + jobGroupsValidationError: i18n.translate( + 'xpack.ml.jobsList.editJobFlyout.groupsAndJobsHasSameIdErrorMessage', + { + defaultMessage: + 'A job with this ID already exists. Groups and jobs cannot use the same ID.', + } + ), + isValidJobDetails: false, + }); } - ); - } else { - jobGroupsValidationError = validateGroupNames(jobDetails.jobGroups).message; + }); } } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts index 1c012033e97c8..cf08de196a7d8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts @@ -14,9 +14,15 @@ import { } from '../../../../../../common/util/job_utils'; import { getNewJobLimits } from '../../../../services/ml_server_info'; import { JobCreator, JobCreatorType, isCategorizationJobCreator } from '../job_creator'; -import { populateValidationMessages, checkForExistingJobAndGroupIds } from './util'; -import { ExistingJobsAndGroups } from '../../../../services/job_service'; -import { cardinalityValidator, CardinalityValidatorResult } from './validators'; +import { populateValidationMessages } from './util'; +import { + cardinalityValidator, + CardinalityValidatorResult, + jobIdValidator, + groupIdsValidator, + JobExistsResult, + GroupsExistResult, +} from './validators'; import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../common/constants/categorization_job'; import { JOB_TYPE } from '../../../../../../common/constants/new_job'; @@ -25,7 +31,9 @@ import { JOB_TYPE } from '../../../../../../common/constants/new_job'; // after every keystroke export const VALIDATION_DELAY_MS = 500; -type AsyncValidatorsResult = Partial; +type AsyncValidatorsResult = Partial< + CardinalityValidatorResult & JobExistsResult & GroupsExistResult +>; /** * Union of possible validation results. @@ -69,7 +77,6 @@ export class JobValidator { private _validateTimeout: ReturnType | null = null; private _asyncValidators$: Array> = []; private _asyncValidatorsResult$: Observable; - private _existingJobsAndGroups: ExistingJobsAndGroups; private _basicValidations: BasicValidations = { jobId: { valid: true }, groupIds: { valid: true }, @@ -97,7 +104,7 @@ export class JobValidator { */ public validationResult$: Observable; - constructor(jobCreator: JobCreatorType, existingJobsAndGroups: ExistingJobsAndGroups) { + constructor(jobCreator: JobCreatorType) { this._jobCreator = jobCreator; this._lastJobConfig = this._jobCreator.formattedJobJson; this._lastDatafeedConfig = this._jobCreator.formattedDatafeedJson; @@ -105,9 +112,12 @@ export class JobValidator { basic: false, advanced: false, }; - this._existingJobsAndGroups = existingJobsAndGroups; - this._asyncValidators$ = [cardinalityValidator(this._jobCreatorSubject$)]; + this._asyncValidators$ = [ + cardinalityValidator(this._jobCreatorSubject$), + jobIdValidator(this._jobCreatorSubject$), + groupIdsValidator(this._jobCreatorSubject$), + ]; this._asyncValidatorsResult$ = combineLatest(this._asyncValidators$).pipe( map((res) => { @@ -208,14 +218,6 @@ export class JobValidator { datafeedConfig ); - // run addition job and group id validation - const idResults = checkForExistingJobAndGroupIds( - this._jobCreator.jobId, - this._jobCreator.groups, - this._existingJobsAndGroups - ); - populateValidationMessages(idResults, this._basicValidations, jobConfig, datafeedConfig); - this._validationSummary.basic = this._isOverallBasicValid(); // Update validation results subject this._basicValidationResult$.next(this._basicValidations); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/util.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/util.ts index 04be935ed4399..9bdb4c7708b40 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/util.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/util.ts @@ -13,8 +13,6 @@ import { } from '../../../../../../common/constants/validation'; import { getNewJobLimits } from '../../../../services/ml_server_info'; import { ValidationResults } from '../../../../../../common/util/job_utils'; -import { ExistingJobsAndGroups } from '../../../../services/job_service'; -import { JobValidationMessage } from '../../../../../../common/constants/messages'; export function populateValidationMessages( validationResults: ValidationResults, @@ -204,36 +202,6 @@ export function populateValidationMessages( } } -export function checkForExistingJobAndGroupIds( - jobId: string, - groupIds: string[], - existingJobsAndGroups: ExistingJobsAndGroups -): ValidationResults { - const messages: JobValidationMessage[] = []; - - // check that job id does not already exist as a job or group or a newly created group - if ( - existingJobsAndGroups.jobIds.includes(jobId) || - existingJobsAndGroups.groupIds.includes(jobId) || - groupIds.includes(jobId) - ) { - messages.push({ id: 'job_id_already_exists' }); - } - - // check that groups that have been newly added in this job do not already exist as job ids - const newGroups = groupIds.filter((g) => !existingJobsAndGroups.groupIds.includes(g)); - if (existingJobsAndGroups.jobIds.some((g) => newGroups.includes(g))) { - messages.push({ id: 'job_group_id_already_exists' }); - } - - return { - messages, - valid: messages.length === 0, - contains: (id: string) => messages.some((m) => id === m.id), - find: (id: string) => messages.find((m) => id === m.id), - }; -} - function invalidTimeIntervalMessage(value: string | undefined) { return i18n.translate( 'xpack.ml.newJob.wizard.validateJob.frequencyInvalidTimeIntervalFormatErrorMessage', diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/validators.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/validators.ts index eabf5588579c5..d17c5507722f4 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/validators.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/validators.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; -import { Observable, Subject } from 'rxjs'; +import { i18n } from '@kbn/i18n'; +import { distinctUntilChanged, filter, map, pluck, switchMap, startWith } from 'rxjs/operators'; +import { combineLatest, Observable, Subject } from 'rxjs'; import { CardinalityModelPlotHigh, CardinalityValidationResult, @@ -13,6 +14,7 @@ import { } from '../../../../services/ml_api_service'; import { JobCreator } from '../job_creator'; import { CombinedJob } from '../../../../../../common/types/anomaly_detection_jobs'; +import { BasicValidations } from './job_validator'; export enum VALIDATOR_SEVERITY { ERROR, @@ -26,8 +28,30 @@ export interface CardinalityValidatorError { }; } +const jobExistsErrorMessage = i18n.translate( + 'xpack.ml.newJob.wizard.validateJob.asyncJobNameAlreadyExists', + { + defaultMessage: + 'Job ID already exists. A job ID cannot be the same as an existing job or group.', + } +); +const groupExistsErrorMessage = i18n.translate( + 'xpack.ml.newJob.wizard.validateJob.asyncGroupNameAlreadyExists', + { + defaultMessage: + 'Group ID already exists. A group ID cannot be the same as an existing group or job.', + } +); + export type CardinalityValidatorResult = CardinalityValidatorError | null; +export type JobExistsResult = { + jobIdExists: BasicValidations['jobId']; +} | null; +export type GroupsExistResult = { + groupIdsExist: BasicValidations['groupIds']; +} | null; + export function isCardinalityModelPlotHigh( cardinalityValidationResult: CardinalityValidationResult ): cardinalityValidationResult is CardinalityModelPlotHigh { @@ -39,39 +63,95 @@ export function isCardinalityModelPlotHigh( export function cardinalityValidator( jobCreator$: Subject ): Observable { + return combineLatest([ + jobCreator$.pipe(pluck('modelPlot')), + jobCreator$.pipe( + filter((jobCreator) => { + return jobCreator?.modelPlot; + }), + map((jobCreator) => { + return { + jobCreator, + analysisConfigString: JSON.stringify(jobCreator.jobConfig.analysis_config, null, 2), + }; + }), + distinctUntilChanged((prev, curr) => { + return prev.analysisConfigString === curr.analysisConfigString; + }), + switchMap(({ jobCreator }) => { + // Perform a cardinality check only with enabled model plot. + return ml + .validateCardinality$({ + ...jobCreator.jobConfig, + datafeed_config: jobCreator.datafeedConfig, + } as CombinedJob) + .pipe( + map((validationResults) => { + for (const validationResult of validationResults) { + if (isCardinalityModelPlotHigh(validationResult)) { + return { + highCardinality: { + value: validationResult.modelPlotCardinality, + severity: VALIDATOR_SEVERITY.WARNING, + }, + }; + } + } + return null; + }) + ); + }), + startWith(null) + ), + ]).pipe( + map(([isModelPlotEnabled, cardinalityValidationResult]) => { + return isModelPlotEnabled ? cardinalityValidationResult : null; + }) + ); +} + +export function jobIdValidator(jobCreator$: Subject): Observable { return jobCreator$.pipe( - // Perform a cardinality check only with enabled model plot. - filter((jobCreator) => { - return jobCreator?.modelPlot; - }), map((jobCreator) => { + return jobCreator.jobId; + }), + // No need to perform an API call if the analysis configuration hasn't been changed + distinctUntilChanged((prevJobId, currJobId) => prevJobId === currJobId), + switchMap((jobId) => ml.jobs.jobsExist$([jobId], true)), + map((jobExistsResults) => { + const jobs = Object.values(jobExistsResults); + const valid = jobs?.[0].exists === false; return { - jobCreator, - analysisConfigString: JSON.stringify(jobCreator.jobConfig.analysis_config), + jobIdExists: { + valid, + ...(valid ? {} : { message: jobExistsErrorMessage }), + }, }; - }), + }) + ); +} + +export function groupIdsValidator(jobCreator$: Subject): Observable { + return jobCreator$.pipe( + map((jobCreator) => jobCreator.groups), // No need to perform an API call if the analysis configuration hasn't been changed - distinctUntilChanged((prev, curr) => { - return prev.analysisConfigString === curr.analysisConfigString; - }), - switchMap(({ jobCreator }) => { - return ml.validateCardinality$({ - ...jobCreator.jobConfig, - datafeed_config: jobCreator.datafeedConfig, - } as CombinedJob); + distinctUntilChanged( + (prevGroups, currGroups) => JSON.stringify(prevGroups) === JSON.stringify(currGroups) + ), + switchMap((groups) => { + return ml.jobs.jobsExist$(groups, true); }), - map((validationResults) => { - for (const validationResult of validationResults) { - if (isCardinalityModelPlotHigh(validationResult)) { - return { - highCardinality: { - value: validationResult.modelPlotCardinality, - severity: VALIDATOR_SEVERITY.WARNING, - }, - }; - } - } - return null; + map((jobExistsResults) => { + const groups = Object.values(jobExistsResults); + // only match jobs that exist but aren't groups. + // as we should allow existing groups to be reused. + const valid = groups.some((g) => g.exists === true && g.isGroup === false) === false; + return { + groupIdsExist: { + valid, + ...(valid ? {} : { message: groupExistsErrorMessage }), + }, + }; }) ); } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/groups/groups_input.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/groups/groups_input.tsx index a693127e07f48..3fd191d653826 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/groups/groups_input.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/groups/groups_input.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, useState, useContext, useEffect } from 'react'; +import React, { FC, useState, useContext, useEffect, useMemo } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { JobCreatorContext } from '../../../job_creator_context'; @@ -17,7 +17,19 @@ export const GroupsInput: FC = () => { ); const { existingJobsAndGroups } = useContext(JobCreatorContext); const [selectedGroups, setSelectedGroups] = useState(jobCreator.groups); - const [validation, setValidation] = useState(jobValidator.groupIds); + + const validation = useMemo(() => { + const valid = + jobValidator.groupIds.valid === true && + jobValidator.latestValidationResult.groupIdsExist?.valid === true; + const message = + jobValidator.groupIds.message ?? jobValidator.latestValidationResult.groupIdsExist?.message; + + return { + valid, + message, + }; + }, [jobValidatorUpdated]); useEffect(() => { jobCreator.groups = selectedGroups; @@ -61,10 +73,6 @@ export const GroupsInput: FC = () => { setSelectedGroups([...selectedOptions, newGroup].map((g) => g.label)); } - useEffect(() => { - setValidation(jobValidator.groupIds); - }, [jobValidatorUpdated]); - return ( { JobCreatorContext ); const [jobId, setJobId] = useState(jobCreator.jobId); - const [validation, setValidation] = useState(jobValidator.jobId); + + const validation = useMemo(() => { + const isEmptyId = jobId === ''; + const valid = + isEmptyId === true || + (jobValidator.jobId.valid === true && + jobValidator.latestValidationResult.jobIdExists?.valid === true); + + const message = + jobValidator.jobId.message ?? jobValidator.latestValidationResult.jobIdExists?.message; + + return { + valid, + message, + }; + }, [jobValidatorUpdated]); useEffect(() => { jobCreator.jobId = jobId; jobCreatorUpdate(); }, [jobId]); - useEffect(() => { - const isEmptyId = jobId === ''; - setValidation({ - valid: isEmptyId === true || jobValidator.jobId.valid, - message: isEmptyId === false ? jobValidator.jobId.message : '', - }); - }, [jobValidatorUpdated]); - return ( = ({ jobValidator.jobId.valid && jobValidator.modelMemoryLimit.valid && jobValidator.groupIds.valid && + jobValidator.latestValidationResult.jobIdExists?.valid === true && + jobValidator.latestValidationResult.groupIdsExist?.valid === true && jobValidator.validating === false; setNextActive(active); }, [jobValidatorUpdated]); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx index 8e223b69b00e8..8f7f93763fdd6 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx @@ -182,7 +182,7 @@ export const Page: FC = ({ existingJobsAndGroups, jobType }) => { const chartLoader = new ChartLoader(mlContext.currentIndexPattern, mlContext.combinedQuery); - const jobValidator = new JobValidator(jobCreator, existingJobsAndGroups); + const jobValidator = new JobValidator(jobCreator); const resultsLoader = new ResultsLoader(jobCreator, chartInterval, chartLoader); diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts index d356fc0ef339b..10e035103dbec 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Observable } from 'rxjs'; import { HttpService } from '../http_service'; import { basePath } from './index'; @@ -23,6 +24,7 @@ import { } from '../../../../common/types/categories'; import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../common/constants/categorization_job'; import { Category } from '../../../../common/types/categories'; +import { JobsExistResponse } from '../../../../common/types/job_service'; export const jobsApiProvider = (httpService: HttpService) => ({ jobsSummary(jobIds: string[]) { @@ -138,9 +140,18 @@ export const jobsApiProvider = (httpService: HttpService) => ({ }); }, - jobsExist(jobIds: string[]) { - const body = JSON.stringify({ jobIds }); - return httpService.http({ + jobsExist(jobIds: string[], allSpaces: boolean = false) { + const body = JSON.stringify({ jobIds, allSpaces }); + return httpService.http({ + path: `${basePath()}/jobs/jobs_exist`, + method: 'POST', + body, + }); + }, + + jobsExist$(jobIds: string[], allSpaces: boolean = false): Observable { + const body = JSON.stringify({ jobIds, allSpaces }); + return httpService.http$({ path: `${basePath()}/jobs/jobs_exist`, method: 'POST', body, diff --git a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts index 67a95de3b3d71..c93af249be203 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts +++ b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts @@ -43,7 +43,7 @@ import { fieldsServiceProvider } from '../fields_service'; import { jobServiceProvider } from '../job_service'; import { resultsServiceProvider } from '../results_service'; import { JobExistResult, JobStat } from '../../../common/types/data_recognizer'; -import { MlJobsStatsResponse } from '../job_service/jobs'; +import { MlJobsStatsResponse } from '../../../common/types/job_service'; import { JobSavedObjectService } from '../../saved_objects'; const ML_DIR = 'ml'; @@ -533,7 +533,7 @@ export class DataRecognizer { const jobInfo = await this._jobsService.jobsExist(jobIds); // Check if the value for any of the jobs is false. - const doJobsExist = Object.values(jobInfo).includes(false) === false; + const doJobsExist = Object.values(jobInfo).every((j) => j.exists === true); results.jobsExist = doJobsExist; if (doJobsExist === true) { diff --git a/x-pack/plugins/ml/server/models/job_service/groups.ts b/x-pack/plugins/ml/server/models/job_service/groups.ts index f6073ae7071b0..81b0494cbdf27 100644 --- a/x-pack/plugins/ml/server/models/job_service/groups.ts +++ b/x-pack/plugins/ml/server/models/job_service/groups.ts @@ -7,7 +7,7 @@ import { CalendarManager } from '../calendar'; import { GLOBAL_CALENDAR } from '../../../common/constants/calendars'; import { Job } from '../../../common/types/anomaly_detection_jobs'; -import { MlJobsResponse } from './jobs'; +import { MlJobsResponse } from '../../../common/types/job_service'; import type { MlClient } from '../../lib/ml_client'; interface Group { diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index 9abc34dfbb5d9..d47a1d4b4892d 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -16,11 +16,14 @@ import { JOB_STATE, DATAFEED_STATE } from '../../../common/constants/states'; import { MlSummaryJob, AuditMessage, - Job, - JobStats, DatafeedWithStats, CombinedJobWithStats, } from '../../../common/types/anomaly_detection_jobs'; +import { + MlJobsResponse, + MlJobsStatsResponse, + JobsExistResponse, +} from '../../../common/types/job_service'; import { GLOBAL_CALENDAR } from '../../../common/constants/calendars'; import { datafeedsProvider, MlDatafeedsResponse, MlDatafeedsStatsResponse } from './datafeeds'; import { jobAuditMessagesProvider } from '../job_audit_messages'; @@ -34,16 +37,6 @@ import { import { groupsProvider } from './groups'; import type { MlClient } from '../../lib/ml_client'; -export interface MlJobsResponse { - jobs: Job[]; - count: number; -} - -export interface MlJobsStatsResponse { - jobs: JobStats[]; - count: number; -} - interface Results { [id: string]: { [status: string]: boolean; @@ -420,10 +413,18 @@ export function jobsProvider(client: IScopedClusterClient, mlClient: MlClient) { // Checks if each of the jobs in the specified list of IDs exist. // Job IDs in supplied array may contain wildcard '*' characters // e.g. *_low_request_rate_ecs - async function jobsExist(jobIds: string[] = [], allSpaces: boolean = false) { - const results: { [id: string]: boolean } = {}; + async function jobsExist( + jobIds: string[] = [], + allSpaces: boolean = false + ): Promise { + const results: JobsExistResponse = {}; for (const jobId of jobIds) { try { + if (jobId === '') { + results[jobId] = { exists: false, isGroup: false }; + continue; + } + const { body } = allSpaces ? await client.asInternalUser.ml.getJobs({ job_id: jobId, @@ -431,13 +432,15 @@ export function jobsProvider(client: IScopedClusterClient, mlClient: MlClient) { : await mlClient.getJobs({ job_id: jobId, }); - results[jobId] = body.count > 0; + + const isGroup = body.jobs.some((j) => j.groups !== undefined && j.groups.includes(jobId)); + results[jobId] = { exists: body.count > 0, isGroup }; } catch (e) { // if a non-wildcarded job id is supplied, the get jobs endpoint will 404 if (e.statusCode !== 404) { throw e; } - results[jobId] = false; + results[jobId] = { exists: false, isGroup: false }; } } return results; diff --git a/x-pack/plugins/ml/server/models/results_service/results_service.ts b/x-pack/plugins/ml/server/models/results_service/results_service.ts index a196f1034fdd3..a153944f37bef 100644 --- a/x-pack/plugins/ml/server/models/results_service/results_service.ts +++ b/x-pack/plugins/ml/server/models/results_service/results_service.ts @@ -17,7 +17,7 @@ import { } from '../../../common/types/anomalies'; import { JOB_ID, PARTITION_FIELD_VALUE } from '../../../common/constants/anomalies'; import { GetStoppedPartitionResult } from '../../../common/types/results'; -import { MlJobsResponse } from '../job_service/jobs'; +import { MlJobsResponse } from '../../../common/types/job_service'; import type { MlClient } from '../../lib/ml_client'; // Service for carrying out Elasticsearch queries to obtain data for the diff --git a/x-pack/test/api_integration/apis/ml/jobs/jobs_exist_spaces.ts b/x-pack/test/api_integration/apis/ml/jobs/jobs_exist_spaces.ts index 0eb8f4abebf93..ca483ffa7bc1e 100644 --- a/x-pack/test/api_integration/apis/ml/jobs/jobs_exist_spaces.ts +++ b/x-pack/test/api_integration/apis/ml/jobs/jobs_exist_spaces.ts @@ -17,6 +17,7 @@ export default ({ getService }: FtrProviderContext) => { const jobIdSpace1 = 'fq_single_space1'; const jobIdSpace2 = 'fq_single_space2'; + const groupSpace1 = 'farequote'; const idSpace1 = 'space1'; const idSpace2 = 'space2'; @@ -57,17 +58,25 @@ export default ({ getService }: FtrProviderContext) => { it('should find single job from same space', async () => { const body = await runRequest(idSpace1, 200, [jobIdSpace1]); - expect(body).to.eql({ [jobIdSpace1]: true }); + expect(body).to.eql({ [jobIdSpace1]: { exists: true, isGroup: false } }); + }); + + it('should find single job from same space', async () => { + const body = await runRequest(idSpace1, 200, [groupSpace1]); + expect(body).to.eql({ [groupSpace1]: { exists: true, isGroup: true } }); }); it('should not find single job from different space', async () => { const body = await runRequest(idSpace2, 200, [jobIdSpace1]); - expect(body).to.eql({ [jobIdSpace1]: false }); + expect(body).to.eql({ [jobIdSpace1]: { exists: false, isGroup: false } }); }); it('should only find job from same space when called with a list of jobs', async () => { const body = await runRequest(idSpace1, 200, [jobIdSpace1, jobIdSpace2]); - expect(body).to.eql({ [jobIdSpace1]: true, [jobIdSpace2]: false }); + expect(body).to.eql({ + [jobIdSpace1]: { exists: true, isGroup: false }, + [jobIdSpace2]: { exists: false, isGroup: false }, + }); }); }); }; From a0b787c39a7eab88c2de3a64aa872779b94abe39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Fri, 15 Jan 2021 08:13:15 -0500 Subject: [PATCH 06/38] Fix flaky test for legacy authorization (#87642) * Unskip test * Increase attempts to 2 for retryIfConflicts * Cleanup authorization for updateApiKey --- .../plugins/alerts/server/alerts_client/alerts_client.ts | 5 +---- .../server/authorization/alerts_authorization.mock.ts | 1 - .../alerts/server/authorization/alerts_authorization.ts | 7 +------ x-pack/plugins/alerts/server/lib/retry_if_conflicts.ts | 4 +--- .../security_and_spaces/tests/alerting/rbac_legacy.ts | 2 +- 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index b6606d9d7fe57..457079229de94 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -817,10 +817,7 @@ export class AlertsClient { attributes.consumer, WriteOperations.UpdateApiKey ); - if ( - attributes.actions.length && - !this.authorization.shouldUseLegacyAuthorization(attributes) - ) { + if (attributes.actions.length) { await this.actionsAuthorization.ensureAuthorized('execute'); } } catch (error) { diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts index 171e3978d0d0d..30de2c79732ce 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts @@ -14,7 +14,6 @@ const createAlertsAuthorizationMock = () => { ensureAuthorized: jest.fn(), filterByAlertTypeAuthorization: jest.fn(), getFindAuthorizationFilter: jest.fn(), - shouldUseLegacyAuthorization: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts index 6814e4ac1cc1b..29f2078bc61e4 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts @@ -8,13 +8,12 @@ import Boom from '@hapi/boom'; import { map, mapValues, fromPairs, has } from 'lodash'; import { KibanaRequest } from 'src/core/server'; import { ALERTS_FEATURE_ID } from '../../common'; -import { AlertTypeRegistry, RawAlert } from '../types'; +import { AlertTypeRegistry } from '../types'; import { SecurityPluginSetup } from '../../../security/server'; import { RegistryAlertType } from '../alert_type_registry'; import { PluginStartContract as FeaturesPluginStart } from '../../../features/server'; import { AlertsAuthorizationAuditLogger, ScopeType } from './audit_logger'; import { Space } from '../../../spaces/server'; -import { LEGACY_LAST_MODIFIED_VERSION } from '../saved_objects/migrations'; import { asFiltersByAlertTypeAndConsumer } from './alerts_authorization_kuery'; import { KueryNode } from '../../../../../src/plugins/data/server'; @@ -112,10 +111,6 @@ export class AlertsAuthorization { ); } - public shouldUseLegacyAuthorization(alert: RawAlert): boolean { - return alert.meta?.versionApiKeyLastmodified === LEGACY_LAST_MODIFIED_VERSION; - } - private shouldCheckAuthorization(): boolean { return this.authorization?.mode?.useRbacForRequest(this.request) ?? false; } diff --git a/x-pack/plugins/alerts/server/lib/retry_if_conflicts.ts b/x-pack/plugins/alerts/server/lib/retry_if_conflicts.ts index 9cb1d7975855c..59ecc59ab57f8 100644 --- a/x-pack/plugins/alerts/server/lib/retry_if_conflicts.ts +++ b/x-pack/plugins/alerts/server/lib/retry_if_conflicts.ts @@ -15,9 +15,7 @@ import { Logger, SavedObjectsErrorHelpers } from '../../../../../src/core/server type RetryableForConflicts = () => Promise; // number of times to retry when conflicts occur -// note: it seems unlikely that we'd need more than one retry, but leaving -// this statically configurable in case we DO need > 1 -export const RetryForConflictsAttempts = 1; +export const RetryForConflictsAttempts = 2; // milliseconds to wait before retrying when conflicts occur // note: we considered making this random, to help avoid a stampede, but diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts index 992d9210b9761..2b25c82cc92e5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts @@ -62,7 +62,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); }); - it.skip('should schedule actions on legacy alerts', async () => { + it('should schedule actions on legacy alerts', async () => { const reference = `alert:migrated-to-7.10:${user.username}`; const migratedAlertId = MIGRATED_ALERT_ID[user.username]; From 5fd0027ba279fcc57f827d3580317c31f3c038dd Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 15 Jan 2021 16:45:26 +0300 Subject: [PATCH 07/38] use the same defaults as Lens (#87991) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/xy_settings.tsx | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/plugins/vis_type_xy/public/components/xy_settings.tsx b/src/plugins/vis_type_xy/public/components/xy_settings.tsx index 3682fdf3350b0..18099173c56a9 100644 --- a/src/plugins/vis_type_xy/public/components/xy_settings.tsx +++ b/src/plugins/vis_type_xy/public/components/xy_settings.tsx @@ -32,6 +32,8 @@ import { LegendColorPicker, TooltipProps, TickFormatter, + VerticalAlignment, + HorizontalAlignment, } from '@elastic/charts'; import { renderEndzoneTooltip } from '../../../charts/public'; @@ -70,6 +72,27 @@ type XYSettingsProps = Pick< legendPosition: Position; }; +function getValueLabelsStyling(isHorizontal: boolean) { + const VALUE_LABELS_MAX_FONTSIZE = 15; + const VALUE_LABELS_MIN_FONTSIZE = 10; + const VALUE_LABELS_VERTICAL_OFFSET = -10; + const VALUE_LABELS_HORIZONTAL_OFFSET = 10; + + return { + displayValue: { + fontSize: { min: VALUE_LABELS_MIN_FONTSIZE, max: VALUE_LABELS_MAX_FONTSIZE }, + fill: { textInverted: true, textBorder: 2 }, + alignment: isHorizontal + ? { + vertical: VerticalAlignment.Middle, + } + : { horizontal: HorizontalAlignment.Center }, + offsetX: isHorizontal ? VALUE_LABELS_HORIZONTAL_OFFSET : 0, + offsetY: isHorizontal ? 0 : VALUE_LABELS_VERTICAL_OFFSET, + }, + }; +} + export const XYSettings: FC = ({ markSizeRatio, rotation, @@ -92,10 +115,7 @@ export const XYSettings: FC = ({ const theme = themeService.useChartsTheme(); const baseTheme = themeService.useChartsBaseTheme(); const dimmingOpacity = getUISettings().get('visualization:dimmingOpacity'); - const fontSize = - typeof theme.barSeriesStyle?.displayValue?.fontSize === 'number' - ? { min: theme.barSeriesStyle?.displayValue?.fontSize } - : theme.barSeriesStyle?.displayValue?.fontSize ?? { min: 8 }; + const valueLabelsStyling = getValueLabelsStyling(rotation === 90 || rotation === -90); const themeOverrides: PartialTheme = { markSizeRatio, @@ -105,13 +125,7 @@ export const XYSettings: FC = ({ }, }, barSeriesStyle: { - displayValue: { - fontSize, - alignment: { - horizontal: 'center', - vertical: 'middle', - }, - }, + ...valueLabelsStyling, }, axes: { axisTitle: { From 1bd408603243e80e5d8ed042505c4c87a140ffb8 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Fri, 15 Jan 2021 14:55:53 +0100 Subject: [PATCH 08/38] Move Spaces, Security and EncryptedSavedObjects plugins to separate TS projects (#88365) --- .../encrypted_saved_objects/tsconfig.json | 14 ++++++ .../common/model/authenticated_user.ts | 2 +- x-pack/plugins/security/tsconfig.json | 24 +++++++++ .../public/management/components/index.ts | 1 - .../components/secure_space_message/index.ts | 7 --- .../secure_space_message.tsx | 49 ------------------- .../enabled_features.test.tsx.snap | 20 -------- .../enabled_features.test.tsx | 8 --- .../enabled_features/enabled_features.tsx | 24 +-------- .../edit_space/manage_space_page.test.tsx | 5 -- .../edit_space/manage_space_page.tsx | 12 +---- .../public/management/management_service.tsx | 6 +-- .../spaces_grid_pages.test.tsx.snap | 3 -- .../spaces_grid/spaces_grid_page.tsx | 10 +--- .../spaces_grid/spaces_grid_pages.test.tsx | 4 -- .../management/spaces_management_app.test.tsx | 15 ++---- .../management/spaces_management_app.tsx | 7 +-- x-pack/plugins/spaces/public/plugin.tsx | 4 -- x-pack/plugins/spaces/tsconfig.json | 22 +++++++++ .../translations/translations/ja-JP.json | 7 +-- .../translations/translations/zh-CN.json | 7 +-- x-pack/test/tsconfig.json | 3 ++ x-pack/tsconfig.json | 6 +++ x-pack/tsconfig.refs.json | 3 ++ 24 files changed, 85 insertions(+), 178 deletions(-) create mode 100644 x-pack/plugins/encrypted_saved_objects/tsconfig.json create mode 100644 x-pack/plugins/security/tsconfig.json delete mode 100644 x-pack/plugins/spaces/public/management/components/secure_space_message/index.ts delete mode 100644 x-pack/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx create mode 100644 x-pack/plugins/spaces/tsconfig.json diff --git a/x-pack/plugins/encrypted_saved_objects/tsconfig.json b/x-pack/plugins/encrypted_saved_objects/tsconfig.json new file mode 100644 index 0000000000000..2b51b313d34fc --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["server/**/*"], + "references": [ + { "path": "../security/tsconfig.json" }, + ] +} diff --git a/x-pack/plugins/security/common/model/authenticated_user.ts b/x-pack/plugins/security/common/model/authenticated_user.ts index 491ceb6845e28..5513fa27fa178 100644 --- a/x-pack/plugins/security/common/model/authenticated_user.ts +++ b/x-pack/plugins/security/common/model/authenticated_user.ts @@ -8,7 +8,7 @@ import type { AuthenticationProvider, User } from '.'; const REALMS_ELIGIBLE_FOR_PASSWORD_CHANGE = ['reserved', 'native']; -interface UserRealm { +export interface UserRealm { name: string; type: string; } diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json new file mode 100644 index 0000000000000..6c3fd1851a8cb --- /dev/null +++ b/x-pack/plugins/security/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["common/**/*", "public/**/*", "server/**/*"], + "references": [ + { "path": "../features/tsconfig.json" }, + { "path": "../licensing/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" }, + { "path": "../task_manager/tsconfig.json" }, + { "path": "../../../src/plugins/data/tsconfig.json" }, + { "path": "../../../src/plugins/es_ui_shared/tsconfig.json" }, + { "path": "../../../src/plugins/home/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, + { "path": "../../../src/plugins/management/tsconfig.json" }, + { "path": "../../../src/plugins/security_oss/tsconfig.json" }, + { "path": "../../../src/plugins/usage_collection/tsconfig.json" } + ] +} diff --git a/x-pack/plugins/spaces/public/management/components/index.ts b/x-pack/plugins/spaces/public/management/components/index.ts index 7f9f80f470d12..91f4964e1da06 100644 --- a/x-pack/plugins/spaces/public/management/components/index.ts +++ b/x-pack/plugins/spaces/public/management/components/index.ts @@ -6,4 +6,3 @@ export { ConfirmDeleteModal } from './confirm_delete_modal'; export { UnauthorizedPrompt } from './unauthorized_prompt'; -export { SecureSpaceMessage } from './secure_space_message'; diff --git a/x-pack/plugins/spaces/public/management/components/secure_space_message/index.ts b/x-pack/plugins/spaces/public/management/components/secure_space_message/index.ts deleted file mode 100644 index 4526dc791a224..0000000000000 --- a/x-pack/plugins/spaces/public/management/components/secure_space_message/index.ts +++ /dev/null @@ -1,7 +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. - */ - -export { SecureSpaceMessage } from './secure_space_message'; diff --git a/x-pack/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx b/x-pack/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx deleted file mode 100644 index 9500810a395f8..0000000000000 --- a/x-pack/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx +++ /dev/null @@ -1,49 +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 { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; -import React, { Fragment } from 'react'; -import { ApplicationStart } from 'kibana/public'; - -interface SecureSpaceMessageProps { - getUrlForApp: ApplicationStart['getUrlForApp']; -} - -export const SecureSpaceMessage = (props: SecureSpaceMessageProps) => { - const rolesLinkTextAriaLabel = i18n.translate( - 'xpack.spaces.management.secureSpaceMessage.rolesLinkTextAriaLabel', - { defaultMessage: 'Roles management page' } - ); - return ( - - - -

- - - - ), - }} - /> -

-
-
- ); -}; diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap index 28aa8750beddd..c22a25ef60c31 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap @@ -59,26 +59,6 @@ exports[`EnabledFeatures renders as expected 1`] = ` values={Object {}} />

-

- - - , - } - } - /> -

diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx index aa47e439af95f..b84b1cc699d5f 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx @@ -42,7 +42,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: ['feature-1', 'feature-2'], }} - securityEnabled={true} onChange={jest.fn()} getUrlForApp={getUrlForApp} /> @@ -61,7 +60,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: ['feature-1', 'feature-2'], }} - securityEnabled={true} onChange={changeHandler} getUrlForApp={getUrlForApp} /> @@ -96,7 +94,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: [], }} - securityEnabled={true} onChange={changeHandler} getUrlForApp={getUrlForApp} /> @@ -134,7 +131,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: [], }} - securityEnabled={true} onChange={changeHandler} getUrlForApp={getUrlForApp} /> @@ -165,7 +161,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: ['feature-1', 'feature-2'], }} - securityEnabled={true} onChange={changeHandler} getUrlForApp={getUrlForApp} /> @@ -194,7 +189,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: ['feature-1'], }} - securityEnabled={true} onChange={jest.fn()} getUrlForApp={getUrlForApp} /> @@ -214,7 +208,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: [], }} - securityEnabled={true} onChange={changeHandler} getUrlForApp={getUrlForApp} /> @@ -243,7 +236,6 @@ describe('EnabledFeatures', () => { name: 'my space', disabledFeatures: [], }} - securityEnabled={true} onChange={changeHandler} getUrlForApp={getUrlForApp} /> diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx index 9429472d9e342..41cdef94fa315 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment, ReactNode } from 'react'; @@ -18,7 +18,6 @@ import { FeatureTable } from './feature_table'; interface Props { space: Partial; features: KibanaFeatureConfig[]; - securityEnabled: boolean; onChange: (space: Partial) => void; getUrlForApp: ApplicationStart['getUrlForApp']; } @@ -129,27 +128,6 @@ export class EnabledFeatures extends Component { defaultMessage="The feature is hidden in the UI, but is not disabled." />

- {this.props.securityEnabled && ( -

- - - - ), - }} - /> -

- )}
); diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx index bc26d6f132522..50e40ca75e0dc 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx @@ -58,7 +58,6 @@ describe('ManageSpacePage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={featuresStart.getFeatures} notifications={notificationServiceMock.createStartContract()} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ @@ -120,7 +119,6 @@ describe('ManageSpacePage', () => { onLoadSpace={onLoadSpace} getFeatures={featuresStart.getFeatures} notifications={notificationServiceMock.createStartContract()} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ @@ -173,7 +171,6 @@ describe('ManageSpacePage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={() => Promise.reject(error)} notifications={notifications} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ @@ -211,7 +208,6 @@ describe('ManageSpacePage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={featuresStart.getFeatures} notifications={notificationServiceMock.createStartContract()} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ @@ -273,7 +269,6 @@ describe('ManageSpacePage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={featuresStart.getFeatures} notifications={notificationServiceMock.createStartContract()} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx index 285dd6a1134ec..b954b314c8854 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx @@ -23,7 +23,7 @@ import { Space } from '../../../../../../src/plugins/spaces_oss/common'; import { KibanaFeature, FeaturesPluginStart } from '../../../../features/public'; import { isReservedSpace } from '../../../common'; import { SpacesManager } from '../../spaces_manager'; -import { SecureSpaceMessage, UnauthorizedPrompt } from '../components'; +import { UnauthorizedPrompt } from '../components'; import { toSpaceIdentifier } from '../lib'; import { SpaceValidator } from '../lib/validate_space'; import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal'; @@ -39,7 +39,6 @@ interface Props { spaceId?: string; onLoadSpace?: (space: Space) => void; capabilities: Capabilities; - securityEnabled: boolean; history: ScopedHistory; getUrlForApp: ApplicationStart['getUrlForApp']; } @@ -107,7 +106,6 @@ export class ManageSpacePage extends Component { return ( {content} - {this.maybeGetSecureSpacesMessage()} ); } @@ -157,7 +155,6 @@ export class ManageSpacePage extends Component { features={this.state.features} onChange={this.onSpaceChange} getUrlForApp={this.props.getUrlForApp} - securityEnabled={this.props.securityEnabled} /> @@ -201,13 +198,6 @@ export class ManageSpacePage extends Component { ); }; - public maybeGetSecureSpacesMessage = () => { - if (this.editingExistingSpace() && this.props.securityEnabled) { - return ; - } - return null; - }; - public getFormButtons = () => { const createSpaceText = i18n.translate( 'xpack.spaces.management.manageSpacePage.createSpaceButton', diff --git a/x-pack/plugins/spaces/public/management/management_service.tsx b/x-pack/plugins/spaces/public/management/management_service.tsx index 11853e5f1abdd..aa4cc51726a4e 100644 --- a/x-pack/plugins/spaces/public/management/management_service.tsx +++ b/x-pack/plugins/spaces/public/management/management_service.tsx @@ -6,7 +6,6 @@ import { StartServicesAccessor } from 'src/core/public'; import { ManagementSetup, ManagementApp } from '../../../../../src/plugins/management/public'; -import { SecurityLicense } from '../../../security/public'; import { SpacesManager } from '../spaces_manager'; import { PluginsStart } from '../plugin'; import { spacesManagementApp } from './spaces_management_app'; @@ -15,15 +14,14 @@ interface SetupDeps { management: ManagementSetup; getStartServices: StartServicesAccessor; spacesManager: SpacesManager; - securityLicense?: SecurityLicense; } export class ManagementService { private registeredSpacesManagementApp?: ManagementApp; - public setup({ getStartServices, management, spacesManager, securityLicense }: SetupDeps) { + public setup({ getStartServices, management, spacesManager }: SetupDeps) { this.registeredSpacesManagementApp = management.sections.section.kibana.registerApp( - spacesManagementApp.create({ getStartServices, spacesManager, securityLicense }) + spacesManagementApp.create({ getStartServices, spacesManager }) ); } diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap b/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap index 497b13642b81b..3b67cceb1c869 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap +++ b/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap @@ -125,9 +125,6 @@ exports[`SpacesGridPage renders as expected 1`] = ` tableLayout="fixed" /> - `; diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx index ae93ae80c7cd2..f75bc1d084cd8 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx @@ -27,10 +27,8 @@ import { isReservedSpace } from '../../../common'; import { DEFAULT_SPACE_ID } from '../../../common/constants'; import { SpaceAvatar } from '../../space_avatar'; import { getSpacesFeatureDescription } from '../../constants'; -import { SpacesManager } from '../..//spaces_manager'; -import { ConfirmDeleteModal } from '../components/confirm_delete_modal'; -import { SecureSpaceMessage } from '../components/secure_space_message'; -import { UnauthorizedPrompt } from '../components/unauthorized_prompt'; +import { SpacesManager } from '../../spaces_manager'; +import { ConfirmDeleteModal, UnauthorizedPrompt } from '../components'; import { getEnabledFeatures } from '../lib/feature_utils'; import { reactRouterNavigate } from '../../../../../../src/plugins/kibana_react/public'; @@ -39,7 +37,6 @@ interface Props { notifications: NotificationsStart; getFeatures: FeaturesPluginStart['getFeatures']; capabilities: Capabilities; - securityEnabled: boolean; history: ScopedHistory; getUrlForApp: ApplicationStart['getUrlForApp']; } @@ -74,9 +71,6 @@ export class SpacesGridPage extends Component { return (
{this.getPageContent()} - {this.props.securityEnabled && ( - - )} {this.getConfirmDeleteModal()}
); diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx index 606bb5cfbdcc9..945e3298f46a5 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx @@ -65,7 +65,6 @@ describe('SpacesGridPage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={featuresStart.getFeatures} notifications={notificationServiceMock.createStartContract()} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ @@ -88,7 +87,6 @@ describe('SpacesGridPage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={featuresStart.getFeatures} notifications={notificationServiceMock.createStartContract()} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ @@ -122,7 +120,6 @@ describe('SpacesGridPage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={featuresStart.getFeatures} notifications={notifications} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ @@ -157,7 +154,6 @@ describe('SpacesGridPage', () => { spacesManager={(spacesManager as unknown) as SpacesManager} getFeatures={() => Promise.reject(error)} notifications={notifications} - securityEnabled={true} getUrlForApp={getUrlForApp} history={history} capabilities={{ diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx index e345657a785c1..fc824bec6e2ce 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -20,9 +20,7 @@ jest.mock('./edit_space', () => ({ import { spacesManagementApp } from './spaces_management_app'; import { coreMock, scopedHistoryMock } from '../../../../../src/core/public/mocks'; -import { securityMock } from '../../../security/public/mocks'; import { spacesManagerMock } from '../spaces_manager/mocks'; -import { SecurityLicenseFeatures } from '../../../security/public'; import { featuresPluginMock } from '../../../features/public/mocks'; import { PluginsStart } from '../plugin'; @@ -39,18 +37,12 @@ async function mountApp(basePath: string, pathname: string, spaceId?: string) { }); } - const securityLicense = securityMock.createSetup().license; - securityLicense.getFeatures.mockReturnValue({ - showLinks: true, - } as SecurityLicenseFeatures); - const [coreStart, pluginsStart] = await coreMock.createSetup().getStartServices(); (pluginsStart as PluginsStart).features = featuresPluginMock.createStart(); const unmount = await spacesManagementApp .create({ spacesManager, - securityLicense, getStartServices: async () => [coreStart, pluginsStart as PluginsStart, {}], }) .mount({ @@ -68,7 +60,6 @@ describe('spacesManagementApp', () => { expect( spacesManagementApp.create({ spacesManager: spacesManagerMock.create(), - securityLicense: securityMock.createSetup().license, getStartServices: coreMock.createSetup().getStartServices as any, }) ).toMatchInlineSnapshot(` @@ -91,7 +82,7 @@ describe('spacesManagementApp', () => { `); @@ -114,7 +105,7 @@ describe('spacesManagementApp', () => { `); @@ -139,7 +130,7 @@ describe('spacesManagementApp', () => { `); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index a328c50af4e7a..9c2584dfcee67 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -10,7 +10,6 @@ import { Router, Route, Switch, useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { StartServicesAccessor } from 'src/core/public'; import { RedirectAppLinks } from '../../../../../src/plugins/kibana_react/public'; -import { SecurityLicense } from '../../../security/public'; import { RegisterManagementAppArgs } from '../../../../../src/plugins/management/public'; import { PluginsStart } from '../plugin'; import { SpacesManager } from '../spaces_manager'; @@ -21,12 +20,11 @@ import { Space } from '..'; interface CreateParams { getStartServices: StartServicesAccessor; spacesManager: SpacesManager; - securityLicense?: SecurityLicense; } export const spacesManagementApp = Object.freeze({ id: 'spaces', - create({ getStartServices, spacesManager, securityLicense }: CreateParams) { + create({ getStartServices, spacesManager }: CreateParams) { return { id: this.id, order: 2, @@ -58,7 +56,6 @@ export const spacesManagementApp = Object.freeze({ spacesManager={spacesManager} history={history} getUrlForApp={application.getUrlForApp} - securityEnabled={securityLicense?.getFeatures().showLinks ?? false} /> ); }; @@ -81,7 +78,6 @@ export const spacesManagementApp = Object.freeze({ spacesManager={spacesManager} history={history} getUrlForApp={application.getUrlForApp} - securityEnabled={securityLicense?.getFeatures().showLinks ?? false} /> ); }; @@ -109,7 +105,6 @@ export const spacesManagementApp = Object.freeze({ onLoadSpace={onLoadSpace} history={history} getUrlForApp={application.getUrlForApp} - securityEnabled={securityLicense?.getFeatures().showLinks ?? false} /> ); }; diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index c479c01991e31..43888c7b5758b 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -11,7 +11,6 @@ import { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_man import { ManagementStart, ManagementSetup } from 'src/plugins/management/public'; import { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; import { FeaturesPluginStart } from '../../features/public'; -import { SecurityPluginStart, SecurityPluginSetup } from '../../security/public'; import { SpacesManager } from './spaces_manager'; import { initSpacesNavControl } from './nav_control'; import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; @@ -26,14 +25,12 @@ export interface PluginsSetup { advancedSettings?: AdvancedSettingsSetup; home?: HomePublicPluginSetup; management?: ManagementSetup; - security?: SecurityPluginSetup; savedObjectsManagement?: SavedObjectsManagementPluginSetup; } export interface PluginsStart { features: FeaturesPluginStart; management?: ManagementStart; - security?: SecurityPluginStart; } export type SpacesPluginSetup = ReturnType; @@ -57,7 +54,6 @@ export class SpacesPlugin implements Plugin, spacesManager: this.spacesManager, - securityLicense: plugins.security?.license, }); } diff --git a/x-pack/plugins/spaces/tsconfig.json b/x-pack/plugins/spaces/tsconfig.json new file mode 100644 index 0000000000000..95fbecaa90936 --- /dev/null +++ b/x-pack/plugins/spaces/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["common/**/*", "public/**/*", "server/**/*"], + "references": [ + { "path": "../features/tsconfig.json" }, + { "path": "../licensing/tsconfig.json" }, + { "path": "../../../src/plugins/advanced_settings/tsconfig.json" }, + { "path": "../../../src/plugins/home/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, + { "path": "../../../src/plugins/management/tsconfig.json" }, + { "path": "../../../src/plugins/saved_objects_management/tsconfig.json" }, + { "path": "../../../src/plugins/spaces_oss/tsconfig.json" }, + { "path": "../../../src/plugins/usage_collection/tsconfig.json" } + ] +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 67c307dbf56cc..f479b3a521185 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20772,10 +20772,8 @@ "xpack.spaces.management.enabledSpaceFeatures.allFeaturesEnabledMessage": "(表示されているすべての機能)", "xpack.spaces.management.enabledSpaceFeatures.enabledFeaturesSectionMessage": "機能", "xpack.spaces.management.enabledSpaceFeatures.enableFeaturesInSpaceMessage": "このスペースの機能の表示を設定", - "xpack.spaces.management.enabledSpaceFeatures.goToRolesLink": "機能へのアクセスを保護する場合は、{manageSecurityRoles}してください。", "xpack.spaces.management.enabledSpaceFeatures.noFeaturesEnabledMessage": "(表示されている機能がありません)", "xpack.spaces.management.enabledSpaceFeatures.notASecurityMechanismMessage": "この機能は UI で非表示になっていますが、無効ではありません。", - "xpack.spaces.management.enabledSpaceFeatures.rolesLinkText": "セキュリティロールを管理", "xpack.spaces.management.enabledSpaceFeatures.someFeaturesEnabledMessage": "({featureCount} 件中 {enabledCount} 件の機能を表示中)", "xpack.spaces.management.featureAccordionSwitchLabel": "{featureCount}件中{enabledCount}件の機能を表示中", "xpack.spaces.management.featureVisibilityTitle": "機能の表示", @@ -20802,9 +20800,6 @@ "xpack.spaces.management.manageSpacePage.spaceSuccessfullySavedNotificationMessage": "スペース {name} が保存されました。", "xpack.spaces.management.manageSpacePage.updateSpaceButton": "スペースを更新", "xpack.spaces.management.reversedSpaceBadge.reversedSpacesCanBePartiallyModifiedTooltip": "リザーブされたスペースはビルトインのため、部分的な変更しかできません。", - "xpack.spaces.management.secureSpaceMessage.howToAssignRoleToSpaceDescription": "スペースへのロールの割り当てをご希望ですか?{rolesLink} に移動してください。", - "xpack.spaces.management.secureSpaceMessage.rolesLinkText": "ロール", - "xpack.spaces.management.secureSpaceMessage.rolesLinkTextAriaLabel": "ロール管理ページ", "xpack.spaces.management.selectAllFeaturesLink": "すべて選択", "xpack.spaces.management.shareToSpace.actionDescription": "この保存されたオブジェクトを1つ以上のスペースと共有します。", "xpack.spaces.management.shareToSpace.actionTitle": "スペースと共有", @@ -22782,4 +22777,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 9746c8b9ca806..a661537e6e288 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20820,10 +20820,8 @@ "xpack.spaces.management.enabledSpaceFeatures.allFeaturesEnabledMessage": "(所有可见功能)", "xpack.spaces.management.enabledSpaceFeatures.enabledFeaturesSectionMessage": "功能", "xpack.spaces.management.enabledSpaceFeatures.enableFeaturesInSpaceMessage": "为此工作区设置功能可见性", - "xpack.spaces.management.enabledSpaceFeatures.goToRolesLink": "如果您希望保护对功能的访问,请{manageSecurityRoles}。", "xpack.spaces.management.enabledSpaceFeatures.noFeaturesEnabledMessage": "(没有可见功能)", "xpack.spaces.management.enabledSpaceFeatures.notASecurityMechanismMessage": "该功能在 UI 中已隐藏,但未禁用。", - "xpack.spaces.management.enabledSpaceFeatures.rolesLinkText": "管理安全角色", "xpack.spaces.management.enabledSpaceFeatures.someFeaturesEnabledMessage": "({enabledCount} / {featureCount} 个功能可见)", "xpack.spaces.management.featureAccordionSwitchLabel": "{enabledCount} / {featureCount} 个功能可见", "xpack.spaces.management.featureVisibilityTitle": "功能可见性", @@ -20850,9 +20848,6 @@ "xpack.spaces.management.manageSpacePage.spaceSuccessfullySavedNotificationMessage": "空间 “{name}” 已保存。", "xpack.spaces.management.manageSpacePage.updateSpaceButton": "更新工作区", "xpack.spaces.management.reversedSpaceBadge.reversedSpacesCanBePartiallyModifiedTooltip": "保留的空间是内置的,只能进行部分修改。", - "xpack.spaces.management.secureSpaceMessage.howToAssignRoleToSpaceDescription": "想要为工作区分配角色?前往{rolesLink}。", - "xpack.spaces.management.secureSpaceMessage.rolesLinkText": "角色", - "xpack.spaces.management.secureSpaceMessage.rolesLinkTextAriaLabel": "角色管理页面", "xpack.spaces.management.selectAllFeaturesLink": "全选", "xpack.spaces.management.shareToSpace.actionDescription": "将此已保存对象共享到一个或多个工作区", "xpack.spaces.management.shareToSpace.actionTitle": "共享到工作区", @@ -22833,4 +22828,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index a0a94f91a1ddb..0fbb16c0e8c87 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -46,5 +46,8 @@ { "path": "../plugins/task_manager/tsconfig.json" }, { "path": "../plugins/telemetry_collection_xpack/tsconfig.json" }, { "path": "../plugins/ui_actions_enhanced/tsconfig.json" }, + { "path": "../plugins/spaces/tsconfig.json" }, + { "path": "../plugins/security/tsconfig.json" }, + { "path": "../plugins/encrypted_saved_objects/tsconfig.json" } ] } diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index dc720fced3063..01d9bcf9537ec 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -21,6 +21,9 @@ "plugins/translations/**/*", "plugins/ui_actions_enhanced/**/*", "plugins/vis_type_timeseries_enhanced/**/*", + "plugins/spaces/**/*", + "plugins/security/**/*", + "plugins/encrypted_saved_objects/**/*", "test/**/*" ], "compilerOptions": { @@ -77,5 +80,8 @@ { "path": "./plugins/ui_actions_enhanced/tsconfig.json" }, { "path": "./plugins/vis_type_timeseries_enhanced/tsconfig.json" }, { "path": "./plugins/translations/tsconfig.json" }, + { "path": "./plugins/spaces/tsconfig.json" }, + { "path": "./plugins/security/tsconfig.json" }, + { "path": "./plugins/encrypted_saved_objects/tsconfig.json" } ] } diff --git a/x-pack/tsconfig.refs.json b/x-pack/tsconfig.refs.json index 08c81d5f3b802..4352d2993002a 100644 --- a/x-pack/tsconfig.refs.json +++ b/x-pack/tsconfig.refs.json @@ -17,5 +17,8 @@ { "path": "./plugins/ui_actions_enhanced/tsconfig.json" }, { "path": "./plugins/vis_type_timeseries_enhanced/tsconfig.json" }, { "path": "./plugins/translations/tsconfig.json" }, + { "path": "./plugins/spaces/tsconfig.json" }, + { "path": "./plugins/security/tsconfig.json" }, + { "path": "./plugins/encrypted_saved_objects/tsconfig.json" } ] } From 11d4457d9714df83d0029d55c87bffb3b339add3 Mon Sep 17 00:00:00 2001 From: John Schulz Date: Fri, 15 Jan 2021 09:33:19 -0500 Subject: [PATCH 09/38] Don't retry if icon isn't in initial response (#88403) --- .../create_package_policy_page/step_select_package.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_package.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_package.tsx index 3bcafaecbf8d9..ef8dba4447c31 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_package.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_package.tsx @@ -141,15 +141,7 @@ export const StepSelectPackage: React.FunctionComponent<{ return { label: title || name, key: pkgkey, - prepend: ( - - ), + prepend: , checked: selectedPkgKey === pkgkey ? 'on' : undefined, }; })} From 31a481a9dda7e2b9028b9aad9e8b4e71804832cd Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Fri, 15 Jan 2021 08:44:28 -0600 Subject: [PATCH 10/38] Remove dependency between alerts and infra (#88342) The alerts plugin was importing `JsonObject` from the infra plugin. The infra plugin imported `JsonObject`, `JsonValue`, and `JsonArray` from kibanaUtils and then re-exported them. Remove the re-export from the infra plugin and instead always import these types from kibanaUtils. --- x-pack/plugins/alerts/common/alert_navigation.ts | 3 ++- x-pack/plugins/alerts/kibana.json | 10 +++++++++- .../alerts/public/alert_navigation_registry/types.ts | 2 +- x-pack/plugins/infra/common/typed_json.ts | 2 -- .../infra/public/components/log_stream/index.tsx | 2 +- .../components/logging/log_text_stream/field_value.tsx | 2 +- .../logging/log_text_stream/log_entry_field_column.tsx | 2 +- .../public/utils/log_column_render_configuration.tsx | 2 +- .../server/lib/adapters/framework/adapter_types.ts | 2 +- .../adapters/log_entries/kibana_log_entries_adapter.ts | 2 +- .../domains/log_entries_domain/log_entries_domain.ts | 2 +- .../server/lib/domains/log_entries_domain/message.ts | 2 +- .../lib/domains/log_entries_domain/rule_types.ts | 2 +- .../routes/snapshot/lib/get_metrics_aggregations.ts | 2 +- x-pack/plugins/infra/server/utils/serialized_query.ts | 2 +- .../infra/server/utils/typed_search_strategy.ts | 3 ++- x-pack/plugins/infra/types/eui.d.ts | 3 --- .../plugins/security_solution/server/endpoint/types.ts | 2 +- 18 files changed, 26 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/alerts/common/alert_navigation.ts b/x-pack/plugins/alerts/common/alert_navigation.ts index 188764069e84f..b04278b6517c8 100644 --- a/x-pack/plugins/alerts/common/alert_navigation.ts +++ b/x-pack/plugins/alerts/common/alert_navigation.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { JsonObject } from '../../infra/common/typed_json'; +import { JsonObject } from '../../../../src/plugins/kibana_utils/common'; + export interface AlertUrlNavigation { path: string; } diff --git a/x-pack/plugins/alerts/kibana.json b/x-pack/plugins/alerts/kibana.json index c0ab242831428..a2cb237467f11 100644 --- a/x-pack/plugins/alerts/kibana.json +++ b/x-pack/plugins/alerts/kibana.json @@ -5,7 +5,15 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "alerts"], - "requiredPlugins": ["licensing", "taskManager", "encryptedSavedObjects", "actions", "eventLog", "features"], + "requiredPlugins": [ + "actions", + "encryptedSavedObjects", + "eventLog", + "features", + "kibanaUtils", + "licensing", + "taskManager" + ], "optionalPlugins": ["usageCollection", "spaces", "security"], "extraPublicDirs": ["common", "common/parse_duration"] } diff --git a/x-pack/plugins/alerts/public/alert_navigation_registry/types.ts b/x-pack/plugins/alerts/public/alert_navigation_registry/types.ts index 0038652f47f12..e0ab2dcf405f3 100644 --- a/x-pack/plugins/alerts/public/alert_navigation_registry/types.ts +++ b/x-pack/plugins/alerts/public/alert_navigation_registry/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { JsonObject } from '../../../infra/common/typed_json'; +import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; import { AlertType, SanitizedAlert } from '../../common'; export type AlertNavigationHandler = ( diff --git a/x-pack/plugins/infra/common/typed_json.ts b/x-pack/plugins/infra/common/typed_json.ts index 0ff9e42942ef2..f3e7608910e09 100644 --- a/x-pack/plugins/infra/common/typed_json.ts +++ b/x-pack/plugins/infra/common/typed_json.ts @@ -20,5 +20,3 @@ export const jsonArrayRT: rt.Type = rt.recursion('JsonArray', () => export const jsonObjectRT: rt.Type = rt.recursion('JsonObject', () => rt.record(rt.string, jsonValueRT) ); - -export { JsonValue, JsonArray, JsonObject }; diff --git a/x-pack/plugins/infra/public/components/log_stream/index.tsx b/x-pack/plugins/infra/public/components/log_stream/index.tsx index 997f81583fb64..3d69b6a022987 100644 --- a/x-pack/plugins/infra/public/components/log_stream/index.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/index.tsx @@ -16,7 +16,7 @@ import { useLogStream } from '../../containers/logs/log_stream'; import { ScrollableLogTextStreamView } from '../logging/log_text_stream'; import { LogColumnRenderConfiguration } from '../../utils/log_column_render_configuration'; -import { JsonValue } from '../../../common/typed_json'; +import { JsonValue } from '../../../../../../src/plugins/kibana_utils/common'; const PAGE_THRESHOLD = 2; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx index 4047e80846ca6..b13e3569c1e5b 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx @@ -7,7 +7,7 @@ import stringify from 'json-stable-stringify'; import React from 'react'; import { euiStyled } from '../../../../../observability/public'; -import { JsonArray, JsonValue } from '../../../../common/typed_json'; +import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting'; export const FieldValue: React.FC<{ diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx index a8165463d7ee6..fe5e7f305f60c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { JsonValue } from '../../../../common/typed_json'; +import { JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; import { euiStyled } from '../../../../../observability/public'; import { LogColumn } from '../../../../common/http_api'; import { isFieldColumn, isHighlightFieldColumn } from '../../../utils/log_entry'; diff --git a/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx b/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx index 7ffbfcace88ce..95ebe37b64c97 100644 --- a/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx +++ b/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx @@ -5,7 +5,7 @@ */ import { ReactNode } from 'react'; -import { JsonValue } from '../../common/typed_json'; +import { JsonValue } from '../../../../../src/plugins/kibana_utils/common'; /** * Interface for common configuration properties, regardless of the column type. diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 93a7bc9a0830b..705d7bf34c1c6 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -19,7 +19,7 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../pl import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server'; import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerts/server'; import { MlPluginSetup } from '../../../../../ml/server'; -import { JsonArray, JsonValue } from '../../../../common/typed_json'; +import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; export interface InfraServerPluginSetupDeps { data: DataPluginSetup; diff --git a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts index 4637f3ab41782..98c42ab7d98ab 100644 --- a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts @@ -11,7 +11,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import * as runtimeTypes from 'io-ts'; import { compact } from 'lodash'; import { RequestHandlerContext } from 'src/core/server'; -import { JsonArray } from '../../../../common/typed_json'; +import { JsonArray } from '../../../../../../../src/plugins/kibana_utils/common'; import { LogEntriesAdapter, LogEntriesParams, diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index 52cf6f46716b3..d9f125908b32d 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -5,7 +5,7 @@ */ import { RequestHandlerContext } from 'src/core/server'; -import { JsonObject } from '../../../../common/typed_json'; +import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/common'; import { LogEntriesSummaryBucket, LogEntriesSummaryHighlightsBucket, diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/message.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/message.ts index f9775e127088a..19ab82c9c5ac1 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/message.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/message.ts @@ -5,7 +5,7 @@ */ import { LogMessagePart } from '../../../../common/http_api/log_entries'; -import { JsonArray, JsonValue } from '../../../../common/typed_json'; +import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; import { LogMessageFormattingCondition, LogMessageFormattingFieldValueConditionValue, diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts index 4569f4b8e8a91..e2368d7475d48 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { JsonValue } from '../../../../common/typed_json'; +import { JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; export interface LogMessageFormattingRule { when: LogMessageFormattingCondition; diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts index 2421469eb1bdd..578ebaffc8369 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { JsonObject } from '../../../../common/typed_json'; +import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/common'; import { InventoryItemType, MetricsUIAggregation, diff --git a/x-pack/plugins/infra/server/utils/serialized_query.ts b/x-pack/plugins/infra/server/utils/serialized_query.ts index 932df847e65d0..8dfe00fcd380e 100644 --- a/x-pack/plugins/infra/server/utils/serialized_query.ts +++ b/x-pack/plugins/infra/server/utils/serialized_query.ts @@ -6,7 +6,7 @@ import { UserInputError } from 'apollo-server-errors'; -import { JsonObject } from '../../common/typed_json'; +import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; export const parseFilterQuery = ( filterQuery: string | null | undefined diff --git a/x-pack/plugins/infra/server/utils/typed_search_strategy.ts b/x-pack/plugins/infra/server/utils/typed_search_strategy.ts index 1234aea507f3f..b7132fa5dc3aa 100644 --- a/x-pack/plugins/infra/server/utils/typed_search_strategy.ts +++ b/x-pack/plugins/infra/server/utils/typed_search_strategy.ts @@ -6,7 +6,8 @@ import * as rt from 'io-ts'; import stringify from 'json-stable-stringify'; -import { JsonValue, jsonValueRT } from '../../common/typed_json'; +import { JsonValue } from '../../../../../src/plugins/kibana_utils/common'; +import { jsonValueRT } from '../../common/typed_json'; import { SearchStrategyError } from '../../common/search_strategies/common/errors'; import { ShardFailure } from './elasticsearch_runtime_types'; diff --git a/x-pack/plugins/infra/types/eui.d.ts b/x-pack/plugins/infra/types/eui.d.ts index 802e11dd8fc84..e3009820d1624 100644 --- a/x-pack/plugins/infra/types/eui.d.ts +++ b/x-pack/plugins/infra/types/eui.d.ts @@ -11,9 +11,6 @@ import { IconType, ToolTipPositions } from '@elastic/eui'; import { CommonProps } from '@elastic/eui/src/components/common'; -import moment from 'moment'; -import { MouseEventHandler, ReactType, Ref } from 'react'; -import { JsonObject } from '../common/typed_json'; declare module '@elastic/eui' { interface EuiFormControlLayoutIconProps { diff --git a/x-pack/plugins/security_solution/server/endpoint/types.ts b/x-pack/plugins/security_solution/server/endpoint/types.ts index 2328c86f78a35..0e4f03c56b9f0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/types.ts +++ b/x-pack/plugins/security_solution/server/endpoint/types.ts @@ -7,7 +7,7 @@ import { LoggerFactory } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; import { ConfigType } from '../config'; import { EndpointAppContextService } from './endpoint_app_context_services'; -import { JsonObject } from '../../../infra/common/typed_json'; +import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; import { HostMetadata, HostMetadataDetails, From 9b22789c3ce570a90f84c34d786c66f24b080b89 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Fri, 15 Jan 2021 14:47:35 +0000 Subject: [PATCH 11/38] [Discover] Use fields API to retrieve fields (#83891) * Add search source to example plugin. * Add uiSetting for fields API. * Update SearchSource to support fields API. * [PoC] reading from the fields API in Discover * Add N fields as a default column * Make fields column non-removeable * Do not add 'fields' to state * Remove fields from app state and read from source when needed * Remove fields column if a new column is added * Add search source to example plugin. * Add uiSetting for fields API. * Update SearchSource to support fields API. * Improve error handling in search examples plugin. * Add unit tests for legacy behavior. * Remove uiSettings feature flag; add fieldsFromSource config. * Rewrite flatten() based on final API design. * Update example app based on final API design. * Update maps app to use legacy fieldsFromSource. * Update Discover to use legacy fieldsFromSource. * Rename source filters to field filters. * Address feedback. * Update generated docs. * Update maps functional test. * Formatting fields column similar to _source * Moving logic for using search API to updating search source * Fix small merge error * Move useSource switch to Discover section of advanced settings * Do not use fields and source at the same time * Remove unmapped fields switch * Add basic support for grouping multifields * Remove output.txt * Fix some merge leftovers * Fix some merge leftovers * Fix merge errors * Fix typescript errors and update nested fields logic * Add a unit test * Fixing field formats * Fix multifield selection logic * Request all fields from source * Fix eslint * Fix default columns when switching between _source and fields * More unit tests * Update API changes * Add unit test for discover field details footer * Remove unused file * Remove fields formatting from index pattern * Remove unnecessary check * Addressing design comments * Fixing fields column display and renaming it to Document * Adding more unit tests * Adding a missing check for useNewFieldsAPI; minor fixes * Fixing typescript error * Remove unnecessary console statement * Add missing prop * Fixing import order * Adding functional test to test fields API * [Functional test] Clean up in after * Fixing context app * Addressing PR comments * Updating failed snapshot * Addressing PR comments * Fixing i18n translations, updating type * Addressing PR comments * Updating a functional test * Add a separate functional test for fields API * Read fields from source in a functional test * Skip buggy test * Use default behavior in functional tests * Fixing remaining failing tests * Fixing date-nanos test * Updating FLS test * Fixing yet another functional test * Skipping non-relevant tests * Fixing more tests * Update stub import in test * Fix import * Fix invalid import Co-authored-by: Luke Elmers Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/discover/common/index.ts | 1 + .../stubbed_saved_object_index_pattern.ts | 2 +- .../application/angular/context/api/_stubs.js | 1 + .../application/angular/context/api/anchor.js | 7 +- .../angular/context/api/anchor.test.js | 25 + .../context/api/context.predecessors.test.js | 77 ++ .../context/api/context.successors.test.js | 77 ++ .../angular/context/api/context.ts | 15 +- .../api/utils/get_es_query_search_after.ts | 20 +- .../angular/context/query/actions.js | 12 +- .../application/angular/context_app.html | 1 + .../public/application/angular/context_app.js | 13 +- .../public/application/angular/discover.js | 46 +- .../application/angular/discover_legacy.html | 1 + .../angular/doc_table/actions/columns.ts | 16 +- .../components/table_header/helpers.tsx | 39 +- .../angular/doc_table/components/table_row.ts | 30 +- .../doc_table/components/table_row/_cell.scss | 6 +- .../components/table_row/details.html | 2 +- .../doc_table/create_doc_table_react.tsx | 2 + .../angular/doc_table/doc_table.html | 2 + .../angular/doc_table/doc_table.ts | 1 + .../application/angular/helpers/index.ts | 1 + .../angular/helpers/row_formatter.test.ts | 69 ++ .../angular/helpers/row_formatter.ts | 47 ++ .../context_app/context_app_legacy.tsx | 12 +- .../context_app_legacy_directive.ts | 1 + .../create_discover_legacy_directive.ts | 1 + .../components/discover_legacy.tsx | 17 +- ...iscover_field_details_footer.test.tsx.snap | 704 ++++++++++++++++++ .../components/sidebar/discover_field.scss | 10 +- .../sidebar/discover_field.test.tsx | 2 +- .../components/sidebar/discover_field.tsx | 217 ++++-- .../sidebar/discover_field_details.test.tsx | 2 +- .../sidebar/discover_field_details.tsx | 33 +- .../discover_field_details_footer.test.tsx | 82 ++ .../sidebar/discover_field_details_footer.tsx | 70 ++ .../components/sidebar/discover_sidebar.tsx | 41 +- .../sidebar/discover_sidebar_responsive.tsx | 4 + .../sidebar/lib/group_fields.test.ts | 83 ++- .../components/sidebar/lib/group_fields.tsx | 12 +- .../application/components/sidebar/types.ts | 2 +- .../get_switch_index_pattern_app_state.ts | 7 +- .../helpers/persist_saved_search.ts | 2 + .../helpers/update_search_source.test.ts | 29 + .../helpers/update_search_source.ts | 12 + src/plugins/discover/server/ui_settings.ts | 8 + .../context/_date_nanos_custom_timestamp.js | 1 + .../discover/_data_grid_doc_navigation.ts | 3 +- .../apps/discover/_data_grid_field_data.ts | 2 +- .../apps/discover/_discover_fields_api.ts | 71 ++ .../apps/discover/_doc_navigation.ts | 3 +- test/functional/apps/discover/_field_data.ts | 1 + .../discover/_field_data_with_fields_api.ts | 105 +++ .../functional/apps/discover/_large_string.ts | 3 +- .../functional/apps/discover/_shared_links.ts | 2 +- .../apps/discover/_source_filters.ts | 4 + .../apps/discover/ftr_provider_context.d.ts | 23 + test/functional/apps/discover/index.ts | 2 + .../es_archiver/date_nanos/mappings.json | 3 +- .../date_nanos_mixed/mappings.json | 3 +- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../apps/security/doc_level_security_roles.js | 2 +- .../apps/security/field_level_security.js | 4 +- .../security/flstest/data/mappings.json | 6 +- 66 files changed, 1920 insertions(+), 186 deletions(-) rename src/{fixtures => plugins/discover/public/__mocks__}/stubbed_saved_object_index_pattern.ts (94%) create mode 100644 src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts create mode 100644 src/plugins/discover/public/application/angular/helpers/row_formatter.ts create mode 100644 src/plugins/discover/public/application/components/sidebar/__snapshots__/discover_field_details_footer.test.tsx.snap create mode 100644 src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx create mode 100644 src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.tsx create mode 100644 test/functional/apps/discover/_discover_fields_api.ts create mode 100644 test/functional/apps/discover/_field_data_with_fields_api.ts create mode 100644 test/functional/apps/discover/ftr_provider_context.d.ts diff --git a/src/plugins/discover/common/index.ts b/src/plugins/discover/common/index.ts index 321a102e8d782..b721c9157fe16 100644 --- a/src/plugins/discover/common/index.ts +++ b/src/plugins/discover/common/index.ts @@ -29,3 +29,4 @@ export const CONTEXT_STEP_SETTING = 'context:step'; export const CONTEXT_TIE_BREAKER_FIELDS_SETTING = 'context:tieBreakerFields'; export const DOC_TABLE_LEGACY = 'doc_table:legacy'; export const MODIFY_COLUMNS_ON_SWITCH = 'discover:modifyColumnsOnSwitch'; +export const SEARCH_FIELDS_FROM_SOURCE = 'discover:searchFieldsFromSource'; diff --git a/src/fixtures/stubbed_saved_object_index_pattern.ts b/src/plugins/discover/public/__mocks__/stubbed_saved_object_index_pattern.ts similarity index 94% rename from src/fixtures/stubbed_saved_object_index_pattern.ts rename to src/plugins/discover/public/__mocks__/stubbed_saved_object_index_pattern.ts index 261e451db5452..a85734edba274 100644 --- a/src/fixtures/stubbed_saved_object_index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/stubbed_saved_object_index_pattern.ts @@ -18,7 +18,7 @@ */ // @ts-expect-error -import stubbedLogstashFields from './logstash_fields'; +import stubbedLogstashFields from '../../../../fixtures/logstash_fields'; const mockLogstashFields = stubbedLogstashFields(); diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.js b/src/plugins/discover/public/application/angular/context/api/_stubs.js index d82189db60935..17d45756af148 100644 --- a/src/plugins/discover/public/application/angular/context/api/_stubs.js +++ b/src/plugins/discover/public/application/angular/context/api/_stubs.js @@ -47,6 +47,7 @@ export function createSearchSourceStub(hits, timeField) { searchSourceStub.setParent = sinon.spy(() => searchSourceStub); searchSourceStub.setField = sinon.spy(() => searchSourceStub); + searchSourceStub.removeField = sinon.spy(() => searchSourceStub); searchSourceStub.getField = sinon.spy((key) => { const previousSetCall = searchSourceStub.setField.withArgs(key).lastCall; diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.js b/src/plugins/discover/public/application/angular/context/api/anchor.js index 4df5ba989f798..31c106b95cbe5 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.js +++ b/src/plugins/discover/public/application/angular/context/api/anchor.js @@ -20,7 +20,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -export function fetchAnchorProvider(indexPatterns, searchSource) { +export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi = false) { return async function fetchAnchor(indexPatternId, anchorId, sort) { const indexPattern = await indexPatterns.get(indexPatternId); searchSource @@ -41,7 +41,10 @@ export function fetchAnchorProvider(indexPatterns, searchSource) { language: 'lucene', }) .setField('sort', sort); - + if (useNewFieldsApi) { + searchSource.removeField('fieldsFromSource'); + searchSource.setField('fields', ['*']); + } const response = await searchSource.fetch(); if (_.get(response, ['hits', 'total'], 0) < 1) { diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.test.js b/src/plugins/discover/public/application/angular/context/api/anchor.test.js index 993aefc4f59e3..d54b38c466a5c 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.test.js +++ b/src/plugins/discover/public/application/angular/context/api/anchor.test.js @@ -144,4 +144,29 @@ describe('context app', function () { }); }); }); + + describe('useNewFields API', () => { + let fetchAnchor; + let searchSourceStub; + + beforeEach(() => { + searchSourceStub = createSearchSourceStub([{ _id: 'hit1' }]); + fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub, true); + }); + + it('should request fields if useNewFieldsApi set', function () { + searchSourceStub._stubHits = [{ property1: 'value1' }, { property2: 'value2' }]; + + return fetchAnchor('INDEX_PATTERN_ID', 'id', [ + { '@timestamp': 'desc' }, + { _doc: 'desc' }, + ]).then(() => { + const setFieldsSpy = searchSourceStub.setField.withArgs('fields'); + const removeFieldsSpy = searchSourceStub.removeField.withArgs('fieldsFromSource'); + expect(setFieldsSpy.calledOnce).toBe(true); + expect(removeFieldsSpy.calledOnce).toBe(true); + expect(setFieldsSpy.firstCall.args[1]).toEqual(['*']); + }); + }); + }); }); diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js index 4c0515906a494..ea181782470fa 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js @@ -227,4 +227,81 @@ describe('context app', function () { }); }); }); + + describe('function fetchPredecessors with useNewFieldsApi set', function () { + let fetchPredecessors; + let mockSearchSource; + + beforeEach(() => { + mockSearchSource = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); + + setServices({ + data: { + search: { + searchSource: { + create: jest.fn().mockImplementation(() => mockSearchSource), + }, + }, + }, + }); + + fetchPredecessors = ( + indexPatternId, + timeField, + sortDir, + timeValIso, + timeValNr, + tieBreakerField, + tieBreakerValue, + size + ) => { + const anchor = { + _source: { + [timeField]: timeValIso, + }, + sort: [timeValNr, tieBreakerValue], + }; + + return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs( + 'predecessors', + indexPatternId, + anchor, + timeField, + tieBreakerField, + sortDir, + size, + [] + ); + }; + }); + + it('should perform exactly one query when enough hits are returned', function () { + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 3000 + 2), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 + 1), + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 2000), + mockSearchSource._createStubHit(MS_PER_DAY * 1000), + ]; + + return fetchPredecessors( + 'INDEX_PATTERN_ID', + '@timestamp', + 'desc', + ANCHOR_TIMESTAMP_3000, + MS_PER_DAY * 3000, + '_doc', + 0, + 3, + [] + ).then((hits) => { + const setFieldsSpy = mockSearchSource.setField.withArgs('fields'); + const removeFieldsSpy = mockSearchSource.removeField.withArgs('fieldsFromSource'); + expect(mockSearchSource.fetch.calledOnce).toBe(true); + expect(removeFieldsSpy.calledOnce).toBe(true); + expect(setFieldsSpy.calledOnce).toBe(true); + expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); + }); + }); + }); }); diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js index 285d39cd4d8a4..2c54de946c8d4 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js @@ -231,4 +231,81 @@ describe('context app', function () { }); }); }); + + describe('function fetchSuccessors with useNewFieldsApi set', function () { + let fetchSuccessors; + let mockSearchSource; + + beforeEach(() => { + mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + + setServices({ + data: { + search: { + searchSource: { + create: jest.fn().mockImplementation(() => mockSearchSource), + }, + }, + }, + }); + + fetchSuccessors = ( + indexPatternId, + timeField, + sortDir, + timeValIso, + timeValNr, + tieBreakerField, + tieBreakerValue, + size + ) => { + const anchor = { + _source: { + [timeField]: timeValIso, + }, + sort: [timeValNr, tieBreakerValue], + }; + + return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs( + 'successors', + indexPatternId, + anchor, + timeField, + tieBreakerField, + sortDir, + size, + [] + ); + }; + }); + + it('should perform exactly one query when enough hits are returned', function () { + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 5000), + mockSearchSource._createStubHit(MS_PER_DAY * 4000), + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 1), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 2), + ]; + + return fetchSuccessors( + 'INDEX_PATTERN_ID', + '@timestamp', + 'desc', + ANCHOR_TIMESTAMP_3000, + MS_PER_DAY * 3000, + '_doc', + 0, + 3, + [] + ).then((hits) => { + expect(mockSearchSource.fetch.calledOnce).toBe(true); + expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); + const setFieldsSpy = mockSearchSource.setField.withArgs('fields'); + const removeFieldsSpy = mockSearchSource.removeField.withArgs('fieldsFromSource'); + expect(removeFieldsSpy.calledOnce).toBe(true); + expect(setFieldsSpy.calledOnce).toBe(true); + }); + }); + }); }); diff --git a/src/plugins/discover/public/application/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts index ba8cffd1d7558..903e4e0f1b485 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.ts @@ -40,7 +40,7 @@ const DAY_MILLIS = 24 * 60 * 60 * 1000; // look from 1 day up to 10000 days into the past and future const LOOKUP_OFFSETS = [0, 1, 7, 30, 365, 10000].map((days) => days * DAY_MILLIS); -function fetchContextProvider(indexPatterns: IndexPatternsContract) { +function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFieldsApi?: boolean) { return { fetchSurroundingDocs, }; @@ -89,7 +89,14 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) { break; } - const searchAfter = getEsQuerySearchAfter(type, documents, timeField, anchor, nanos); + const searchAfter = getEsQuerySearchAfter( + type, + documents, + timeField, + anchor, + nanos, + useNewFieldsApi + ); const sort = getEsQuerySort(timeField, tieBreakerField, sortDirToApply); @@ -116,6 +123,10 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) { const { data } = getServices(); const searchSource = await data.search.searchSource.create(); + if (useNewFieldsApi) { + searchSource.removeField('fieldsFromSource'); + searchSource.setField('fields', ['*']); + } return searchSource .setParent(undefined) .setField('index', indexPattern) diff --git a/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts b/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts index 24ac19a7e3bc3..348a0c04a84ad 100644 --- a/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts @@ -31,16 +31,30 @@ export function getEsQuerySearchAfter( documents: EsHitRecordList, timeFieldName: string, anchor: EsHitRecord, - nanoSeconds: string + nanoSeconds: string, + useNewFieldsApi?: boolean ): EsQuerySearchAfter { if (documents.length) { // already surrounding docs -> first or last record is used const afterTimeRecIdx = type === 'successors' && documents.length ? documents.length - 1 : 0; const afterTimeDoc = documents[afterTimeRecIdx]; - const afterTimeValue = nanoSeconds ? afterTimeDoc._source[timeFieldName] : afterTimeDoc.sort[0]; + let afterTimeValue = afterTimeDoc.sort[0]; + if (nanoSeconds) { + afterTimeValue = useNewFieldsApi + ? afterTimeDoc.fields[timeFieldName][0] + : afterTimeDoc._source[timeFieldName]; + } return [afterTimeValue, afterTimeDoc.sort[1]]; } // if data_nanos adapt timestamp value for sorting, since numeric value was rounded by browser // ES search_after also works when number is provided as string - return [nanoSeconds ? anchor._source[timeFieldName] : anchor.sort[0], anchor.sort[1]]; + const searchAfter = new Array(2) as EsQuerySearchAfter; + searchAfter[0] = anchor.sort[0]; + if (nanoSeconds) { + searchAfter[0] = useNewFieldsApi + ? anchor.fields[timeFieldName][0] + : anchor._source[timeFieldName]; + } + searchAfter[1] = anchor.sort[1]; + return searchAfter; } diff --git a/src/plugins/discover/public/application/angular/context/query/actions.js b/src/plugins/discover/public/application/angular/context/query/actions.js index d5c72d34006e2..42638cd90a1bb 100644 --- a/src/plugins/discover/public/application/angular/context/query/actions.js +++ b/src/plugins/discover/public/application/angular/context/query/actions.js @@ -27,11 +27,17 @@ import { fetchContextProvider } from '../api/context'; import { getQueryParameterActions } from '../query_parameters'; import { FAILURE_REASONS, LOADING_STATUS } from './index'; import { MarkdownSimple } from '../../../../../../kibana_react/public'; +import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common'; export function QueryActionsProvider(Promise) { - const { filterManager, indexPatterns, data } = getServices(); - const fetchAnchor = fetchAnchorProvider(indexPatterns, data.search.searchSource.createEmpty()); - const { fetchSurroundingDocs } = fetchContextProvider(indexPatterns); + const { filterManager, indexPatterns, data, uiSettings } = getServices(); + const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); + const fetchAnchor = fetchAnchorProvider( + indexPatterns, + data.search.searchSource.createEmpty(), + useNewFieldsApi + ); + const { fetchSurroundingDocs } = fetchContextProvider(indexPatterns, useNewFieldsApi); const { setPredecessorCount, setQueryParameters, setSuccessorCount } = getQueryParameterActions( filterManager, indexPatterns diff --git a/src/plugins/discover/public/application/angular/context_app.html b/src/plugins/discover/public/application/angular/context_app.html index 8dc3e5c87e504..3d731459ad8d7 100644 --- a/src/plugins/discover/public/application/angular/context_app.html +++ b/src/plugins/discover/public/application/angular/context_app.html @@ -17,5 +17,6 @@ successor-available="contextApp.state.rows.successors.length" successor-status="contextApp.state.loadingStatus.successors.status" on-change-successor-count="contextApp.actions.fetchGivenSuccessorRows" + use-new-fields-api="contextApp.state.useNewFieldsApi" top-nav-menu="contextApp.topNavMenu" > diff --git a/src/plugins/discover/public/application/angular/context_app.js b/src/plugins/discover/public/application/angular/context_app.js index d9e2452eb8bd6..f18389df6d12d 100644 --- a/src/plugins/discover/public/application/angular/context_app.js +++ b/src/plugins/discover/public/application/angular/context_app.js @@ -18,7 +18,11 @@ */ import _ from 'lodash'; -import { CONTEXT_STEP_SETTING, CONTEXT_TIE_BREAKER_FIELDS_SETTING } from '../../../common'; +import { + CONTEXT_STEP_SETTING, + CONTEXT_TIE_BREAKER_FIELDS_SETTING, + SEARCH_FIELDS_FROM_SOURCE, +} from '../../../common'; import { getAngularModule, getServices } from '../../kibana_services'; import contextAppTemplate from './context_app.html'; import './context/components/action_bar'; @@ -59,9 +63,11 @@ function ContextAppController($scope, Private) { const { filterManager, indexPatterns, uiSettings, navigation } = getServices(); const queryParameterActions = getQueryParameterActions(filterManager, indexPatterns); const queryActions = Private(QueryActionsProvider); + const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); this.state = createInitialState( parseInt(uiSettings.get(CONTEXT_STEP_SETTING), 10), - getFirstSortableField(this.indexPattern, uiSettings.get(CONTEXT_TIE_BREAKER_FIELDS_SETTING)) + getFirstSortableField(this.indexPattern, uiSettings.get(CONTEXT_TIE_BREAKER_FIELDS_SETTING)), + useNewFieldsApi ); this.topNavMenu = navigation.ui.TopNavMenu; @@ -127,7 +133,7 @@ function ContextAppController($scope, Private) { ); } -function createInitialState(defaultStepSize, tieBreakerField) { +function createInitialState(defaultStepSize, tieBreakerField, useNewFieldsApi) { return { queryParameters: createInitialQueryParametersState(defaultStepSize, tieBreakerField), rows: { @@ -137,5 +143,6 @@ function createInitialState(defaultStepSize, tieBreakerField) { successors: [], }, loadingStatus: createInitialLoadingStatusState(), + useNewFieldsApi, }; } diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index de3b7c6c1a326..6b552d92df0f0 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -64,6 +64,7 @@ import { DEFAULT_COLUMNS_SETTING, MODIFY_COLUMNS_ON_SWITCH, SAMPLE_SIZE_SETTING, + SEARCH_FIELDS_FROM_SOURCE, SEARCH_ON_PAGE_LOAD_SETTING, SORT_DEFAULT_ORDER_SETTING, } from '../../../common'; @@ -197,6 +198,8 @@ function discoverController($element, $route, $scope, $timeout, Promise) { $scope.searchSource, toastNotifications ); + $scope.useNewFieldsApi = !config.get(SEARCH_FIELDS_FROM_SOURCE); + //used for functional testing $scope.fetchCounter = 0; @@ -308,7 +311,8 @@ function discoverController($element, $route, $scope, $timeout, Promise) { nextIndexPattern, $scope.state.columns, $scope.state.sort, - config.get(MODIFY_COLUMNS_ON_SWITCH) + config.get(MODIFY_COLUMNS_ON_SWITCH), + $scope.useNewFieldsApi ); await setAppState(nextAppState); } @@ -415,19 +419,33 @@ function discoverController($element, $route, $scope, $timeout, Promise) { setBreadcrumbsTitle(savedSearch, chrome); + function removeSourceFromColumns(columns) { + return columns.filter((col) => col !== '_source'); + } + + function getDefaultColumns() { + const columns = [...savedSearch.columns]; + + if ($scope.useNewFieldsApi) { + return removeSourceFromColumns(columns); + } + if (columns.length > 0) { + return columns; + } + return [...config.get(DEFAULT_COLUMNS_SETTING)]; + } + function getStateDefaults() { const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery(); const sort = getSortArray(savedSearch.sort, $scope.indexPattern); + const columns = getDefaultColumns(); const defaultState = { query, sort: !sort.length ? getDefaultSort($scope.indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING, 'desc')) : sort, - columns: - savedSearch.columns.length > 0 - ? savedSearch.columns - : config.get(DEFAULT_COLUMNS_SETTING).slice(), + columns, index: $scope.indexPattern.id, interval: 'auto', filters: _.cloneDeep($scope.searchSource.getOwnField('filter')), @@ -739,10 +757,14 @@ function discoverController($element, $route, $scope, $timeout, Promise) { }; $scope.updateDataSource = () => { - updateSearchSource($scope.searchSource, { - indexPattern: $scope.indexPattern, + const { indexPattern, searchSource, useNewFieldsApi } = $scope; + const { columns, sort } = $scope.state; + updateSearchSource(searchSource, { + indexPattern, services, - sort: $scope.state.sort, + sort, + columns, + useNewFieldsApi, }); return Promise.resolve(); }; @@ -770,20 +792,20 @@ function discoverController($element, $route, $scope, $timeout, Promise) { }; $scope.addColumn = function addColumn(columnName) { + const { indexPattern, useNewFieldsApi } = $scope; if (capabilities.discover.save) { - const { indexPattern } = $scope; popularizeField(indexPattern, columnName, indexPatterns); } - const columns = columnActions.addColumn($scope.state.columns, columnName); + const columns = columnActions.addColumn($scope.state.columns, columnName, useNewFieldsApi); setAppState({ columns }); }; $scope.removeColumn = function removeColumn(columnName) { + const { indexPattern, useNewFieldsApi } = $scope; if (capabilities.discover.save) { - const { indexPattern } = $scope; popularizeField(indexPattern, columnName, indexPatterns); } - const columns = columnActions.removeColumn($scope.state.columns, columnName); + const columns = columnActions.removeColumn($scope.state.columns, columnName, useNewFieldsApi); // The state's sort property is an array of [sortByColumn,sortDirection] const sort = $scope.state.sort.length ? $scope.state.sort.filter((subArr) => subArr[0] !== columnName) diff --git a/src/plugins/discover/public/application/angular/discover_legacy.html b/src/plugins/discover/public/application/angular/discover_legacy.html index 3596c0a2519ed..9383980fd9fd6 100644 --- a/src/plugins/discover/public/application/angular/discover_legacy.html +++ b/src/plugins/discover/public/application/angular/discover_legacy.html @@ -29,6 +29,7 @@ top-nav-menu="topNavMenu" update-query="handleRefresh" update-saved-query-id="updateSavedQueryId" + use-new-fields-api="useNewFieldsApi" > diff --git a/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts b/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts index 8257c79af7e8a..1b6d8fcbc2544 100644 --- a/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts +++ b/src/plugins/discover/public/application/angular/doc_table/actions/columns.ts @@ -21,28 +21,32 @@ * Helper function to provide a fallback to a single _source column if the given array of columns * is empty, and removes _source if there are more than 1 columns given * @param columns + * @param useNewFieldsApi should a new fields API be used */ -function buildColumns(columns: string[]) { +function buildColumns(columns: string[], useNewFieldsApi = false) { if (columns.length > 1 && columns.indexOf('_source') !== -1) { return columns.filter((col) => col !== '_source'); } else if (columns.length !== 0) { return columns; } - return ['_source']; + return useNewFieldsApi ? [] : ['_source']; } -export function addColumn(columns: string[], columnName: string) { +export function addColumn(columns: string[], columnName: string, useNewFieldsApi?: boolean) { if (columns.includes(columnName)) { return columns; } - return buildColumns([...columns, columnName]); + return buildColumns([...columns, columnName], useNewFieldsApi); } -export function removeColumn(columns: string[], columnName: string) { +export function removeColumn(columns: string[], columnName: string, useNewFieldsApi?: boolean) { if (!columns.includes(columnName)) { return columns; } - return buildColumns(columns.filter((col) => col !== columnName)); + return buildColumns( + columns.filter((col) => col !== columnName), + useNewFieldsApi + ); } export function moveColumn(columns: string[], columnName: string, newIndex: number) { diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header/helpers.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/helpers.tsx index b456fa0773b85..bb855373c910f 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_header/helpers.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header/helpers.tsx @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { i18n } from '@kbn/i18n'; import { IndexPattern } from '../../../../../kibana_services'; export type SortOrder = [string, string]; @@ -62,17 +63,33 @@ export function getDisplayedColumns( if (!Array.isArray(columns) || typeof indexPattern !== 'object' || !indexPattern.getFieldByName) { return []; } - const columnProps = columns.map((column, idx) => { - const field = indexPattern.getFieldByName(column); - return { - name: column, - displayName: field ? field.displayName : column, - isSortable: field && field.sortable ? true : false, - isRemoveable: column !== '_source' || columns.length > 1, - colLeftIdx: idx - 1 < 0 ? -1 : idx - 1, - colRightIdx: idx + 1 >= columns.length ? -1 : idx + 1, - }; - }); + + const columnProps = + columns.length === 0 + ? [ + { + name: '__document__', + displayName: i18n.translate('discover.docTable.tableHeader.documentHeader', { + defaultMessage: 'Document', + }), + isSortable: false, + isRemoveable: false, + colLeftIdx: -1, + colRightIdx: -1, + }, + ] + : columns.map((column, idx) => { + const field = indexPattern.getFieldByName(column); + return { + name: column, + displayName: field?.displayName ?? column, + isSortable: !!(field && field.sortable), + isRemoveable: column !== '_source' || columns.length > 1, + colLeftIdx: idx - 1 < 0 ? -1 : idx - 1, + colRightIdx: idx + 1 >= columns.length ? -1 : idx + 1, + }; + }); + return !hideTimeField && indexPattern.timeFieldName ? [getTimeColumn(indexPattern.timeFieldName), ...columnProps] : columnProps; diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts index e45f18606e3fc..75206d6bf2e84 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts @@ -27,6 +27,7 @@ import cellTemplateHtml from '../components/table_row/cell.html'; import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html'; import { getServices } from '../../../../kibana_services'; import { getContextUrl } from '../../../helpers/get_context_url'; +import { formatRow } from '../../helpers'; const TAGS_WITH_WS = />\s+ { $el.after(''); @@ -139,19 +141,33 @@ export function createTableRowDirective($compile: ng.ICompileService) { ); } - $scope.columns.forEach(function (column: any) { - const isFilterable = mapping(column) && mapping(column).filterable && $scope.filter; + if ($scope.columns.length === 0 && $scope.useNewFieldsApi) { + const formatted = formatRow(row, indexPattern); newHtmls.push( cellTemplate({ timefield: false, - sourcefield: column === '_source', - formatted: _displayField(row, column, true), - filterable: isFilterable, - column, + sourcefield: true, + formatted, + filterable: false, + column: '__document__', }) ); - }); + } else { + $scope.columns.forEach(function (column: string) { + const isFilterable = mapping(column) && mapping(column).filterable && $scope.filter; + + newHtmls.push( + cellTemplate({ + timefield: false, + sourcefield: column === '_source', + formatted: _displayField(row, column, true), + filterable: isFilterable, + column, + }) + ); + }); + } let $cells = $el.children(); newHtmls.forEach(function (html, i) { diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/_cell.scss b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_cell.scss index b73a2598070b5..22b6e0f29268b 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row/_cell.scss +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_cell.scss @@ -1,9 +1,5 @@ -.kbnDocTableCell__dataField { - white-space: pre-wrap; -} - .kbnDocTableCell__toggleDetails { - padding: 4px 0 0 0!important; + padding: $euiSizeXS 0 0 0!important; } .kbnDocTableCell__filter { diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/details.html b/src/plugins/discover/public/application/angular/doc_table/components/table_row/details.html index fd20bea8fb3df..bb443b880e217 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row/details.html +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/details.html @@ -1,4 +1,4 @@ - +
diff --git a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx index f191fa2dc89e8..0a162673eec82 100644 --- a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx @@ -97,6 +97,7 @@ export interface DocTableLegacyProps { onMoveColumn?: (columns: string, newIdx: number) => void; onRemoveColumn?: (column: string) => void; sort?: string[][]; + useNewFieldsApi?: boolean; } export function DocTableLegacy(renderProps: DocTableLegacyProps) { @@ -118,6 +119,7 @@ export function DocTableLegacy(renderProps: DocTableLegacyProps) { on-move-column="onMoveColumn" on-remove-column="onRemoveColumn" render-complete + use-new-fields-api="useNewFieldsApi" sorting="sort">`, }, () => getServices().getEmbeddableInjector() diff --git a/src/plugins/discover/public/application/angular/doc_table/doc_table.html b/src/plugins/discover/public/application/angular/doc_table/doc_table.html index bb8cc4b9ee4c2..427893bd3e6fe 100644 --- a/src/plugins/discover/public/application/angular/doc_table/doc_table.html +++ b/src/plugins/discover/public/application/angular/doc_table/doc_table.html @@ -46,6 +46,7 @@ class="kbnDocTable__row" on-add-column="onAddColumn" on-remove-column="onRemoveColumn" + use-new-fields-api="useNewFieldsApi" > @@ -97,6 +98,7 @@ data-test-subj="docTableRow{{ row['$$_isAnchor'] ? ' docTableAnchorRow' : ''}}" on-add-column="onAddColumn" on-remove-column="onRemoveColumn" + use-new-fields-api="useNewFieldsApi" > diff --git a/src/plugins/discover/public/application/angular/doc_table/doc_table.ts b/src/plugins/discover/public/application/angular/doc_table/doc_table.ts index 735ee9f555740..2baf010b47c78 100644 --- a/src/plugins/discover/public/application/angular/doc_table/doc_table.ts +++ b/src/plugins/discover/public/application/angular/doc_table/doc_table.ts @@ -48,6 +48,7 @@ export function createDocTableDirective(pagerFactory: any, $filter: any) { onMoveColumn: '=?', onRemoveColumn: '=?', inspectorAdapters: '=?', + useNewFieldsApi: '<', }, link: ($scope: LazyScope, $el: JQuery) => { $scope.persist = { diff --git a/src/plugins/discover/public/application/angular/helpers/index.ts b/src/plugins/discover/public/application/angular/helpers/index.ts index 9bfba4de966be..cba50dfa58751 100644 --- a/src/plugins/discover/public/application/angular/helpers/index.ts +++ b/src/plugins/discover/public/application/angular/helpers/index.ts @@ -18,3 +18,4 @@ */ export { buildPointSeriesData } from './point_series'; +export { formatRow } from './row_formatter'; diff --git a/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts b/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts new file mode 100644 index 0000000000000..60ee1e4c2b68b --- /dev/null +++ b/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts @@ -0,0 +1,69 @@ +/* + * 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 { formatRow } from './row_formatter'; +import { stubbedSavedObjectIndexPattern } from '../../../__mocks__/stubbed_saved_object_index_pattern'; +import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; +import { fieldFormatsMock } from '../../../../../data/common/field_formats/mocks'; + +describe('Row formatter', () => { + const hit = { + foo: 'bar', + number: 42, + hello: '

World

', + also: 'with "quotes" or \'single quotes\'', + }; + + const createIndexPattern = () => { + const id = 'my-index'; + const { + type, + version, + attributes: { timeFieldName, fields, title }, + } = stubbedSavedObjectIndexPattern(id); + + return new IndexPattern({ + spec: { id, type, version, timeFieldName, fields, title }, + fieldFormats: fieldFormatsMock, + shortDotsEnable: false, + metaFields: [], + }); + }; + + const indexPattern = createIndexPattern(); + + const formatHitReturnValue = { + also: 'with \\"quotes\\" or 'single qoutes'', + number: '42', + foo: 'bar', + hello: '<h1>World</h1>', + }; + const formatHitMock = jest.fn().mockReturnValueOnce(formatHitReturnValue); + + beforeEach(() => { + // @ts-ignore + indexPattern.formatHit = formatHitMock; + }); + + it('formats document properly', () => { + expect(formatRow(hit, indexPattern).trim()).toBe( + '
also:
with \\"quotes\\" or 'single qoutes'
number:
42
foo:
bar
hello:
<h1>World</h1>
' + ); + }); +}); diff --git a/src/plugins/discover/public/application/angular/helpers/row_formatter.ts b/src/plugins/discover/public/application/angular/helpers/row_formatter.ts new file mode 100644 index 0000000000000..4ad50ef7621c5 --- /dev/null +++ b/src/plugins/discover/public/application/angular/helpers/row_formatter.ts @@ -0,0 +1,47 @@ +/* + * 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 { template } from 'lodash'; +import { IndexPattern } from '../../../kibana_services'; + +function noWhiteSpace(html: string) { + const TAGS_WITH_WS = />\s+<'); +} + +const templateHtml = ` +
+ <% defPairs.forEach(function (def) { %> +
<%- def[0] %>:
+
<%= def[1] %>
+ <%= ' ' %> + <% }); %> +
`; +export const doTemplate = template(noWhiteSpace(templateHtml)); + +export const formatRow = (hit: Record, indexPattern: IndexPattern) => { + const highlights = hit?.highlight ?? {}; + const formatted = indexPattern.formatHit(hit); + const highlightPairs: Array<[string, unknown]> = []; + const sourcePairs: Array<[string, unknown]> = []; + Object.entries(formatted).forEach(([key, val]) => { + const pairs = highlights[key] ? highlightPairs : sourcePairs; + pairs.push([key, val]); + }); + return doTemplate({ defPairs: [...highlightPairs, ...sourcePairs] }); +}; diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx index f519df8a0b80d..4ace823471c45 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx @@ -48,6 +48,7 @@ export interface ContextAppProps { onChangeSuccessorCount: (count: number) => void; predecessorStatus: string; successorStatus: string; + useNewFieldsApi?: boolean; } const PREDECESSOR_TYPE = 'predecessors'; @@ -87,7 +88,15 @@ export function ContextAppLegacy(renderProps: ContextAppProps) { }; const docTableProps = () => { - const { hits, filter, sorting, columns, indexPattern, minimumVisibleRows } = renderProps; + const { + hits, + filter, + sorting, + columns, + indexPattern, + minimumVisibleRows, + useNewFieldsApi, + } = renderProps; return { columns, indexPattern, @@ -95,6 +104,7 @@ export function ContextAppLegacy(renderProps: ContextAppProps) { rows: hits, onFilter: filter, sort: sorting.map((el) => [el]), + useNewFieldsApi, } as DocTableLegacyProps; }; diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts index dfb5d90c2befe..e52226bee3785 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts @@ -37,6 +37,7 @@ export function createContextAppLegacy(reactDirective: any) { ['successorAvailable', { watchDepth: 'reference' }], ['successorStatus', { watchDepth: 'reference' }], ['onChangeSuccessorCount', { watchDepth: 'reference' }], + ['useNewFieldsApi', { watchDepth: 'reference' }], ['topNavMenu', { watchDepth: 'reference' }], ]); } diff --git a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts index 6e5d47be987d8..fc877cab00e0b 100644 --- a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts +++ b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts @@ -51,5 +51,6 @@ export function createDiscoverLegacyDirective(reactDirective: any) { ['topNavMenu', { watchDepth: 'reference' }], ['updateQuery', { watchDepth: 'reference' }], ['updateSavedQueryId', { watchDepth: 'reference' }], + ['useNewFieldsApi', { watchDepth: 'reference' }], ]); } diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index 436a145024437..402e686979d67 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -219,6 +219,7 @@ export interface DiscoverProps { * Function to update the actual savedQuery id */ updateSavedQueryId: (savedQueryId?: string) => void; + useNewFieldsApi?: boolean; } export const DocTableLegacyMemoized = React.memo((props: DocTableLegacyProps) => ( @@ -257,6 +258,7 @@ export function DiscoverLegacy({ topNavMenu, updateQuery, updateSavedQueryId, + useNewFieldsApi, }: DiscoverProps) { const scrollableDesktop = useRef(null); const collapseIcon = useRef(null); @@ -278,6 +280,17 @@ export function DiscoverLegacy({ : undefined; const contentCentered = resultState === 'uninitialized'; + const getDisplayColumns = () => { + if (!state.columns) { + return []; + } + const columns = [...state.columns]; + if (useNewFieldsApi) { + return columns.filter((column) => column !== '_source'); + } + return columns.length === 0 ? ['_source'] : columns; + }; + return ( @@ -315,6 +328,7 @@ export function DiscoverLegacy({ setIndexPattern={setIndexPattern} isClosed={isSidebarClosed} trackUiMetric={trackUiMetric} + useNewFieldsApi={useNewFieldsApi} /> @@ -445,7 +459,7 @@ export function DiscoverLegacy({ {rows && rows.length && (
{rows.length === opts.sampleSize ? (
+ +
+ +
+ +
+ + + +
+
+
+
+
+
+ +`; diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.scss b/src/plugins/discover/public/application/components/sidebar/discover_field.scss index 8e1dd41f66ab1..40bc58cef7023 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.scss +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.scss @@ -1,4 +1,10 @@ .dscSidebarItem__fieldPopoverPanel { - min-width: 260px; - max-width: 300px; + min-width: $euiSizeXXL * 6.5; + max-width: $euiSizeXXL * 7.5; +} + +.dscSidebarItem--multi { + .kbnFieldButton__button { + padding-left: 0; + } } diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx index 0957ee101bd27..d22ef7cdcc28c 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx @@ -82,7 +82,7 @@ function getComponent({ const props = { indexPattern, field: finalField, - getDetails: jest.fn(() => ({ buckets: [], error: '', exists: 1, total: true, columns: [] })), + getDetails: jest.fn(() => ({ buckets: [], error: '', exists: 1, total: 2, columns: [] })), onAddFilter: jest.fn(), onAddField: jest.fn(), onRemoveField: jest.fn(), diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx index f95e512dfb66e..b885bdab316b5 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx @@ -19,7 +19,7 @@ import './discover_field.scss'; import React, { useState } from 'react'; -import { EuiPopover, EuiPopoverTitle, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { EuiPopover, EuiPopoverTitle, EuiButtonIcon, EuiToolTip, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { UiCounterMetricType } from '@kbn/analytics'; import classNames from 'classnames'; @@ -28,6 +28,7 @@ import { FieldIcon, FieldButton } from '../../../../../kibana_react/public'; import { FieldDetails } from './types'; import { IndexPatternField, IndexPattern } from '../../../../../data/public'; import { getFieldTypeName } from './lib/get_field_type_name'; +import { DiscoverFieldDetailsFooter } from './discover_field_details_footer'; export interface DiscoverFieldProps { /** @@ -69,6 +70,8 @@ export interface DiscoverFieldProps { * @param eventName */ trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; + + multiFields?: Array<{ field: IndexPatternField; isSelected: boolean }>; } export function DiscoverField({ @@ -81,6 +84,7 @@ export function DiscoverField({ getDetails, selected, trackUiMetric, + multiFields, }: DiscoverFieldProps) { const addLabelAria = i18n.translate('discover.fieldChooser.discoverField.addButtonAriaLabel', { defaultMessage: 'Add {field} to table', @@ -96,8 +100,8 @@ export function DiscoverField({ const [infoIsOpen, setOpen] = useState(false); - const toggleDisplay = (f: IndexPatternField) => { - if (selected) { + const toggleDisplay = (f: IndexPatternField, isSelected: boolean) => { + if (isSelected) { onRemoveField(f.name); } else { onAddField(f.name); @@ -115,72 +119,100 @@ export function DiscoverField({ return str ? str.replace(/\./g, '.\u200B') : ''; } - const dscFieldIcon = ( - - ); + const getDscFieldIcon = (indexPatternField: IndexPatternField) => { + return ( + + ); + }; - const title = - field.displayName !== field.name ? `${field.name} (${field.displayName} )` : field.displayName; + const dscFieldIcon = getDscFieldIcon(field); + + const getTitle = (indexPatternField: IndexPatternField) => { + return indexPatternField.displayName !== indexPatternField.name + ? i18n.translate('discover.field.title', { + defaultMessage: '{fieldName} ({fieldDisplayName})', + values: { + fieldName: indexPatternField.name, + fieldDisplayName: indexPatternField.displayName, + }, + }) + : indexPatternField.displayName; + }; + + const getFieldName = (indexPatternField: IndexPatternField) => { + return ( + + {wrapOnDot(indexPatternField.displayName)} + + ); + }; + const fieldName = getFieldName(field); - const fieldName = ( - - {wrapOnDot(field.displayName)} - - ); const actionBtnClassName = classNames('dscSidebarItem__action', { ['dscSidebarItem__mobile']: alwaysShowActionButton, }); - let actionButton; - if (field.name !== '_source' && !selected) { - actionButton = ( - - ) => { - if (ev.type === 'click') { - ev.currentTarget.focus(); - } - ev.preventDefault(); - ev.stopPropagation(); - toggleDisplay(field); - }} - data-test-subj={`fieldToggle-${field.name}`} - aria-label={addLabelAria} - /> - - ); - } else if (field.name !== '_source' && selected) { - actionButton = ( - - ) => { - if (ev.type === 'click') { - ev.currentTarget.focus(); - } - ev.preventDefault(); - ev.stopPropagation(); - toggleDisplay(field); - }} - data-test-subj={`fieldToggle-${field.name}`} - aria-label={removeLabelAria} - /> - - ); - } + const getActionButton = (f: IndexPatternField, isSelected?: boolean) => { + if (f.name !== '_source' && !isSelected) { + return ( + + ) => { + if (ev.type === 'click') { + ev.currentTarget.focus(); + } + ev.preventDefault(); + ev.stopPropagation(); + toggleDisplay(f, false); + }} + data-test-subj={`fieldToggle-${f.name}`} + aria-label={addLabelAria} + /> + + ); + } else if (f.name !== '_source' && isSelected) { + return ( + + ) => { + if (ev.type === 'click') { + ev.currentTarget.focus(); + } + ev.preventDefault(); + ev.stopPropagation(); + toggleDisplay(f, isSelected); + }} + data-test-subj={`fieldToggle-${f.name}`} + aria-label={removeLabelAria} + /> + + ); + } + }; + + const actionButton = getActionButton(field, selected); if (field.type === '_source') { return ( @@ -195,6 +227,37 @@ export function DiscoverField({ ); } + const shouldRenderMultiFields = !!multiFields; + const renderMultiFields = () => { + if (!multiFields) { + return null; + } + return ( + + +
+ {i18n.translate('discover.fieldChooser.discoverField.multiFields', { + defaultMessage: 'Multi fields', + })} +
+
+ {multiFields.map((entry) => ( + {}} + dataTestSubj={`field-${entry.field.name}-showDetails`} + fieldIcon={getDscFieldIcon(entry.field)} + fieldAction={getActionButton(entry.field, entry.isSelected)} + fieldName={getFieldName(entry.field)} + key={entry.field.name} + /> + ))} +
+ ); + }; + return ( - - {' '} - {i18n.translate('discover.fieldChooser.discoverField.fieldTopValuesLabel', { - defaultMessage: 'Top 5 values', - })} - + {field.displayName} + +
+ {i18n.translate('discover.fieldChooser.discoverField.fieldTopValuesLabel', { + defaultMessage: 'Top 5 values', + })} +
+
{infoIsOpen && ( )} + {shouldRenderMultiFields ? ( + <> + {renderMultiFields()} + + + ) : null}
); } diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx index 0618e53d15dbb..8444f11ac912c 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx @@ -38,7 +38,7 @@ const indexPattern = getStubIndexPattern( describe('discover sidebar field details', function () { const defaultProps = { indexPattern, - details: { buckets: [], error: '', exists: 1, total: true, columns: [] }, + details: { buckets: [], error: '', exists: 1, total: 2, columns: [] }, onAddFilter: jest.fn(), }; diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index 740de54ae0cf3..bf24337543037 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useState, useEffect } from 'react'; -import { EuiLink, EuiIconTip, EuiText, EuiPopoverFooter, EuiButton, EuiSpacer } from '@elastic/eui'; +import { EuiIconTip, EuiText, EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import { DiscoverFieldBucket } from './discover_field_bucket'; @@ -30,6 +30,7 @@ import { import { Bucket, FieldDetails } from './types'; import { IndexPatternField, IndexPattern } from '../../../../../data/public'; import './discover_field_details.scss'; +import { DiscoverFieldDetailsFooter } from './discover_field_details_footer'; interface DiscoverFieldDetailsProps { field: IndexPatternField; @@ -37,6 +38,7 @@ interface DiscoverFieldDetailsProps { details: FieldDetails; onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; + showFooter?: boolean; } export function DiscoverFieldDetails({ @@ -45,6 +47,7 @@ export function DiscoverFieldDetails({ details, onAddFilter, trackUiMetric, + showFooter = true, }: DiscoverFieldDetailsProps) { const warnings = getWarnings(field); const [showVisualizeLink, setShowVisualizeLink] = useState(false); @@ -118,27 +121,13 @@ export function DiscoverFieldDetails({ )}
- {!details.error && ( - - - {!indexPattern.metaFields.includes(field.name) && !field.scripted ? ( - onAddFilter('_exists_', field.name, '+')}> - {' '} - {details.exists} - - ) : ( - {details.exists} - )}{' '} - / {details.total}{' '} - - - + {!details.error && showFooter && ( + )} ); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx new file mode 100644 index 0000000000000..028187569e977 --- /dev/null +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx @@ -0,0 +1,82 @@ +/* + * 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 React from 'react'; +import { findTestSubject } from '@elastic/eui/lib/test'; +// @ts-ignore +import stubbedLogstashFields from 'fixtures/logstash_fields'; +import { mountWithIntl } from '@kbn/test/jest'; +import { coreMock } from '../../../../../../core/public/mocks'; +import { IndexPatternField } from '../../../../../data/public'; +import { getStubIndexPattern } from '../../../../../data/public/test_utils'; +import { DiscoverFieldDetailsFooter } from './discover_field_details_footer'; + +const indexPattern = getStubIndexPattern( + 'logstash-*', + (cfg: any) => cfg, + 'time', + stubbedLogstashFields(), + coreMock.createSetup() +); + +describe('discover sidebar field details footer', function () { + const onAddFilter = jest.fn(); + const defaultProps = { + indexPattern, + details: { buckets: [], error: '', exists: 1, total: 2, columns: [] }, + onAddFilter, + }; + + function mountComponent(field: IndexPatternField) { + const compProps = { ...defaultProps, field }; + return mountWithIntl(); + } + + it('renders properly', function () { + const visualizableField = new IndexPatternField({ + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }); + const component = mountComponent(visualizableField); + expect(component).toMatchSnapshot(); + }); + + it('click on addFilter calls the function', function () { + const visualizableField = new IndexPatternField({ + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }); + const component = mountComponent(visualizableField); + const onAddButton = findTestSubject(component, 'onAddFilterButton'); + onAddButton.simulate('click'); + expect(onAddFilter).toHaveBeenCalledWith('_exists_', visualizableField.name, '+'); + }); +}); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.tsx new file mode 100644 index 0000000000000..58e91c85913a1 --- /dev/null +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.tsx @@ -0,0 +1,70 @@ +/* + * 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 React from 'react'; +import { EuiLink, EuiPopoverFooter, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { IndexPatternField } from '../../../../../data/common/index_patterns/fields'; +import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; +import { FieldDetails } from './types'; + +interface DiscoverFieldDetailsFooterProps { + field: IndexPatternField; + indexPattern: IndexPattern; + details: FieldDetails; + onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; +} + +export function DiscoverFieldDetailsFooter({ + field, + indexPattern, + details, + onAddFilter, +}: DiscoverFieldDetailsFooterProps) { + return ( + + + {!indexPattern.metaFields.includes(field.name) && !field.scripted ? ( + onAddFilter('_exists_', field.name, '+')} + data-test-subj="onAddFilterButton" + > + + + ) : ( + + )} + + + ); +} diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index 57cc45b3c3e9f..6c312924fb7b7 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -100,6 +100,10 @@ export interface DiscoverSidebarProps { * Callback function to select another index pattern */ setIndexPattern: (id: string) => void; + /** + * If on, fields are read from the fields API, not from source + */ + useNewFieldsApi?: boolean; /** * Metric tracking function * @param metricType @@ -127,9 +131,11 @@ export function DiscoverSidebar({ setFieldFilter, setIndexPattern, trackUiMetric, + useNewFieldsApi = false, useFlyout = false, }: DiscoverSidebarProps) { const [fields, setFields] = useState(null); + useEffect(() => { const newFields = getIndexPatternFieldList(selectedIndexPattern, fieldCounts); setFields(newFields); @@ -154,13 +160,10 @@ export function DiscoverSidebar({ selected: selectedFields, popular: popularFields, unpopular: unpopularFields, - } = useMemo(() => groupFields(fields, columns, popularLimit, fieldCounts, fieldFilter), [ - fields, - columns, - popularLimit, - fieldCounts, - fieldFilter, - ]); + } = useMemo( + () => groupFields(fields, columns, popularLimit, fieldCounts, fieldFilter, useNewFieldsApi), + [fields, columns, popularLimit, fieldCounts, fieldFilter, useNewFieldsApi] + ); const fieldTypes = useMemo(() => { const result = ['any']; @@ -174,6 +177,27 @@ export function DiscoverSidebar({ return result; }, [fields]); + const multiFields = useMemo(() => { + if (!useNewFieldsApi || !fields) { + return undefined; + } + const map = new Map>(); + fields.forEach((field) => { + const parent = field.spec?.subType?.multi?.parent; + if (!parent) { + return; + } + const multiField = { + field, + isSelected: selectedFields.includes(field), + }; + const value = map.get(parent) ?? []; + value.push(multiField); + map.set(parent, value); + }); + return map; + }, [fields, useNewFieldsApi, selectedFields]); + if (!selectedIndexPattern || !fields) { return null; } @@ -278,6 +302,7 @@ export function DiscoverSidebar({ getDetails={getDetailsByField} selected={true} trackUiMetric={trackUiMetric} + multiFields={multiFields?.get(field.name)} /> ); @@ -338,6 +363,7 @@ export function DiscoverSidebar({ onAddFilter={onAddFilter} getDetails={getDetailsByField} trackUiMetric={trackUiMetric} + multiFields={multiFields?.get(field.name)} /> ); @@ -366,6 +392,7 @@ export function DiscoverSidebar({ onAddFilter={onAddFilter} getDetails={getDetailsByField} trackUiMetric={trackUiMetric} + multiFields={multiFields?.get(field.name)} /> ); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx index 0413ebd17d71b..3000291fc23bb 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.tsx @@ -103,6 +103,10 @@ export interface DiscoverSidebarResponsiveProps { * Shows index pattern and a button that displays the sidebar in a flyout */ useFlyout?: boolean; + /** + * Read from the Fields API + */ + useNewFieldsApi?: boolean; } /** diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts index 22cacae4c3b45..6cbfa03a070db 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts @@ -69,7 +69,8 @@ describe('group_fields', function () { ['currency'], 5, fieldCounts, - fieldFilterState + fieldFilterState, + false ); expect(actual).toMatchInlineSnapshot(` Object { @@ -118,6 +119,80 @@ describe('group_fields', function () { } `); }); + it('should group fields in selected, popular, unpopular group if they contain multifields', function () { + const category = { + name: 'category', + type: 'string', + esTypes: ['text'], + count: 1, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }; + const currency = { + name: 'currency', + displayName: 'currency', + kbnFieldType: { + esTypes: ['string', 'text', 'keyword', '_type', '_id'], + filterable: true, + name: 'string', + sortable: true, + }, + spec: { + esTypes: ['text'], + name: 'category', + }, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }; + const currencyKeyword = { + name: 'currency.keyword', + displayName: 'currency.keyword', + type: 'string', + esTypes: ['keyword'], + kbnFieldType: { + esTypes: ['string', 'text', 'keyword', '_type', '_id'], + filterable: true, + name: 'string', + sortable: true, + }, + spec: { + aggregatable: true, + esTypes: ['keyword'], + name: 'category.keyword', + readFromDocValues: true, + searchable: true, + shortDotsEnable: false, + subType: { + multi: { + parent: 'currency', + }, + }, + }, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }; + const fieldsToGroup = [category, currency, currencyKeyword]; + + const fieldFilterState = getDefaultFieldFilter(); + + const actual = groupFields( + fieldsToGroup as any, + ['currency'], + 5, + fieldCounts, + fieldFilterState, + true + ); + expect(actual.popular).toEqual([category]); + expect(actual.selected).toEqual([currency]); + expect(actual.unpopular).toEqual([]); + }); it('should sort selected fields by columns order ', function () { const fieldFilterState = getDefaultFieldFilter(); @@ -127,7 +202,8 @@ describe('group_fields', function () { ['customer_birth_date', 'currency', 'unknown'], 5, fieldCounts, - fieldFilterState + fieldFilterState, + false ); expect(actual1.selected.map((field) => field.name)).toEqual([ 'customer_birth_date', @@ -140,7 +216,8 @@ describe('group_fields', function () { ['currency', 'customer_birth_date', 'unknown'], 5, fieldCounts, - fieldFilterState + fieldFilterState, + false ); expect(actual2.selected.map((field) => field.name)).toEqual([ 'currency', diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx index c34becc97cb93..e6c3d0fe3ea42 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx @@ -33,7 +33,8 @@ export function groupFields( columns: string[], popularLimit: number, fieldCounts: Record, - fieldFilterState: FieldFilterState + fieldFilterState: FieldFilterState, + useNewFieldsApi: boolean ): GroupedFields { const result: GroupedFields = { selected: [], @@ -62,12 +63,17 @@ export function groupFields( if (!isFieldFiltered(field, fieldFilterState, fieldCounts)) { continue; } + const isSubfield = useNewFieldsApi && field.spec?.subType?.multi?.parent; if (columns.includes(field.name)) { result.selected.push(field); } else if (popular.includes(field.name) && field.type !== '_source') { - result.popular.push(field); + if (!isSubfield) { + result.popular.push(field); + } } else if (field.type !== '_source') { - result.unpopular.push(field); + if (!isSubfield) { + result.unpopular.push(field); + } } } // add columns, that are not part of the index pattern, to be removeable diff --git a/src/plugins/discover/public/application/components/sidebar/types.ts b/src/plugins/discover/public/application/components/sidebar/types.ts index d80662b65cc7b..4ec731e852ce3 100644 --- a/src/plugins/discover/public/application/components/sidebar/types.ts +++ b/src/plugins/discover/public/application/components/sidebar/types.ts @@ -25,7 +25,7 @@ export interface IndexPatternRef { export interface FieldDetails { error: string; exists: number; - total: boolean; + total: number; buckets: Bucket[]; columns: string[]; } diff --git a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts index 458b9b7e066fd..5af4449a63e0b 100644 --- a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts +++ b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts @@ -29,7 +29,8 @@ export function getSwitchIndexPatternAppState( nextIndexPattern: IndexPattern, currentColumns: string[], currentSort: SortPairArr[], - modifyColumns: boolean = true + modifyColumns: boolean = true, + useNewFieldsApi: boolean = false ) { const nextColumns = modifyColumns ? currentColumns.filter( @@ -38,9 +39,11 @@ export function getSwitchIndexPatternAppState( ) : currentColumns; const nextSort = getSortArray(currentSort, nextIndexPattern); + const defaultColumns = useNewFieldsApi ? [] : ['_source']; + const columns = nextColumns.length ? nextColumns : defaultColumns; return { index: nextIndexPattern.id, - columns: nextColumns.length ? nextColumns : ['_source'], + columns, sort: nextSort, }; } diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts index 8ec2012b5843e..2f373c34eb17d 100644 --- a/src/plugins/discover/public/application/helpers/persist_saved_search.ts +++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts @@ -49,6 +49,8 @@ export async function persistSavedSearch( indexPattern, services, sort: state.sort as SortOrder[], + columns: state.columns || [], + useNewFieldsApi: false, }); savedSearch.columns = state.columns || []; diff --git a/src/plugins/discover/public/application/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/helpers/update_search_source.test.ts index 91832325432ef..615a414680469 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.test.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.test.ts @@ -44,8 +44,37 @@ describe('updateSearchSource', () => { } as unknown) as IUiSettingsClient, } as unknown) as DiscoverServices, sort: [] as SortOrder[], + columns: [], + useNewFieldsApi: false, }); expect(result.getField('index')).toEqual(indexPatternMock); expect(result.getField('size')).toEqual(sampleSize); + expect(result.getField('fields')).toBe(undefined); + }); + + test('updates a given search source with the usage of the new fields api', async () => { + const searchSourceMock = createSearchSourceMock({}); + const sampleSize = 250; + const result = updateSearchSource(searchSourceMock, { + indexPattern: indexPatternMock, + services: ({ + data: dataPluginMock.createStartContract(), + uiSettings: ({ + get: (key: string) => { + if (key === SAMPLE_SIZE_SETTING) { + return sampleSize; + } + return false; + }, + } as unknown) as IUiSettingsClient, + } as unknown) as DiscoverServices, + sort: [] as SortOrder[], + columns: [], + useNewFieldsApi: true, + }); + expect(result.getField('index')).toEqual(indexPatternMock); + expect(result.getField('size')).toEqual(sampleSize); + expect(result.getField('fields')).toEqual(['*']); + expect(result.getField('fieldsFromSource')).toBe(undefined); }); }); diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts index 324dc8a48457a..46f1c9f626054 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.ts @@ -31,10 +31,14 @@ export function updateSearchSource( indexPattern, services, sort, + columns, + useNewFieldsApi, }: { indexPattern: IndexPattern; services: DiscoverServices; sort: SortOrder[]; + columns: string[]; + useNewFieldsApi: boolean; } ) { const { uiSettings, data } = services; @@ -50,5 +54,13 @@ export function updateSearchSource( .setField('sort', usedSort) .setField('query', data.query.queryString.getQuery() || null) .setField('filter', data.query.filterManager.getFilters()); + if (useNewFieldsApi) { + searchSource.removeField('fieldsFromSource'); + searchSource.setField('fields', ['*']); + } else { + searchSource.removeField('fields'); + const fieldNames = indexPattern.fields.map((field) => field.name); + searchSource.setField('fieldsFromSource', fieldNames); + } return searchSource; } diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index 425928385e64a..673f55c78a506 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -35,6 +35,7 @@ import { CONTEXT_TIE_BREAKER_FIELDS_SETTING, DOC_TABLE_LEGACY, MODIFY_COLUMNS_ON_SWITCH, + SEARCH_FIELDS_FROM_SOURCE, } from '../common'; export const uiSettings: Record = { @@ -198,4 +199,11 @@ export const uiSettings: Record = { name: 'discover:modifyColumnsOnSwitchTitle', }, }, + [SEARCH_FIELDS_FROM_SOURCE]: { + name: 'Read fields from _source', + description: `When enabled will load documents directly from \`_source\`. This is soon going to be deprecated. When disabled, will retrieve fields via the new Fields API in the high-level search service.`, + value: false, + category: ['discover'], + schema: schema.boolean(), + }, }; diff --git a/test/functional/apps/context/_date_nanos_custom_timestamp.js b/test/functional/apps/context/_date_nanos_custom_timestamp.js index 8fe08d13af0aa..8772b10a4b8c8 100644 --- a/test/functional/apps/context/_date_nanos_custom_timestamp.js +++ b/test/functional/apps/context/_date_nanos_custom_timestamp.js @@ -38,6 +38,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.update({ 'context:defaultSize': `${TEST_DEFAULT_CONTEXT_SIZE}`, 'context:step': `${TEST_STEP_SIZE}`, + 'discover:searchFieldsFromSource': true, }); }); diff --git a/test/functional/apps/discover/_data_grid_doc_navigation.ts b/test/functional/apps/discover/_data_grid_doc_navigation.ts index 92d9893cab0b6..97b8eb564a256 100644 --- a/test/functional/apps/discover/_data_grid_doc_navigation.ts +++ b/test/functional/apps/discover/_data_grid_doc_navigation.ts @@ -56,7 +56,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(hasDocHit).to.be(true); }); - it('add filter should create an exists filter if value is null (#7189)', async function () { + // no longer relevant as null field won't be returned in the Fields API response + xit('add filter should create an exists filter if value is null (#7189)', async function () { await PageObjects.discover.waitUntilSearchingHasFinished(); // Filter special document await filterBar.addFilter('agent', 'is', 'Missing/Fields'); diff --git a/test/functional/apps/discover/_data_grid_field_data.ts b/test/functional/apps/discover/_data_grid_field_data.ts index 8224f59f7fabf..137c19149d274 100644 --- a/test/functional/apps/discover/_data_grid_field_data.ts +++ b/test/functional/apps/discover/_data_grid_field_data.ts @@ -53,7 +53,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('the search term should be highlighted in the field data', async function () { // marks is the style that highlights the text in yellow const marks = await PageObjects.discover.getMarks(); - expect(marks.length).to.be(25); + expect(marks.length).to.be(50); expect(marks.indexOf('php')).to.be(0); }); diff --git a/test/functional/apps/discover/_discover_fields_api.ts b/test/functional/apps/discover/_discover_fields_api.ts new file mode 100644 index 0000000000000..94cb4ed5fa52e --- /dev/null +++ b/test/functional/apps/discover/_discover_fields_api.ts @@ -0,0 +1,71 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from './ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const log = getService('log'); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); + const defaultSettings = { + defaultIndex: 'logstash-*', + 'discover:searchFieldsFromSource': false, + }; + describe('discover uses fields API test', function describeIndexTests() { + before(async function () { + log.debug('load kibana index with default index pattern'); + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + log.debug('discover'); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + after(async () => { + await kibanaServer.uiSettings.replace({ 'discover:searchFieldsFromSource': true }); + }); + + it('should correctly display documents', async function () { + log.debug('check if Document title exists in the grid'); + expect(await PageObjects.discover.getDocHeader()).to.have.string('Document'); + const rowData = await PageObjects.discover.getDocTableIndex(1); + log.debug('check the newest doc timestamp in UTC (check diff timezone in last test)'); + expect(rowData.startsWith('Sep 22, 2015 @ 23:50:13.253')).to.be.ok(); + const expectedHitCount = '14,004'; + await retry.try(async function () { + expect(await PageObjects.discover.getHitCount()).to.be(expectedHitCount); + }); + }); + + it('adding a column removes a default column', async function () { + await PageObjects.discover.clickFieldListItemAdd('_score'); + expect(await PageObjects.discover.getDocHeader()).to.have.string('_score'); + expect(await PageObjects.discover.getDocHeader()).not.to.have.string('Document'); + }); + + it('removing a column adds a default column', async function () { + await PageObjects.discover.clickFieldListItemRemove('_score'); + expect(await PageObjects.discover.getDocHeader()).not.to.have.string('_score'); + expect(await PageObjects.discover.getDocHeader()).to.have.string('Document'); + }); + }); +} diff --git a/test/functional/apps/discover/_doc_navigation.ts b/test/functional/apps/discover/_doc_navigation.ts index 76612b255ac23..79632942cf04a 100644 --- a/test/functional/apps/discover/_doc_navigation.ts +++ b/test/functional/apps/discover/_doc_navigation.ts @@ -55,7 +55,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(hasDocHit).to.be(true); }); - it('add filter should create an exists filter if value is null (#7189)', async function () { + // no longer relevant as null field won't be returned in the Fields API response + xit('add filter should create an exists filter if value is null (#7189)', async function () { await PageObjects.discover.waitUntilSearchingHasFinished(); // Filter special document await filterBar.addFilter('agent', 'is', 'Missing/Fields'); diff --git a/test/functional/apps/discover/_field_data.ts b/test/functional/apps/discover/_field_data.ts index e08325a81a3e8..3811cde8a6367 100644 --- a/test/functional/apps/discover/_field_data.ts +++ b/test/functional/apps/discover/_field_data.ts @@ -36,6 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await esArchiver.load('discover'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*', + 'discover:searchFieldsFromSource': true, }); await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.common.navigateToApp('discover'); diff --git a/test/functional/apps/discover/_field_data_with_fields_api.ts b/test/functional/apps/discover/_field_data_with_fields_api.ts new file mode 100644 index 0000000000000..923a021f5fad6 --- /dev/null +++ b/test/functional/apps/discover/_field_data_with_fields_api.ts @@ -0,0 +1,105 @@ +/* + * 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 expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const toasts = getService('toasts'); + const queryBar = getService('queryBar'); + const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']); + + describe('discover tab with new fields API', function describeIndexTests() { + this.tags('includeFirefox'); + before(async function () { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.load('discover'); + await kibanaServer.uiSettings.replace({ + defaultIndex: 'logstash-*', + 'discover:searchFieldsFromSource': false, + }); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await PageObjects.common.navigateToApp('discover'); + }); + describe('field data', function () { + it('search php should show the correct hit count', async function () { + const expectedHitCount = '445'; + await retry.try(async function () { + await queryBar.setQuery('php'); + await queryBar.submitQuery(); + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be(expectedHitCount); + }); + }); + + it('the search term should be highlighted in the field data', async function () { + // marks is the style that highlights the text in yellow + const marks = await PageObjects.discover.getMarks(); + expect(marks.length).to.be(100); + expect(marks.indexOf('php')).to.be(0); + }); + + it('search type:apache should show the correct hit count', async function () { + const expectedHitCount = '11,156'; + await queryBar.setQuery('type:apache'); + await queryBar.submitQuery(); + await retry.try(async function tryingForTime() { + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be(expectedHitCount); + }); + }); + + it('doc view should show Time and Document columns', async function () { + const expectedHeader = 'Time Document'; + const Docheader = await PageObjects.discover.getDocHeader(); + expect(Docheader).to.be(expectedHeader); + }); + + it('doc view should sort ascending', async function () { + const expectedTimeStamp = 'Sep 20, 2015 @ 00:00:00.000'; + await PageObjects.discover.clickDocSortDown(); + + // we don't technically need this sleep here because the tryForTime will retry and the + // results will match on the 2nd or 3rd attempt, but that debug output is huge in this + // case and it can be avoided with just a few seconds sleep. + await PageObjects.common.sleep(2000); + await retry.try(async function tryingForTime() { + const rowData = await PageObjects.discover.getDocTableIndex(1); + + expect(rowData.startsWith(expectedTimeStamp)).to.be.ok(); + }); + }); + + it('a bad syntax query should show an error message', async function () { + const expectedError = + 'Expected ":", "<", "<=", ">", ">=", AND, OR, end of input, ' + + 'whitespace but "(" found.'; + await queryBar.setQuery('xxx(yyy))'); + await queryBar.submitQuery(); + const { message } = await toasts.getErrorToast(); + expect(message).to.contain(expectedError); + await toasts.dismissToast(); + }); + }); + }); +} diff --git a/test/functional/apps/discover/_large_string.ts b/test/functional/apps/discover/_large_string.ts index fe5613a4e3f19..e8ad6131bcc21 100644 --- a/test/functional/apps/discover/_large_string.ts +++ b/test/functional/apps/discover/_large_string.ts @@ -40,7 +40,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('verify the large string book present', async function () { const ExpectedDoc = - 'mybook:Project Gutenberg EBook of Hamlet, by William Shakespeare' + + '_id:1 _type: - _index:testlargestring _score:0' + + ' mybook:Project Gutenberg EBook of Hamlet, by William Shakespeare' + ' This eBook is for the use of anyone anywhere in the United States' + ' and most other parts of the world at no cost and with almost no restrictions whatsoever.' + ' You may copy it, give it away or re-use it under the terms of the' + diff --git a/test/functional/apps/discover/_shared_links.ts b/test/functional/apps/discover/_shared_links.ts index 51ea5f997e859..b15f5b0aae39f 100644 --- a/test/functional/apps/discover/_shared_links.ts +++ b/test/functional/apps/discover/_shared_links.ts @@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { '/app/discover?_t=1453775307251#' + '/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time' + ":(from:'2015-09-19T06:31:44.000Z',to:'2015-09" + - "-23T18:31:44.000Z'))&_a=(columns:!(_source),filters:!(),index:'logstash-" + + "-23T18:31:44.000Z'))&_a=(columns:!(),filters:!(),index:'logstash-" + "*',interval:auto,query:(language:kuery,query:'')" + ",sort:!(!('@timestamp',desc)))"; const actualUrl = await PageObjects.share.getSharedUrl(); diff --git a/test/functional/apps/discover/_source_filters.ts b/test/functional/apps/discover/_source_filters.ts index 0af7c0ade79ba..d2ae02ef25de4 100644 --- a/test/functional/apps/discover/_source_filters.ts +++ b/test/functional/apps/discover/_source_filters.ts @@ -40,6 +40,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // and load a set of makelogs data await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.update({ + 'discover:searchFieldsFromSource': true, + }); + log.debug('discover'); await PageObjects.common.navigateToApp('discover'); diff --git a/test/functional/apps/discover/ftr_provider_context.d.ts b/test/functional/apps/discover/ftr_provider_context.d.ts new file mode 100644 index 0000000000000..a4894e024b612 --- /dev/null +++ b/test/functional/apps/discover/ftr_provider_context.d.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; +import { services } from '../../services'; +import { pageObjects } from '../../page_objects'; + +export type FtrProviderContext = GenericFtrProviderContext; diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index 450049af66abf..5fd49a1d35216 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -42,6 +42,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_filter_editor')); loadTestFile(require.resolve('./_errors')); loadTestFile(require.resolve('./_field_data')); + loadTestFile(require.resolve('./_field_data_with_fields_api')); loadTestFile(require.resolve('./_shared_links')); loadTestFile(require.resolve('./_sidebar')); loadTestFile(require.resolve('./_source_filters')); @@ -51,6 +52,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_date_nanos')); loadTestFile(require.resolve('./_date_nanos_mixed')); loadTestFile(require.resolve('./_indexpattern_without_timefield')); + loadTestFile(require.resolve('./_discover_fields_api')); loadTestFile(require.resolve('./_data_grid')); loadTestFile(require.resolve('./_data_grid_context')); loadTestFile(require.resolve('./_data_grid_field_data')); diff --git a/test/functional/fixtures/es_archiver/date_nanos/mappings.json b/test/functional/fixtures/es_archiver/date_nanos/mappings.json index bea82767f6cbb..f9ef429a0f97c 100644 --- a/test/functional/fixtures/es_archiver/date_nanos/mappings.json +++ b/test/functional/fixtures/es_archiver/date_nanos/mappings.json @@ -5,7 +5,8 @@ "mappings": { "properties": { "@timestamp": { - "type": "date_nanos" + "type": "date_nanos", + "format": "strict_date_optional_time_nanos" } } }, diff --git a/test/functional/fixtures/es_archiver/date_nanos_mixed/mappings.json b/test/functional/fixtures/es_archiver/date_nanos_mixed/mappings.json index c62918abced58..b29f6b111b06d 100644 --- a/test/functional/fixtures/es_archiver/date_nanos_mixed/mappings.json +++ b/test/functional/fixtures/es_archiver/date_nanos_mixed/mappings.json @@ -29,7 +29,8 @@ "mappings": { "properties": { "timestamp": { - "type": "date_nanos" + "type": "date_nanos", + "format": "strict_date_optional_time_nanos" } } }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f479b3a521185..999da541615c1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1510,10 +1510,8 @@ "discover.embeddable.inspectorRequestDescription": "このリクエストはElasticsearchにクエリをかけ、検索データを取得します。", "discover.embeddable.search.displayName": "検索", "discover.fieldChooser.detailViews.emptyStringText": "空の文字列", - "discover.fieldChooser.detailViews.existsText": "存在する", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "{field}を除外:\"{value}\"", "discover.fieldChooser.detailViews.filterValueButtonAriaLabel": "{field}を除外:\"{value}\"", - "discover.fieldChooser.detailViews.recordsText": "記録", "discover.fieldChooser.detailViews.visualizeLinkText": "可視化", "discover.fieldChooser.discoverField.addButtonAriaLabel": "{field}を表に追加", "discover.fieldChooser.discoverField.addFieldTooltip": "フィールドを列として追加", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a661537e6e288..d92a2ba94ff34 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1510,10 +1510,8 @@ "discover.embeddable.inspectorRequestDescription": "此请求将查询 Elasticsearch 以获取搜索的数据。", "discover.embeddable.search.displayName": "搜索", "discover.fieldChooser.detailViews.emptyStringText": "空字符串", - "discover.fieldChooser.detailViews.existsText": "存在于", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "筛除 {field}:“{value}”", "discover.fieldChooser.detailViews.filterValueButtonAriaLabel": "筛留 {field}:“{value}”", - "discover.fieldChooser.detailViews.recordsText": "个记录", "discover.fieldChooser.detailViews.visualizeLinkText": "可视化", "discover.fieldChooser.discoverField.addButtonAriaLabel": "将 {field} 添加到表中", "discover.fieldChooser.discoverField.addFieldTooltip": "将字段添加为列", diff --git a/x-pack/test/functional/apps/security/doc_level_security_roles.js b/x-pack/test/functional/apps/security/doc_level_security_roles.js index 72f463be48fd5..0595322ad2d21 100644 --- a/x-pack/test/functional/apps/security/doc_level_security_roles.js +++ b/x-pack/test/functional/apps/security/doc_level_security_roles.js @@ -77,7 +77,7 @@ export default function ({ getService, getPageObjects }) { }); const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be( - 'name:ABC Company region:EAST _id:doc1 _type: - _index:dlstest _score:0' + '_id:doc1 _type: - _index:dlstest _score:0 region.keyword:EAST name:ABC Company name.keyword:ABC Company region:EAST' ); }); after('logout', async () => { diff --git a/x-pack/test/functional/apps/security/field_level_security.js b/x-pack/test/functional/apps/security/field_level_security.js index 7b22d72885c9d..3f3984dd05a94 100644 --- a/x-pack/test/functional/apps/security/field_level_security.js +++ b/x-pack/test/functional/apps/security/field_level_security.js @@ -112,7 +112,7 @@ export default function ({ getService, getPageObjects }) { }); const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be( - 'customer_ssn:444.555.6666 customer_name:ABC Company customer_region:WEST _id:2 _type: - _index:flstest _score:0' + '_id:2 _type: - _index:flstest _score:0 customer_name.keyword:ABC Company customer_ssn:444.555.6666 customer_region.keyword:WEST runtime_customer_ssn:444.555.6666 calculated at runtime customer_region:WEST customer_name:ABC Company customer_ssn.keyword:444.555.6666' ); }); @@ -126,7 +126,7 @@ export default function ({ getService, getPageObjects }) { }); const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be( - 'customer_name:ABC Company customer_region:WEST _id:2 _type: - _index:flstest _score:0' + '_id:2 _type: - _index:flstest _score:0 customer_name.keyword:ABC Company customer_region.keyword:WEST customer_region:WEST customer_name:ABC Company' ); }); diff --git a/x-pack/test/functional/es_archives/security/flstest/data/mappings.json b/x-pack/test/functional/es_archives/security/flstest/data/mappings.json index 4f419e4b6ade4..0b970d5a3c1df 100644 --- a/x-pack/test/functional/es_archives/security/flstest/data/mappings.json +++ b/x-pack/test/functional/es_archives/security/flstest/data/mappings.json @@ -7,7 +7,8 @@ "runtime_customer_ssn": { "type": "keyword", "script": { - "source": "emit(doc['customer_ssn'].value + ' calculated at runtime')" + "lang": "painless", + "source": "if (doc['customer_ssn'].size() !== 0) { return emit(doc['customer_ssn'].value + ' calculated at runtime') }" } } }, @@ -37,7 +38,8 @@ "type": "keyword" } }, - "type": "text" + "type": "text", + "fielddata": true } } }, From fc370b74eb2f5efc0fe58ef587371fc276d54bd6 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 15 Jan 2021 10:25:06 -0500 Subject: [PATCH 12/38] [Uptime] Unskip "Observer location" test block (#87571) * Unskip "Observer location" test block. * Commit temp "describe.only" to make flaky test runner go faster. * Add optional chain for some potentially-null props. * Make overview filters type partial. * Repair broken types. * Remove \`only\` call from test. --- .../overview_filters/overview_filters.ts | 2 +- .../alerts_containers/alert_monitor_status.tsx | 2 +- .../overview/filter_group/filter_group.tsx | 18 +++++++++++------- .../test/functional/apps/uptime/locations.ts | 3 +-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts b/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts index 9b9241494f001..854b4b6b6e9b8 100644 --- a/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts +++ b/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts @@ -6,7 +6,7 @@ import * as t from 'io-ts'; -export const OverviewFiltersType = t.type({ +export const OverviewFiltersType = t.partial({ locations: t.array(t.string), ports: t.array(t.number), schemes: t.array(t.string), diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx index 50b6fe2aa0ef1..1c1deb2104970 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx @@ -122,7 +122,7 @@ export const AlertMonitorStatus: React.FC = ({ enabled={enabled} hasFilters={!!overviewFilters?.filters} isOldAlert={isOldAlert} - locations={locations} + locations={locations || []} numTimes={numTimes} setAlertParams={setAlertParams} shouldUpdateUrl={shouldUpdateUrl} diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx index 45268977a543f..a5256ee1e2626 100644 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx @@ -24,6 +24,10 @@ const Container = styled(EuiFilterGroup)` margin-bottom: 10px; `; +function isDisabled(array?: T[]) { + return array ? array.length === 0 : true; +} + export const FilterGroupComponent: React.FC = ({ overviewFilters, loading, @@ -51,7 +55,7 @@ export const FilterGroupComponent: React.FC = ({ onFilterFieldChange, fieldName: 'observer.geo.name', id: 'location', - items: locations, + items: locations || [], selectedItems: selectedLocations, title: filterLabels.LOCATION, }, @@ -63,8 +67,8 @@ export const FilterGroupComponent: React.FC = ({ onFilterFieldChange, fieldName: 'url.port', id: 'port', - disabled: ports.length === 0, - items: ports.map((p: number) => p.toString()), + disabled: isDisabled(ports), + items: ports?.map((p: number) => p.toString()) ?? [], selectedItems: selectedPorts, title: filterLabels.PORT, }, @@ -73,8 +77,8 @@ export const FilterGroupComponent: React.FC = ({ onFilterFieldChange, fieldName: 'monitor.type', id: 'scheme', - disabled: schemes.length === 0, - items: schemes, + disabled: isDisabled(schemes), + items: schemes ?? [], selectedItems: selectedSchemes, title: filterLabels.SCHEME, }, @@ -83,8 +87,8 @@ export const FilterGroupComponent: React.FC = ({ onFilterFieldChange, fieldName: 'tags', id: 'tags', - disabled: tags.length === 0, - items: tags, + disabled: isDisabled(tags), + items: tags ?? [], selectedItems: selectedTags, title: filterLabels.TAGS, }, diff --git a/x-pack/test/functional/apps/uptime/locations.ts b/x-pack/test/functional/apps/uptime/locations.ts index eb5a642c8d69d..6bfa19c6ef578 100644 --- a/x-pack/test/functional/apps/uptime/locations.ts +++ b/x-pack/test/functional/apps/uptime/locations.ts @@ -38,8 +38,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await makeChecksWithStatus(es, LessAvailMonitor, 5, 2, 10000, {}, 'down'); }; - // FLAKY: https://github.com/elastic/kibana/issues/85208 - describe.skip('Observer location', () => { + describe('Observer location', () => { const start = '~ 15 minutes ago'; const end = 'now'; From e2fc156bc55b9be2ccfa8715b4637ef8401fb4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Fri, 15 Jan 2021 17:03:56 +0100 Subject: [PATCH 13/38] [APM] Extracting custom link flaky test. (#88455) * extracting the flaky test to another file * extracting the flaky test to another file --- .../DeleteButton.test.tsx | 29 +++++++++++++++++ .../CustomizeUI/CustomLink/index.test.tsx | 32 +------------------ 2 files changed, 30 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.test.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.test.tsx new file mode 100644 index 0000000000000..1dda204c8d6bc --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.test.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 { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { MockApmPluginContextWrapper } from '../../../../../../context/apm_plugin/mock_apm_plugin_context'; +import * as apmApi from '../../../../../../services/rest/createCallApmApi'; +import { DeleteButton } from './DeleteButton'; + +describe('Delete custom link', () => { + beforeAll(() => { + jest.spyOn(apmApi, 'callApmApi').mockResolvedValue({}); + }); + it('deletes a custom link', async () => { + const onDeleteMock = jest.fn(); + const { getByText } = render( + + + + ); + await act(async () => { + fireEvent.click(getByText('Delete')); + }); + expect(onDeleteMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx index 63dd486544124..c476686595b39 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx @@ -4,12 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - fireEvent, - render, - RenderResult, - waitFor, -} from '@testing-library/react'; +import { fireEvent, render, RenderResult } from '@testing-library/react'; import React from 'react'; import { act } from 'react-dom/test-utils'; import { CustomLinkOverview } from '.'; @@ -219,31 +214,6 @@ describe('CustomLink', () => { expect(saveCustomLinkSpy).toHaveBeenCalledTimes(1); }); - // FLAKY: https://github.com/elastic/kibana/issues/75106 - it.skip('deletes a custom link', async () => { - const mockContext = getMockAPMContext({ canSave: true }); - const component = render( - - - - - - ); - expect(component.queryByText('Create link')).not.toBeInTheDocument(); - const editButtons = component.getAllByLabelText('Edit'); - expect(editButtons.length).toEqual(2); - act(() => { - fireEvent.click(editButtons[0]); - }); - await waitFor(() => - expect(component.queryByText('Create link')).toBeInTheDocument() - ); - await act(async () => { - fireEvent.click(component.getByText('Delete')); - }); - expect(refetch).toHaveBeenCalled(); - }); - describe('Filters', () => { const addFilterField = (component: RenderResult, amount: number) => { for (let i = 1; i <= amount; i++) { From 329a5a3f21e8f6ef86e0139baf090f6ddc8834b4 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Fri, 15 Jan 2021 11:45:24 -0500 Subject: [PATCH 14/38] [App Search] Add configurable Filter and Sort modal to Documents View (#88066) --- .../__mocks__/shallow_useeffect.mock.ts | 13 +- .../customization_callout.test.tsx | 30 ++++ .../customization_callout.tsx | 48 ++++++ .../customization_modal.test.tsx | 75 +++++++++ .../search_experience/customization_modal.tsx | 156 ++++++++++++++++++ .../search_experience/search_experience.scss | 5 + .../search_experience.test.tsx | 51 ++++++ .../search_experience/search_experience.tsx | 30 +++- .../shared/use_local_storage/index.ts | 7 + .../use_local_storage.test.tsx | 69 ++++++++ .../use_local_storage/use_local_storage.ts | 56 +++++++ 11 files changed, 534 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_useeffect.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_useeffect.mock.ts index 1e3a45a83853c..ce4ec39505732 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_useeffect.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_useeffect.mock.ts @@ -4,15 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ +// Helper for calling the returned useEffect unmount handler +let mockUnmountHandler: () => void; +export const unmountHandler = () => mockUnmountHandler(); + jest.mock('react', () => ({ ...(jest.requireActual('react') as object), - useEffect: jest.fn((fn) => fn()), // Calls on mount/every update - use mount for more complex behavior + useEffect: jest.fn((fn) => { + mockUnmountHandler = fn(); + return mockUnmountHandler; + }), // Calls on mount/every update - use mount for more complex behavior })); -// Helper for calling the returned useEffect unmount handler -import { useEffect } from 'react'; -export const unmountHandler = () => (useEffect as jest.Mock).mock.calls[0][0]()(); - /** * Example usage within a component test using shallow(): * diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.test.tsx new file mode 100644 index 0000000000000..7864a6411ffa6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.test.tsx @@ -0,0 +1,30 @@ +/* + * 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 { shallow, ShallowWrapper } from 'enzyme'; +import { EuiButton } from '@elastic/eui'; + +import { CustomizationCallout } from './customization_callout'; + +describe('CustomizationCallout', () => { + let wrapper: ShallowWrapper; + const onClick = jest.fn(); + + beforeAll(() => { + wrapper = shallow(); + }); + + it('renders', () => { + expect(wrapper.isEmptyRender()).toBe(false); + }); + + it('calls onClick param when the Customize button is clicked', () => { + wrapper.find(EuiButton).simulate('click'); + expect(onClick).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.tsx new file mode 100644 index 0000000000000..a9bec6ced828f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_callout.tsx @@ -0,0 +1,48 @@ +/* + * 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 { EuiButton, EuiFlexGroup, EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; + +interface Props { + onClick(): void; +} + +export const CustomizationCallout: React.FC = ({ onClick }) => { + return ( + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.customizationCallout.message', + { + defaultMessage: + 'Did you know that you can customize your document search experience? Click "Customize" below to get started.', + } + )} + + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.customizationCallout.button', + { + defaultMessage: 'Customize', + } + )} + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.test.tsx new file mode 100644 index 0000000000000..94b2ab7cf3f04 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.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 { setMockValues, setMockActions } from '../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; + +import { CustomizationModal } from './customization_modal'; + +describe('CustomizationModal', () => { + const props = { + filterFields: ['field1', 'field2'], + sortFields: ['sortField1', 'sortField2'], + onClose: jest.fn(), + onSave: jest.fn(), + }; + + const values = { + engine: { + name: 'some-engine', + apiKey: '1234', + }, + }; + + const actions = { + setEngine: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(values); + setMockActions(actions); + }); + + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.isEmptyRender()).toBe(false); + }); + + it('when save is clicked, it calls onSave prop with selected filter and sort fields', () => { + const wrapper = shallow(); + wrapper.find(EuiButton).simulate('click'); + expect(props.onSave).toHaveBeenCalledWith({ + filterFields: ['field1', 'field2'], + sortFields: ['sortField1', 'sortField2'], + }); + }); + + it('when save is clicked, it calls onSave prop when with updated selections', () => { + const wrapper = shallow(); + + const sortFieldsDropdown = wrapper.find('[data-test-subj="sortFieldsDropdown"]'); + sortFieldsDropdown.simulate('change', [{ label: 'newSort1' }]); + + const filterFieldsDropdown = wrapper.find('[data-test-subj="filterFieldsDropdown"]'); + filterFieldsDropdown.simulate('change', [{ label: 'newField1' }]); + + wrapper.find(EuiButton).simulate('click'); + expect(props.onSave).toHaveBeenCalledWith({ + filterFields: ['newField1'], + sortFields: ['newSort1'], + }); + }); + + it('calls onClose when cancel is clicked', () => { + const wrapper = shallow(); + wrapper.find(EuiButtonEmpty).simulate('click'); + expect(props.onClose).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.tsx new file mode 100644 index 0000000000000..2b05ed7e78f64 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/customization_modal.tsx @@ -0,0 +1,156 @@ +/* + * 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, { useState, useMemo } from 'react'; + +import { + EuiButton, + EuiButtonEmpty, + EuiComboBox, + EuiForm, + EuiFormRow, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiOverlayMask, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useValues } from 'kea'; + +import { EngineLogic } from '../../engine'; + +interface Props { + filterFields: string[]; + sortFields: string[]; + onClose(): void; + onSave({ sortFields, filterFields }: { sortFields: string[]; filterFields: string[] }): void; +} + +const fieldNameToComboBoxOption = (fieldName: string) => ({ label: fieldName }); +const comboBoxOptionToFieldName = ({ label }: { label: string }) => label; + +export const CustomizationModal: React.FC = ({ + filterFields, + onClose, + onSave, + sortFields, +}) => { + const { engine } = useValues(EngineLogic); + + const [selectedFilterFields, setSelectedFilterFields] = useState( + filterFields.map(fieldNameToComboBoxOption) + ); + const [selectedSortFields, setSelectedSortFields] = useState( + sortFields.map(fieldNameToComboBoxOption) + ); + + const engineSchema = engine.schema || {}; + const selectableFilterFields = useMemo( + () => Object.keys(engineSchema).map(fieldNameToComboBoxOption), + [engineSchema] + ); + const selectableSortFields = useMemo( + () => Object.keys(engineSchema).map(fieldNameToComboBoxOption), + [engineSchema] + ); + + return ( + + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.customizationModal.title', + { + defaultMessage: 'Customize document search', + } + )} + + + + + + + + + + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.customizationModal.cancel', + { + defaultMessage: 'Cancel', + } + )} + + { + onSave({ + filterFields: selectedFilterFields.map(comboBoxOptionToFieldName), + sortFields: selectedSortFields.map(comboBoxOptionToFieldName), + }); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.customizationModal.save', + { + defaultMessage: 'Save', + } + )} + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss index 868a561a27873..ba9931dc90fdc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss @@ -20,4 +20,9 @@ .documentsSearchExperience__pagingInfo { flex-grow: 0; } + + .customizationCallout { + background-color: $euiPageBackgroundColor; + padding: $euiSizeL; + } } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx index 750d00311255c..250cd00943d7e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx @@ -7,11 +7,26 @@ import '../../../../__mocks__/kea.mock'; import { setMockValues } from '../../../../__mocks__'; import '../../../../__mocks__/enterprise_search_url.mock'; +const mockSetFields = jest.fn(); + +jest.mock('../../../../shared/use_local_storage', () => ({ + useLocalStorage: jest.fn(() => [ + { + filterFields: ['a', 'b', 'c'], + sortFields: ['d', 'c'], + }, + mockSetFields, + ]), +})); + import React from 'react'; // @ts-expect-error types are not available for this package yet import { SearchProvider } from '@elastic/react-search-ui'; import { shallow } from 'enzyme'; +import { CustomizationCallout } from './customization_callout'; +import { CustomizationModal } from './customization_modal'; + import { SearchExperience } from './search_experience'; describe('SearchExperience', () => { @@ -31,4 +46,40 @@ describe('SearchExperience', () => { const wrapper = shallow(); expect(wrapper.find(SearchProvider).length).toBe(1); }); + + describe('customization modal', () => { + it('has a customization modal which can be opened and closed', () => { + const wrapper = shallow(); + expect(wrapper.find(CustomizationModal).exists()).toBe(false); + + wrapper.find(CustomizationCallout).simulate('click'); + expect(wrapper.find(CustomizationModal).exists()).toBe(true); + + wrapper.find(CustomizationModal).prop('onClose')(); + expect(wrapper.find(CustomizationModal).exists()).toBe(false); + }); + + it('passes values from localStorage to the customization modal', () => { + const wrapper = shallow(); + wrapper.find(CustomizationCallout).simulate('click'); + expect(wrapper.find(CustomizationModal).prop('filterFields')).toEqual(['a', 'b', 'c']); + expect(wrapper.find(CustomizationModal).prop('sortFields')).toEqual(['d', 'c']); + }); + + it('updates selected fields in localStorage and closes modal on save', () => { + const wrapper = shallow(); + wrapper.find(CustomizationCallout).simulate('click'); + wrapper.find(CustomizationModal).prop('onSave')({ + filterFields: ['new', 'filters'], + sortFields: ['new', 'sorts'], + }); + + expect(mockSetFields).toHaveBeenCalledWith({ + filterFields: ['new', 'filters'], + sortFields: ['new', 'sorts'], + }); + + expect(wrapper.find(CustomizationModal).exists()).toBe(false); + }); + }); }); 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 1501efc589fc0..e80ab2e18b2d3 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 @@ -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 from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { useValues } from 'kea'; @@ -17,10 +17,13 @@ import './search_experience.scss'; import { EngineLogic } from '../../engine'; import { externalUrl } from '../../../../shared/enterprise_search_url'; +import { useLocalStorage } from '../../../../shared/use_local_storage'; import { SearchBoxView, SortingView } from './views'; import { SearchExperienceContent } from './search_experience_content'; import { buildSearchUIConfig } from './build_search_ui_config'; +import { CustomizationCallout } from './customization_callout'; +import { CustomizationModal } from './customization_modal'; const DEFAULT_SORT_OPTIONS = [ { @@ -43,6 +46,18 @@ export const SearchExperience: React.FC = () => { const { engine } = useValues(EngineLogic); const endpointBase = externalUrl.enterpriseSearchUrl; + const [showCustomizationModal, setShowCustomizationModal] = useState(false); + const openCustomizationModal = () => setShowCustomizationModal(true); + const closeCustomizationModal = () => setShowCustomizationModal(false); + + const [fields, setFields] = useLocalStorage( + `documents-search-experience-customization--${engine.name}`, + { + filterFields: [] as string[], + sortFields: [] as string[], + } + ); + // TODO const sortFieldsOptions = _flatten(fields.sortFields.map(fieldNameToSortOptions)) // we need to flatten this array since fieldNameToSortOptions returns an array of two sorting options const sortingOptions = [...DEFAULT_SORT_OPTIONS /* TODO ...sortFieldsOptions*/]; @@ -85,12 +100,25 @@ export const SearchExperience: React.FC = () => { sortOptions={sortingOptions} view={SortingView} /> + + + {showCustomizationModal && ( + { + setFields({ filterFields, sortFields }); + closeCustomizationModal(); + }} + /> + )}
); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/index.ts new file mode 100644 index 0000000000000..8c75ca9ae43c8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { useLocalStorage } from './use_local_storage'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.test.tsx new file mode 100644 index 0000000000000..0b0edcdf86f6a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.test.tsx @@ -0,0 +1,69 @@ +/* + * 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 { shallow } from 'enzyme'; + +import { useLocalStorage } from './use_local_storage'; + +describe('useLocalStorage', () => { + const KEY = 'fields'; + + const TestComponent = () => { + const [fields, setFields] = useLocalStorage(KEY, { + options: ['foo', 'bar', 'baz'], + }); + return ( +
+
+ ); + }; + + beforeEach(() => { + global.localStorage.clear(); + jest.clearAllMocks(); + }); + + it('will read state from localStorage on init if values already exist', () => { + global.localStorage.setItem( + KEY, + JSON.stringify({ + options: ['some', 'old', 'values'], + }) + ); + const wrapper = shallow(); + expect(wrapper.text()).toBe('some, old, values'); + }); + + it('will ignore non-JSON values in localStorage', () => { + global.localStorage.setItem(KEY, 'blah blah blah'); + const wrapper = shallow(); + expect(wrapper.text()).toBe('foo, bar, baz'); + expect(global.localStorage.getItem(KEY)).toBe('{"options":["foo","bar","baz"]}'); + }); + + it('if will use provided default values if state does not already exist in localStorage', () => { + const wrapper = shallow(); + expect(wrapper.text()).toBe('foo, bar, baz'); + expect(global.localStorage.getItem(KEY)).toBe('{"options":["foo","bar","baz"]}'); + }); + + it('state can be updated with new values', () => { + const wrapper = shallow(); + wrapper.find('#change').simulate('click'); + expect(wrapper.text()).toBe('big, new, values'); + expect(global.localStorage.getItem(KEY)).toBe('{"options":["big","new","values"]}'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.ts b/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.ts new file mode 100644 index 0000000000000..11f5fdb5aa1d2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/use_local_storage/use_local_storage.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useState } from 'react'; + +/** + * A hook that works like `useState`, but persisted to localStorage. + * + * example: + * + * const [foo, setFoo] = useLocalStorage("foo", "bar"); + * + * console.log(foo) // "bar" + * setFoo("baz") + * console.log(foo) // "baz" + * + * // Navigate away from page and return + * + * const [foo, setFoo] = useLocalStorage("foo", "bar"); + * console.log(foo) // "baz" + */ +export const useLocalStorage = (key: string, defaultValue: Value): [Value, Function] => { + const saveToStorage = (value: Value) => window.localStorage.setItem(key, JSON.stringify(value)); + const removeFromStorage = () => window.localStorage.removeItem(key); + const getFromStorage = (): Value | undefined => { + const storedItem = window.localStorage.getItem(key); + if (!storedItem) return; + + let parsedItem; + try { + return JSON.parse(storedItem) as Value; + } catch (e) { + removeFromStorage(); + } + + return parsedItem; + }; + + const storedItem = getFromStorage(); + if (!storedItem) { + saveToStorage(defaultValue); + } + const toStore = storedItem || defaultValue; + + const [item, setItem] = useState(toStore); + + const saveItem = (value: Value) => { + saveToStorage(value); + setItem(value); + }; + + return [item, saveItem]; +}; From b36273693a928a9d4ab013f851d9a7ccbe963c78 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Fri, 15 Jan 2021 17:58:33 +0100 Subject: [PATCH 15/38] Functional tests - stabilize reporting API network policy test for cloud (#88456) This PR fixes a reporting API network policy test failure that occurred during cloud execution. --- .../reporting_and_security/network_policy.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts b/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts index a9a1b31072e7e..a4b2c005aa908 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts @@ -5,14 +5,13 @@ */ import expect from '@kbn/expect'; -import * as Rx from 'rxjs'; -import { filter, first, map, switchMap, timeout } from 'rxjs/operators'; import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const reportingAPI = getService('reportingAPI'); + const retry = getService('retry'); const supertest = getService('supertest'); const archive = 'reporting/canvas_disallowed_url'; @@ -35,17 +34,10 @@ export default function ({ getService }: FtrProviderContext) { ); // Retry the download URL until a "failed" response status is returned - const fails$: Rx.Observable = Rx.interval(100).pipe( - switchMap(() => supertest.get(downloadPath).then((response) => response.body)), - filter(({ statusCode }) => statusCode === 500), - map(({ message }) => message), - first(), - timeout(120000) - ); - - const reportFailed = await fails$.toPromise(); - - expect(reportFailed).to.match(/Reporting generation failed: Error:/); + await retry.tryForTime(120000, async () => { + const { body } = await supertest.get(downloadPath).expect(500); + expect(body.message).to.match(/Reporting generation failed: Error:/); + }); }); }); } From c0a833d8ea1957d260e33e5047ca85104a571a3a Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Fri, 15 Jan 2021 16:59:31 +0000 Subject: [PATCH 16/38] [ML] Adds a11y tests for ML plugin (#88323) * [ML] Adds a11y tests for ML plugin * [ML] Edits to a11y tests following review --- .../components/data_grid/data_grid.tsx | 2 +- .../multi_select_picker.tsx | 2 +- .../additional_section/additional_section.tsx | 2 +- x-pack/plugins/ml/readme.md | 81 ++-- x-pack/test/accessibility/apps/ml.ts | 351 ++++++++++++++++++ x-pack/test/accessibility/config.ts | 1 + .../ml/data_frame_analytics_creation.ts | 2 +- .../functional/services/transform/wizard.ts | 2 +- 8 files changed, 401 insertions(+), 42 deletions(-) create mode 100644 x-pack/test/accessibility/apps/ml.ts diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx index f18610561ad2c..c499d1153a238 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx @@ -309,7 +309,7 @@ export const DataGrid: FC = memo( })} > = ({ additionalExpanded, setAdditional { + const esArchiver = getService('esArchiver'); + + before(async () => { + await ml.securityCommon.createMlRoles(); + await ml.securityCommon.createMlUsers(); + }); + + after(async () => { + await ml.securityCommon.cleanMlUsers(); + await ml.securityCommon.cleanMlRoles(); + }); + + describe('for user with full ML access', function () { + before(async () => { + await ml.securityUI.loginAsMlPowerUser(); + await ml.api.cleanMlIndices(); + }); + + after(async () => { + await ml.securityUI.logout(); + }); + + describe('with no data loaded', function () { + it('overview page', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToOverview(); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection page', async () => { + await ml.navigation.navigateToAnomalyDetection(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics page', async () => { + await ml.navigation.navigateToDataFrameAnalytics(); + await a11y.testAppSnapshot(); + }); + + it('settings page', async () => { + await ml.navigation.navigateToSettings(); + await a11y.testAppSnapshot(); + }); + }); + + describe('with data loaded', function () { + const adJobId = 'fq_single_a11y'; + const dfaOutlierJobId = 'iph_outlier_a11y'; + const calendarId = 'calendar_a11y'; + const eventDescription = 'calendar_event_a11y'; + const filterId = 'filter_a11y'; + const filterItems = ['filter_item_a11y']; + const fqIndexPattern = 'ft_farequote'; + const ecIndexPattern = 'ft_module_sample_ecommerce'; + const ihpIndexPattern = 'ft_ihp_outlier'; + const ecExpectedTotalCount = '287'; + + const adJobAggAndFieldIdentifier = 'Mean(responsetime)'; + const adJobBucketSpan = '30m'; + const adSingleMetricJobId = `fq_single_a11y_${Date.now()}`; + + const dfaJobType = 'outlier_detection'; + const dfaJobId = `ihp_ally_${Date.now()}`; + + const uploadFilePath = path.join( + __dirname, + '..', + '..', + 'functional', + 'apps', + 'ml', + 'data_visualizer', + 'files_to_import', + 'artificial_server_log' + ); + + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await esArchiver.loadIfNeeded('ml/ihp_outlier'); + await esArchiver.loadIfNeeded('ml/module_sample_ecommerce'); + await ml.testResources.createIndexPatternIfNeeded(fqIndexPattern, '@timestamp'); + await ml.testResources.createIndexPatternIfNeeded(ihpIndexPattern, '@timestamp'); + await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + await ml.api.createAndRunAnomalyDetectionLookbackJob( + ml.commonConfig.getADFqMultiMetricJobConfig(adJobId), + ml.commonConfig.getADFqDatafeedConfig(adJobId) + ); + + await ml.api.createAndRunDFAJob( + ml.commonConfig.getDFAIhpOutlierDetectionJobConfig(dfaOutlierJobId) + ); + + await ml.api.createCalendar(calendarId, { + calendar_id: calendarId, + job_ids: [], + description: 'Test calendar', + }); + await ml.api.createCalendarEvents(calendarId, [ + { + description: eventDescription, + start_time: 1513641600000, + end_time: 1513728000000, + }, + ]); + + await ml.api.createFilter(filterId, { + description: 'Test filter list', + items: filterItems, + }); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.api.deleteIndices(`user-${dfaOutlierJobId}`); + await ml.api.deleteCalendar(calendarId); + await ml.api.deleteFilter(filterId); + + await ml.testResources.deleteIndexPatternByTitle(fqIndexPattern); + await ml.testResources.deleteIndexPatternByTitle(ihpIndexPattern); + await ml.testResources.deleteIndexPatternByTitle(ecIndexPattern); + await esArchiver.unload('ml/farequote'); + await esArchiver.unload('ml/ihp_outlier'); + await esArchiver.unload('ml/module_sample_ecommerce'); + await ml.testResources.resetKibanaTimeZone(); + }); + + it('overview page', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToOverview(); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection jobs list page', async () => { + await ml.navigation.navigateToAnomalyDetection(); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection create job select index pattern page', async () => { + await ml.jobManagement.navigateToNewJobSourceSelection(); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection create job select type page', async () => { + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexPattern); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection create single metric job time range step', async () => { + await ml.jobTypeSelection.selectSingleMetricJob(); + await ml.testExecution.logTestStep('job creation set the time range'); + await ml.jobWizardCommon.clickUseFullDataButton( + 'Feb 7, 2016 @ 00:00:00.000', + 'Feb 11, 2016 @ 23:59:54.000' + ); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection create single metric job pick fields step', async () => { + await ml.jobWizardCommon.advanceToPickFieldsSection(); + await ml.testExecution.logTestStep('job creation selects field and aggregation'); + await ml.jobWizardCommon.selectAggAndField(adJobAggAndFieldIdentifier, true); + await ml.testExecution.logTestStep('job creation inputs the bucket span'); + await ml.jobWizardCommon.setBucketSpan(adJobBucketSpan); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection create single metric job details step', async () => { + await ml.jobWizardCommon.advanceToJobDetailsSection(); + await ml.testExecution.logTestStep('job creation inputs the job id'); + await ml.jobWizardCommon.setJobId(adSingleMetricJobId); + await ml.testExecution.logTestStep('job creation opens the additional settings section'); + await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen(); + await ml.testExecution.logTestStep('job creation opens the advanced section'); + await ml.jobWizardCommon.ensureAdvancedSectionOpen(); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection create single metric job validation step', async () => { + await ml.jobWizardCommon.advanceToValidationSection(); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection create single metric job summary step', async () => { + await ml.jobWizardCommon.advanceToSummarySection(); + await a11y.testAppSnapshot(); + }); + + it('anomaly detection Single Metric Viewer page', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToAnomalyDetection(); + await ml.jobTable.clickOpenJobInSingleMetricViewerButton(adJobId); + await ml.commonUI.waitForMlLoadingIndicatorToDisappear(); + + await ml.testExecution.logTestStep('should input the airline entity value'); + await ml.singleMetricViewer.assertEntityInputExist('airline'); + await ml.singleMetricViewer.assertEntityInputSelection('airline', []); + await ml.singleMetricViewer.selectEntityValue('airline', 'AAL'); + + await a11y.testAppSnapshot(); + }); + + it.skip('anomaly detection Anomaly Explorer page', async () => { + // Skip test until the dots used in the Elastic chart legend no longer have duplicate ids + // see https://github.com/elastic/elastic-charts/issues/970 + await ml.singleMetricViewer.openAnomalyExplorer(); + await ml.commonUI.waitForMlLoadingIndicatorToDisappear(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics page', async () => { + await ml.navigation.navigateToDataFrameAnalytics(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics outlier job exploration page', async () => { + await ml.dataFrameAnalyticsTable.openResultsView(dfaOutlierJobId); + await ml.dataFrameAnalyticsResults.assertOutlierTablePanelExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics create job select index pattern modal', async () => { + await ml.navigation.navigateToDataFrameAnalytics(); + await ml.dataFrameAnalytics.startAnalyticsCreation(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics create job configuration step', async () => { + await ml.testExecution.logTestStep( + 'job creation selects the source data and loads the DFA job wizard page' + ); + await ml.jobSourceSelection.selectSourceForAnalyticsJob(ihpIndexPattern); + await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics create job configuration step for outlier job', async () => { + await ml.testExecution.logTestStep('selects the outlier job type'); + await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists(); + await ml.dataFrameAnalyticsCreation.selectJobType(dfaJobType); + await ml.testExecution.logTestStep('displays the source data preview'); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); + await ml.testExecution.logTestStep('enables the source data preview histogram charts'); + await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics create job additional options step for outlier job', async () => { + await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep(); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics create job additional options step for outlier job', async () => { + await ml.dataFrameAnalyticsCreation.continueToDetailsStep(); + await ml.dataFrameAnalyticsCreation.setJobId(dfaJobId); + await a11y.testAppSnapshot(); + }); + + it('data frame analytics create job create step for outlier job', async () => { + await ml.dataFrameAnalyticsCreation.continueToCreateStep(); + await a11y.testAppSnapshot(); + }); + + it('settings page', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToSettings(); + await a11y.testAppSnapshot(); + }); + + it('calendar management page', async () => { + await ml.settings.navigateToCalendarManagement(); + await a11y.testAppSnapshot(); + }); + + it('edit calendar page', async () => { + await ml.settingsCalendar.openCalendarEditForm(calendarId); + await a11y.testAppSnapshot(); + }); + + it('filter list management page', async () => { + await ml.navigation.navigateToSettings(); + await ml.settings.navigateToFilterListsManagement(); + await a11y.testAppSnapshot(); + }); + + it('edit filter list page', async () => { + await ml.settingsFilterList.openFilterListEditForm(filterId); + await a11y.testAppSnapshot(); + }); + + it('data visualizer selector page', async () => { + await ml.navigation.navigateToDataVisualizer(); + await a11y.testAppSnapshot(); + }); + + it('index data visualizer select index pattern page', async () => { + await ml.dataVisualizer.navigateToIndexPatternSelection(); + await a11y.testAppSnapshot(); + }); + + it('index data visualizer page for selected index', async () => { + await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(ecIndexPattern); + + await ml.testExecution.logTestStep('should display the time range step'); + await ml.dataVisualizerIndexBased.assertTimeRangeSelectorSectionExists(); + + await ml.testExecution.logTestStep('should load data for full time range'); + await ml.dataVisualizerIndexBased.clickUseFullDataButton(ecExpectedTotalCount); + await a11y.testAppSnapshot(); + }); + + it('file data visualizer select file page', async () => { + await ml.navigation.navigateToDataVisualizer(); + await ml.dataVisualizer.navigateToFileUpload(); + await a11y.testAppSnapshot(); + }); + + it('file data visualizer file details page', async () => { + await ml.testExecution.logTestStep( + 'should select a file and load visualizer result page' + ); + await ml.dataVisualizerFileBased.selectFile(uploadFilePath); + await a11y.testAppSnapshot(); + }); + + it('file data visualizer import data page', async () => { + await ml.dataVisualizerFileBased.navigateToFileImport(); + await a11y.testAppSnapshot(); + }); + }); + }); + }); +} diff --git a/x-pack/test/accessibility/config.ts b/x-pack/test/accessibility/config.ts index 40acd461ab6a1..070e2c3a61f2b 100644 --- a/x-pack/test/accessibility/config.ts +++ b/x-pack/test/accessibility/config.ts @@ -27,6 +27,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('./apps/roles'), require.resolve('./apps/kibana_overview'), require.resolve('./apps/ingest_node_pipelines'), + require.resolve('./apps/ml'), ], pageObjects, diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 71d68b16c4037..062d314084112 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -125,7 +125,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( const actualCheckState = (await testSubjects.getAttribute( 'mlAnalyticsCreationDataGridHistogramButton', - 'aria-checked' + 'aria-pressed' )) === 'true'; expect(actualCheckState).to.eql( expectedCheckState, diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 1bfe3284c9045..97ed179d3ee94 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -175,7 +175,7 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) { const actualCheckState = (await testSubjects.getAttribute( 'transformIndexPreviewHistogramButton', - 'aria-checked' + 'aria-pressed' )) === 'true'; expect(actualCheckState).to.eql( expectedCheckState, From d2de5d03835b5b257aada5f111a9bae14d59479f Mon Sep 17 00:00:00 2001 From: Constance Date: Fri, 15 Jan 2021 09:26:07 -0800 Subject: [PATCH 17/38] [Enterprise Search] Small test helper for expected async errors (#88422) * Add small helper to reduce boilerplate on async tests expected to throw - Also hopefully a bit more descriptive on what's happening * Update all test files using the previous try/catch do nothing blocks to use new helper --- .../__mocks__/expected_async_error.ts | 20 ++++++ .../public/applications/__mocks__/index.ts | 2 + .../analytics/analytics_logic.test.ts | 18 ++--- .../credentials/credentials_logic.test.ts | 35 ++++----- .../documents/document_detail_logic.test.ts | 21 ++---- .../components/engine/engine_logic.test.ts | 12 ++-- .../engine_overview_logic.test.ts | 12 ++-- .../log_retention/log_retention_logic.test.ts | 15 +--- .../indexing_status_logic.test.ts | 9 ++- .../add_source/add_source_logic.test.ts | 46 ++++-------- .../views/groups/group_logic.test.ts | 72 ++++++++----------- .../views/groups/groups_logic.test.ts | 37 ++++------ 12 files changed, 121 insertions(+), 178 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/__mocks__/expected_async_error.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/expected_async_error.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/expected_async_error.ts new file mode 100644 index 0000000000000..e8fc8ef0c1506 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/expected_async_error.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const expectedAsyncError = async (promise: Promise) => { + let expectedError: unknown; + + try { + await promise; + } catch (e) { + // Silence expected errors from being reported by tests + + // Pass back expected error to optionally assert on + expectedError = e; + } + + return expectedError; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts index 57f4a9f66b987..85b2821cd40ee 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts @@ -23,3 +23,5 @@ export { mountWithIntl } from './mount_with_i18n.mock'; export { shallowWithIntl } from './shallow_with_i18n.mock'; export { rerender } from './enzyme_rerender.mock'; // Note: shallow_useeffect must be imported directly as a file + +export { expectedAsyncError } from './expected_async_error'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts index 62e241df01361..7be811b7b27db 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; +import { LogicMounter, expectedAsyncError } from '../../../__mocks__'; jest.mock('../../../shared/kibana', () => ({ KibanaLogic: { values: { history: { location: { search: '' } } } }, @@ -215,12 +215,8 @@ describe('AnalyticsLogic', () => { mount(); jest.spyOn(AnalyticsLogic.actions, 'onAnalyticsUnavailable'); - try { - AnalyticsLogic.actions.loadAnalyticsData(); - await promise; - } catch { - // Do nothing - } + AnalyticsLogic.actions.loadAnalyticsData(); + await expectedAsyncError(promise); expect(flashAPIErrors).toHaveBeenCalledWith('error'); expect(AnalyticsLogic.actions.onAnalyticsUnavailable).toHaveBeenCalled(); @@ -293,12 +289,8 @@ describe('AnalyticsLogic', () => { mount(); jest.spyOn(AnalyticsLogic.actions, 'onAnalyticsUnavailable'); - try { - AnalyticsLogic.actions.loadQueryData('some-query'); - await promise; - } catch { - // Do nothing - } + AnalyticsLogic.actions.loadQueryData('some-query'); + await expectedAsyncError(promise); expect(flashAPIErrors).toHaveBeenCalledWith('error'); expect(AnalyticsLogic.actions.onAnalyticsUnavailable).toHaveBeenCalled(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts index 48be2b0ae8dfd..b33d1eba572ec 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; +import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; -import { mockHttpValues } from '../../../__mocks__'; jest.mock('../../../shared/http', () => ({ HttpLogic: { values: mockHttpValues }, })); @@ -1093,11 +1092,9 @@ describe('CredentialsLogic', () => { http.get.mockReturnValue(promise); CredentialsLogic.actions.fetchCredentials(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); }); }); @@ -1124,11 +1121,9 @@ describe('CredentialsLogic', () => { http.get.mockReturnValue(promise); CredentialsLogic.actions.fetchDetails(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); }); }); @@ -1154,11 +1149,9 @@ describe('CredentialsLogic', () => { http.delete.mockReturnValue(promise); CredentialsLogic.actions.deleteApiKey(tokenName); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); }); }); @@ -1218,11 +1211,9 @@ describe('CredentialsLogic', () => { http.post.mockReturnValue(promise); CredentialsLogic.actions.onApiTokenChange(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); }); describe('token type data', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts index fe735f70247c6..015a9abd5405c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; +import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; -import { mockHttpValues } from '../../../__mocks__'; jest.mock('../../../shared/http', () => ({ HttpLogic: { values: mockHttpValues }, })); @@ -81,12 +80,9 @@ describe('DocumentDetailLogic', () => { const promise = Promise.reject('An error occurred'); http.get.mockReturnValue(promise); - try { - DocumentDetailLogic.actions.getDocumentDetails('1'); - await promise; - } catch { - // Do nothing - } + DocumentDetailLogic.actions.getDocumentDetails('1'); + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('An error occurred', { isQueued: true }); expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith('/engines/engine1/documents'); }); @@ -134,12 +130,9 @@ describe('DocumentDetailLogic', () => { promise = Promise.reject('An error occured'); http.delete.mockReturnValue(promise); - try { - DocumentDetailLogic.actions.deleteDocument('1'); - await promise; - } catch { - // Do nothing - } + DocumentDetailLogic.actions.deleteDocument('1'); + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts index 094260b6df095..8d855bebdb20f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; +import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; -import { mockHttpValues } from '../../../__mocks__'; jest.mock('../../../shared/http', () => ({ HttpLogic: { values: mockHttpValues }, })); @@ -193,12 +192,9 @@ describe('EngineLogic', () => { const promise = Promise.reject('An error occured'); http.get.mockReturnValue(promise); - try { - EngineLogic.actions.initializeEngine(); - await promise; - } catch { - // Do nothing - } + EngineLogic.actions.initializeEngine(); + await expectedAsyncError(promise); + expect(EngineLogic.actions.setEngineNotFound).toHaveBeenCalledWith(true); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts index 61319245bba03..46d5a7b6608a5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; +import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; -import { mockHttpValues } from '../../../__mocks__'; jest.mock('../../../shared/http', () => ({ HttpLogic: { values: mockHttpValues }, })); @@ -106,12 +105,9 @@ describe('EngineOverviewLogic', () => { const promise = Promise.reject('An error occurred'); http.get.mockReturnValue(promise); - try { - EngineOverviewLogic.actions.pollForOverviewMetrics(); - await promise; - } catch { - // Do nothing - } + EngineOverviewLogic.actions.pollForOverviewMetrics(); + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('An error occurred'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts index 004e5e9752d97..b1920b6ddea8a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; +import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; -import { mockHttpValues } from '../../../__mocks__'; jest.mock('../../../shared/http', () => ({ HttpLogic: { values: mockHttpValues }, })); @@ -232,12 +231,8 @@ describe('LogRetentionLogic', () => { http.put.mockReturnValue(promise); LogRetentionLogic.actions.saveLogRetention(LogRetentionOptions.Analytics, true); + await expectedAsyncError(promise); - try { - await promise; - } catch { - // Do nothing - } expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); expect(LogRetentionLogic.actions.clearLogRetentionUpdating).toHaveBeenCalled(); }); @@ -305,12 +300,8 @@ describe('LogRetentionLogic', () => { http.get.mockReturnValue(promise); LogRetentionLogic.actions.fetchLogRetention(); + await expectedAsyncError(promise); - try { - await promise; - } catch { - // Do nothing - } expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); expect(LogRetentionLogic.actions.clearLogRetentionUpdating).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts index 9fa5fe0f84bab..abddb96275c6f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts @@ -6,6 +6,8 @@ import { resetContext } from 'kea'; +import { expectedAsyncError } from '../../__mocks__'; + jest.mock('../http', () => ({ HttpLogic: { values: { http: { get: jest.fn() } }, @@ -82,11 +84,8 @@ describe('IndexingStatusLogic', () => { IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete }); jest.advanceTimersByTime(TIMEOUT); - try { - await promise; - } catch { - // Do nothing - } + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); }); 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 084c3d1fb9c4f..97f08e294be27 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 @@ -5,7 +5,9 @@ */ import { resetContext } from 'kea'; -import { mockHttpValues } from '../../../../../__mocks__'; + +import { mockHttpValues, expectedAsyncError } from '../../../../../__mocks__'; + jest.mock('../../../../../shared/http', () => ({ HttpLogic: { values: { http: mockHttpValues.http }, @@ -296,11 +298,8 @@ describe('AddSourceLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); AddSourceLogic.actions.getSourceConfigData('github'); - try { - await promise; - } catch { - // Do nothing - } + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -345,11 +344,8 @@ describe('AddSourceLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); AddSourceLogic.actions.getSourceConnectData('github', successCallback); - try { - await promise; - } catch { - // Do nothing - } + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -377,11 +373,8 @@ describe('AddSourceLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); AddSourceLogic.actions.getSourceReConnectData('github'); - try { - await promise; - } catch { - // Do nothing - } + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -409,11 +402,8 @@ describe('AddSourceLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); AddSourceLogic.actions.getPreContentSourceConfigData('123'); - try { - await promise; - } catch { - // Do nothing - } + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -485,11 +475,8 @@ describe('AddSourceLogic', () => { (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); AddSourceLogic.actions.saveSourceConfig(true); - try { - await promise; - } catch { - // Do nothing - } + await expectedAsyncError(promise); + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -550,11 +537,8 @@ describe('AddSourceLogic', () => { (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback); - try { - await promise; - } catch { - // Do nothing - } + await expectedAsyncError(promise); + expect(errorCallback).toHaveBeenCalled(); expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts index b4724c8ed3f0e..4de524fd9ac8e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts @@ -6,6 +6,8 @@ import { resetContext } from 'kea'; +import { expectedAsyncError } from '../../../__mocks__'; + jest.mock('../../../shared/http', () => ({ HttpLogic: { values: { http: { get: jest.fn(), post: jest.fn(), put: jest.fn(), delete: jest.fn() } }, @@ -253,16 +255,13 @@ describe('GroupLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); GroupLogic.actions.initializeGroup(sourceIds[0]); + await expectedAsyncError(promise); - try { - await promise; - } catch { - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); - expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ - type: 'error', - message: 'Unable to find group with ID: "123".', - }); - } + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ + type: 'error', + message: 'Unable to find group with ID: "123".', + }); }); it('handles non-404 error', async () => { @@ -270,16 +269,13 @@ describe('GroupLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); GroupLogic.actions.initializeGroup(sourceIds[0]); + await expectedAsyncError(promise); - try { - await promise; - } catch { - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); - expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ - type: 'error', - message: 'this is an error', - }); - } + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ + type: 'error', + message: 'this is an error', + }); }); }); @@ -308,11 +304,9 @@ describe('GroupLogic', () => { (HttpLogic.values.http.delete as jest.Mock).mockReturnValue(promise); GroupLogic.actions.deleteGroup(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -343,11 +337,9 @@ describe('GroupLogic', () => { (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); GroupLogic.actions.updateGroupName(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -381,11 +373,9 @@ describe('GroupLogic', () => { (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); GroupLogic.actions.saveGroupSources(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -418,11 +408,9 @@ describe('GroupLogic', () => { (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); GroupLogic.actions.saveGroupUsers(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -463,11 +451,9 @@ describe('GroupLogic', () => { (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); GroupLogic.actions.saveGroupSourcePrioritization(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts index 18206573a3978..d046863fd388c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -6,6 +6,8 @@ import { resetContext } from 'kea'; +import { expectedAsyncError } from '../../../__mocks__'; + jest.mock('../../../shared/http', () => ({ HttpLogic: { values: { http: { get: jest.fn(), post: jest.fn() } }, @@ -240,11 +242,9 @@ describe('GroupsLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); GroupsLogic.actions.initializeGroups(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -304,13 +304,10 @@ describe('GroupsLogic', () => { (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); GroupsLogic.actions.getSearchResults(); - try { - await promise; - } catch { - // Account for `breakpoint` that debounces filter value. - await delay(); - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + await delay(); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -333,11 +330,9 @@ describe('GroupsLogic', () => { (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); GroupsLogic.actions.fetchGroupUsers('123'); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); @@ -363,11 +358,9 @@ describe('GroupsLogic', () => { (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); GroupsLogic.actions.saveNewGroup(); - try { - await promise; - } catch { - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); - } + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); From 51ba94d3e5616d86a21128088ba9cbfa20e5162d Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 15 Jan 2021 11:52:29 -0600 Subject: [PATCH 18/38] [dev] Replace sass-lint with stylelint (#86177) Co-authored-by: Tyler Smalley Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dave Snider --- .../checks/{sasslint.sh => stylelint.sh} | 4 +- .prettierignore | 2 + .sass-lint.yml | 87 -- .stylelintignore | 3 + .stylelintrc | 84 ++ .teamcity/src/builds/Lint.kt | 6 +- package.json | 7 +- .../mock_repo/plugins/bar/public/index.scss | 1 + .../bar/public/legacy/_other_styles.scss | 1 + .../plugins/bar/public/legacy/styles.scss | 2 +- .../basic_optimization.test.ts.snap | 2 +- scripts/{sasslint.js => stylelint.js} | 2 +- src/core/public/core_app/styles/_mixins.scss | 6 +- src/dev/run_precommit_hook.js | 4 +- src/dev/{run_sasslint.js => run_stylelint.js} | 16 +- src/dev/{sasslint => stylelint}/index.js | 0 src/dev/{sasslint => stylelint}/lint_files.js | 41 +- .../pick_files_to_lint.js | 10 +- .../management_app/_advanced_settings.scss | 8 +- .../static/components/legend_toggle.scss | 1 - .../embeddable/grid/_dashboard_grid.scss | 4 +- .../embeddable/panel/_dashboard_panel.scss | 2 +- .../top_nav/panel_toolbar/panel_toolbar.scss | 2 +- .../ui/filter_bar/_global_filter_group.scss | 2 +- .../ui/filter_bar/_global_filter_item.scss | 2 +- .../_shard_failure_modal.scss | 48 +- .../angular/doc_table/_doc_table.scss | 7 +- .../components/table_row/_details.scss | 1 - .../doc_table/components/table_row/_open.scss | 2 +- .../application/components/discover.scss | 2 +- .../components/doc_viewer/doc_viewer.scss | 2 +- .../public/lib/panel/_embeddable_panel.scss | 4 +- .../ace/_ui_ace_keyboard_mode.scss | 2 +- .../components/_solutions_section.scss | 7 +- .../public/font_awesome/font_awesome.scss | 16 +- .../public/paginate/_paginate.scss | 3 +- .../public/markdown/_markdown.scss | 28 +- .../public/map/_leaflet_overrides.scss | 13 +- src/plugins/navigation/public/index.scss | 2 +- src/plugins/timelion/public/_base.scss | 1 - .../timelion/public/directives/_form.scss | 3 +- .../timelion/public/directives/_index.scss | 1 - .../directives/_saved_object_finder.scss | 1 - .../public/directives/cells/_cells.scss | 1 - .../vis_default_editor/public/_default.scss | 2 +- .../vis_default_editor/public/_sidebar.scss | 6 +- .../vis_default_editor/public/index.scss | 2 +- .../public/markdown_vis.scss | 2 +- .../public/legacy/_table_vis.scss | 2 +- .../public/legacy/agg_table/_agg_table.scss | 2 +- .../public/application/_tvb_editor.scss | 1 - .../components/vis_types/_vis_types.scss | 4 +- .../vis_types/markdown/_markdown.scss | 2 - .../visualizations/views/_top_n.scss | 1 - .../public/components/vega_vis.scss | 2 +- .../public/vislib/lib/_index.scss | 1 - .../public/vislib/lib/layout/_layout.scss | 232 +++-- .../public/components/_visualization.scss | 8 +- .../public/embeddable/_embeddables.scss | 19 +- .../components/visualize_editor.scss | 4 +- .../components/visualize_listing.scss | 2 +- test/scripts/jenkins_unit.sh | 2 +- test/scripts/lint/sasslint.sh | 6 - test/scripts/lint/stylelint.sh | 6 + vars/tasks.groovy | 2 +- .../components/element_card/element_card.scss | 2 +- .../components/fullscreen/fullscreen.scss | 3 +- .../public/components/toolbar/tray/tray.scss | 1 - .../public/components/workpad/workpad.scss | 2 +- .../workpad_dropzone/workpad_dropzone.scss | 2 +- x-pack/plugins/canvas/public/style/main.scss | 1 - .../components/footer/footer.module.scss | 2 +- .../components/footer/scrubber.module.scss | 2 +- .../components/result/result_field.scss | 2 +- .../components/product_card/product_card.scss | 2 +- .../applications/shared/layout/layout.scss | 4 +- .../applications/shared/layout/side_nav.scss | 2 +- .../shared/truncate/truncated_content.scss | 2 +- .../shared/license_badge/license_badge.scss | 6 +- .../shared/user_icon/user_icon.scss | 10 +- .../display_settings/display_settings.scss | 16 +- .../workplace_search/views/groups/groups.scss | 2 +- .../views/overview/recent_activity.scss | 4 +- x-pack/plugins/graph/public/_main.scss | 2 +- .../public/angular/templates/_graph.scss | 13 +- .../public/angular/templates/_sidebar.scss | 4 +- .../field_manager/_field_editor.scss | 2 +- .../_graph_visualization.scss | 8 +- .../components/legacy_icon/_legacy_icon.scss | 2 +- .../venn_diagram/_venn_diagram.scss | 4 +- .../component_templates.scss | 1 - .../component_templates_list_item.scss | 4 +- .../component_templates_selector.scss | 8 +- .../components/mappings_editor/_index.scss | 88 +- .../fields/_field_list_item.scss | 2 +- .../index_management/public/index.scss | 2 +- x-pack/plugins/infra/public/index.scss | 1 - .../processors_tree/processors_tree.scss | 2 +- x-pack/plugins/lens/public/_mixins.scss | 8 +- .../workspace_panel/chart_switch.scss | 2 +- .../indexpattern_datasource/datapanel.scss | 2 - .../vector/components/color/_color_stops.scss | 1 - .../components/legend/_vector_legend.scss | 2 +- .../widget_overlay/_mixins.scss | 14 +- .../layer_toc/toc_entry/_toc_entry.scss | 1 - .../ml/public/application/_variables.scss | 10 +- .../anomalies_table/_anomalies_table.scss | 9 +- .../chart_tooltip/_chart_tooltip.scss | 2 +- .../components/controls/_controls.scss | 2 +- .../components/data_grid/column_chart.scss | 2 +- .../components/data_grid/data_grid.scss | 1 - .../components/entity_cell/entity_cell.scss | 2 +- .../influencers_list/_influencers_list.scss | 2 +- .../job_selector/_job_selector.scss | 22 +- .../components/rule_editor/_rule_editor.scss | 6 +- .../_classification_exploration.scss | 2 +- .../expanded_row_messages_pane.scss | 1 - .../_experimental_badge.scss | 2 +- .../fields_stats/_field_stats_card.scss | 6 +- .../fields_stats/_fields_stats.scss | 2 - .../results_view/_results_view.scss | 4 +- .../field_data_card/_field_data_card.scss | 2 +- .../datavisualizer/stats_datagrid/_index.scss | 13 +- .../field_data_expanded_row/_index.scss | 2 +- .../_number_content.scss | 1 - .../application/explorer/_explorer.scss | 2 +- .../explorer_charts/_explorer_chart.scss | 32 +- .../_explorer_charts_container.scss | 4 +- .../edit_job_flyout/_edit_job_flyout.scss | 2 +- .../components/job_details/_job_details.scss | 6 +- .../job_filter_bar/_job_filter_bar.scss | 2 +- .../components/job_group/_job_group.scss | 2 +- .../components/jobs_list/_jobs_list.scss | 12 +- .../multi_job_actions/_multi_job_actions.scss | 7 +- .../group_list/_group_list.scss | 14 +- .../new_group_input/_new_group_input.scss | 2 +- .../_time_range_selector.scss | 9 +- .../components/jobs_list_page/_buttons.scss | 4 +- .../jobs_list_page/_expanded_row.scss | 6 +- .../components/jobs_list_page/_stats_bar.scss | 1 - .../settings/calendars/edit/_edit.scss | 2 +- .../settings/filter_lists/_filter_lists.scss | 1 - .../settings/filter_lists/edit/_edit.scss | 2 +- .../_timeseriesexplorer.scss | 47 +- .../_timeseriesexplorer_annotations.scss | 6 +- .../components/app/news_feed/index.scss | 2 +- .../public/application/_hacks.scss | 2 +- .../public/application/_app.scss | 23 +- .../application/sections/home/_home.scss | 12 +- .../components/wizard/_wizard.scss | 2 +- yarn.lock | 857 +++++++++--------- 151 files changed, 1068 insertions(+), 1075 deletions(-) rename .ci/teamcity/checks/{sasslint.sh => stylelint.sh} (50%) create mode 100644 .prettierignore delete mode 100644 .sass-lint.yml create mode 100644 .stylelintignore create mode 100644 .stylelintrc rename scripts/{sasslint.js => stylelint.js} (95%) rename src/dev/{run_sasslint.js => run_stylelint.js} (66%) rename src/dev/{sasslint => stylelint}/index.js (100%) rename src/dev/{sasslint => stylelint}/lint_files.js (59%) rename src/dev/{sasslint => stylelint}/pick_files_to_lint.js (75%) delete mode 100755 test/scripts/lint/sasslint.sh create mode 100755 test/scripts/lint/stylelint.sh diff --git a/.ci/teamcity/checks/sasslint.sh b/.ci/teamcity/checks/stylelint.sh similarity index 50% rename from .ci/teamcity/checks/sasslint.sh rename to .ci/teamcity/checks/stylelint.sh index 45b90f6a8034e..f4e1da5027346 100755 --- a/.ci/teamcity/checks/sasslint.sh +++ b/.ci/teamcity/checks/stylelint.sh @@ -4,5 +4,5 @@ set -euo pipefail source "$(dirname "${0}")/../util.sh" -checks-reporter-with-killswitch "Lint: sasslint" \ - node scripts/sasslint +checks-reporter-with-killswitch "Lint: stylelint" \ + node scripts/stylelint diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000000..3802245b91e81 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# use stylelint +*.scss \ No newline at end of file diff --git a/.sass-lint.yml b/.sass-lint.yml deleted file mode 100644 index 85599750b0cb8..0000000000000 --- a/.sass-lint.yml +++ /dev/null @@ -1,87 +0,0 @@ -files: - include: - - 'src/legacy/core_plugins/metrics/**/*.s+(a|c)ss' - - 'src/plugins/index_pattern_management/**/*.s+(a|c)ss' - - 'src/plugins/timelion/**/*.s+(a|c)ss' - - 'src/plugins/vis_type_vislib/**/*.s+(a|c)ss' - - 'src/plugins/vis_type_vega/**/*.s+(a|c)ss' - - 'src/plugins/vis_type_xy/**/*.s+(a|c)ss' - - 'src/plugins/visualizations/public/wizard/**/*.s+(a|c)ss' - - 'x-pack/plugins/canvas/**/*.s+(a|c)ss' - - 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss' - - 'x-pack/plugins/lens/**/*.s+(a|c)ss' - - 'x-pack/plugins/cross_cluster_replication/**/*.s+(a|c)ss' - - 'x-pack/legacy/plugins/maps/**/*.s+(a|c)ss' - - 'x-pack/plugins/maps/**/*.s+(a|c)ss' - - 'x-pack/plugins/spaces/**/*.s+(a|c)ss' - - 'x-pack/plugins/security/**/*.s+(a|c)ss' - - 'x-pack/plugins/monitoring/**/*.s+(a|c)ss' - ignore: - - 'x-pack/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss' -rules: - quotes: - - 2 - - style: 'single' - # } else { style on one line, like our JS - brace-style: - - 2 - - style: '1tbs' - variable-name-format: - - 2 - - convention: 'camelcase' - # Needs regex, right now we ignore - class-name-format: 0 - # Order how you please - property-sort-order: 0 - hex-notation: - - 2 - - style: 'uppercase' - mixin-name-format: - - 2 - - allow-leading-underscore: false - convention: 'camelcase' - # Use none instead of 0 for no border - border-zero: - - 2 - - convention: 'none' - force-element-nesting: 0 - # something { not something{ - space-before-brace: - - 2 - force-pseudo-nesting: 0 - # 2 spaces for indentation - indentation: 2 - function-name-format: - - 2 - - allow-leading-underscore: false - convention: 'camelcase' - # This removes the need for ::hover - pseudo-element: 0 - # ($var / 2) rather than ($var/2) - space-around-operator: 2 - # We minify css, so this doesn't apply - no-css-comments: 0 - # We use _ (underscores) for import path that don't directly compile - clean-import-paths: 0 - # Allows input[type=search] - force-attribute-nesting: 0 - no-qualifying-elements: - - 2 - - # Allows input[type=search] - allow-element-with-attribute: 1 - # Files can end without a newline - final-newline: 0 - # We use some rare duplicate property values for browser variance - no-duplicate-properties: - - 2 - - exclude: - - 'font-size' - - 'word-break' - # Put a line-break between sections of CSS, but allow quicky one-liners for legibility - empty-line-between-blocks: - - 2 - - allow-single-line-rulesets: 1 - # Warns are nice for deprecations and development - no-warn: 0 - # Transition all is useful in certain situations and there's no recent info to suggest slowdown - no-transition-all: 0 diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 0000000000000..a48b3adfa3632 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,3 @@ +x-pack/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss +build +target diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000000000..29c1f4b552b48 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,84 @@ +plugins: + - stylelint-scss +rules: + color-no-invalid-hex: true + function-calc-no-invalid: true + string-no-newline: true + unit-no-unknown: true + property-no-unknown: + - true + # Used in css modules + - ignoreProperties: + - composes + block-no-empty: true + selector-pseudo-class-no-unknown: + - true + # Used in css modules + - ignorePseudoClasses: + - local + - global + selector-pseudo-element-no-unknown: true + media-feature-name-no-unknown: true + at-rule-no-unknown: + - true + # Sass related mixins + - ignoreAtRules: + - include + - mixin + - if + - else + - each + - extend + - function + - return + - for + comment-no-empty: true + no-duplicate-at-import-rules: true + no-duplicate-selectors: true + no-extra-semicolons: true + alpha-value-notation: number + color-named: never + length-zero-no-unit: true + no-eol-whitespace: true + max-empty-lines: 1 + # no-descending-specificity: true + number-leading-zero: never + color-hex-case: upper + string-quotes: single + indentation: 2 + declaration-block-no-duplicate-properties: + - true + # We use some rare duplicate property values for browser variance + - ignoreProperties: + - font-size + - word-break + - composes + - filter + - background + - width + - transition + - display + + # ($var / 2) rather than ($var/2) + function-calc-no-unspaced-operator: true + + # something { not something{ + block-opening-brace-space-before: always + + # TODO: # Allows input[type=search] + selector-no-qualifying-type: + - true + - ignore: + - attribute + + # Put a line-break between sections of CSS, but allow quicky one-liners for legibility + block-closing-brace-newline-after: + - always-multi-line + - ignoreAtRules: + - if + - else + + # camelCase mixin, function, and variable names + scss/at-mixin-pattern: "^[a-z][a-zA-Z0-9]+$" + scss/at-function-pattern: "^[a-z][a-zA-Z0-9]+$" + scss/dollar-variable-pattern: "^[a-z][a-zA-Z0-9]+$" diff --git a/.teamcity/src/builds/Lint.kt b/.teamcity/src/builds/Lint.kt index d02f1c9038aca..4a4bb8651a7c0 100644 --- a/.teamcity/src/builds/Lint.kt +++ b/.teamcity/src/builds/Lint.kt @@ -6,18 +6,18 @@ import kibanaAgent object Lint : BuildType({ name = "Lint" - description = "Executes Linting, such as eslint and sasslint" + description = "Executes Linting, such as eslint and stylelint" kibanaAgent(2) steps { script { - name = "Sasslint" + name = "Stylelint" scriptContent = """ #!/bin/bash - ./.ci/teamcity/checks/sasslint.sh + ./.ci/teamcity/checks/stylelint.sh """.trimIndent() } diff --git a/package.json b/package.json index 07984e5a237b0..7effbd0a5550a 100644 --- a/package.json +++ b/package.json @@ -51,9 +51,9 @@ "start": "node scripts/kibana --dev", "debug": "node --nolazy --inspect scripts/kibana --dev", "debug-break": "node --nolazy --inspect-brk scripts/kibana --dev", - "lint": "yarn run lint:es && yarn run lint:sass", + "lint": "yarn run lint:es && yarn run lint:style", "lint:es": "node scripts/eslint", - "lint:sass": "node scripts/sasslint", + "lint:style": "node scripts/stylelint", "makelogs": "node scripts/makelogs", "kbn:watch": "node scripts/kibana --dev --logging.json=false", "build:types": "rm -rf ./target/types && tsc --p tsconfig.types.json", @@ -793,7 +793,6 @@ "resize-observer-polyfill": "^1.5.0", "resolve": "^1.7.1", "rxjs-marbles": "^5.0.6", - "sass-lint": "^1.12.1", "sass-loader": "^8.0.2", "sass-resources-loader": "^2.0.1", "selenium-webdriver": "^4.0.0-alpha.7", @@ -805,6 +804,8 @@ "string-replace-loader": "^2.2.0", "strong-log-transformer": "^2.1.0", "style-loader": "^1.1.3", + "stylelint": "13.8.0", + "stylelint-scss": "^3.18.0", "superagent": "^3.8.2", "supertest": "^3.1.0", "supertest-as-promised": "^4.0.2", diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.scss b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.scss index 563d20e99ce82..9603185daf410 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.scss +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.scss @@ -1,3 +1,4 @@ body { + /* stylelint-disable-next-line color-named */ color: green; } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/_other_styles.scss b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/_other_styles.scss index 2c1b9562b9567..fae6b1e627661 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/_other_styles.scss +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/_other_styles.scss @@ -1,3 +1,4 @@ p { + /* stylelint-disable-next-line color-named */ background-color: rebeccapurple; } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/styles.scss b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/styles.scss index 7fa8383ec239c..89c5d0d7d98c1 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/styles.scss +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/legacy/styles.scss @@ -1,4 +1,4 @@ -@import "./other_styles.scss"; +@import './other_styles.scss'; body { width: $globalStyleConstant; diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index ddb19c8cdc3d7..4681057ad0998 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -99,7 +99,7 @@ OptimizerConfig { } `; -exports[`prepares assets for distribution: bar bundle 1`] = `"(function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module[\\"default\\"]}:function getModuleExports(){return module};__webpack_require__.d(getter,\\"a\\",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";return __webpack_require__(__webpack_require__.s=3)})([function(module,exports,__webpack_require__){\\"use strict\\";var isOldIE=function isOldIE(){var memo;return function memorize(){if(typeof memo===\\"undefined\\"){memo=Boolean(window&&document&&document.all&&!window.atob)}return memo}}();var getTarget=function getTarget(){var memo={};return function memorize(target){if(typeof memo[target]===\\"undefined\\"){var styleTarget=document.querySelector(target);if(window.HTMLIFrameElement&&styleTarget instanceof window.HTMLIFrameElement){try{styleTarget=styleTarget.contentDocument.head}catch(e){styleTarget=null}}memo[target]=styleTarget}return memo[target]}}();var stylesInDom=[];function getIndexByIdentifier(identifier){var result=-1;for(var i=0;i 0) { try { diff --git a/src/dev/run_sasslint.js b/src/dev/run_stylelint.js similarity index 66% rename from src/dev/run_sasslint.js rename to src/dev/run_stylelint.js index 8ee5c4d7a956d..060b322c12a22 100644 --- a/src/dev/run_sasslint.js +++ b/src/dev/run_stylelint.js @@ -18,11 +18,19 @@ */ import { resolve } from 'path'; +import { buildCLI } from 'stylelint/lib/cli'; -process.argv.push('--no-exit'); // don't exit after encountering a rule error -process.argv.push('--verbose'); // print results +const options = buildCLI(process.argv.slice(2)); + +const stylelintConfigPath = resolve(__dirname, '..', '..', '.stylelintrc'); +const stylelintIgnorePath = resolve(__dirname, '..', '..', '.stylelintignore'); + +if (!options.input.length) { + process.argv.push('**/*.s+(a|c)ss'); +} process.argv.push('--max-warnings', '0'); // return nonzero exit code on any warnings -process.argv.push('--config', resolve(__dirname, '..', '..', '.sass-lint.yml')); // configuration file +process.argv.push('--config', stylelintConfigPath); // configuration file +process.argv.push('--ignore-path', stylelintIgnorePath); // ignore file // common-js is required so that logic before this executes before loading sass-lint -require('sass-lint/bin/sass-lint'); +require('stylelint/bin/stylelint'); diff --git a/src/dev/sasslint/index.js b/src/dev/stylelint/index.js similarity index 100% rename from src/dev/sasslint/index.js rename to src/dev/stylelint/index.js diff --git a/src/dev/sasslint/lint_files.js b/src/dev/stylelint/lint_files.js similarity index 59% rename from src/dev/sasslint/lint_files.js rename to src/dev/stylelint/lint_files.js index 3a560b4a6ea1a..415cd8650c8f8 100644 --- a/src/dev/sasslint/lint_files.js +++ b/src/dev/stylelint/lint_files.js @@ -17,10 +17,16 @@ * under the License. */ -import sassLint from 'sass-lint'; +import stylelint from 'stylelint'; import path from 'path'; +import { safeLoad } from 'js-yaml'; +import fs from 'fs'; import { createFailError } from '@kbn/dev-utils'; +// load the include globs from .stylelintrc and convert them to regular expressions for filtering files +const stylelintPath = path.resolve(__dirname, '..', '..', '..', '.stylelintrc'); +const styleLintConfig = safeLoad(fs.readFileSync(stylelintPath)); + /** * Lints a list of files with eslint. eslint reports are written to the log * and a FailError is thrown when linting errors occur. @@ -29,28 +35,21 @@ import { createFailError } from '@kbn/dev-utils'; * @param {Array} files * @return {undefined} */ -export function lintFiles(log, files) { +export async function lintFiles(log, files) { const paths = files.map((file) => file.getRelativePath()); - const report = sassLint.lintFiles( - paths.join(', '), - {}, - path.resolve(__dirname, '..', '..', '..', '.sass-lint.yml') - ); - - const failTypes = Object.keys( - report.reduce((failTypes, reportEntry) => { - if (reportEntry.warningCount > 0) failTypes.warning = true; - if (reportEntry.errorCount > 0) failTypes.errors = true; - return failTypes; - }, {}) - ); + const options = { + files: paths, + config: styleLintConfig, + formatter: 'string', + ignorePath: path.resolve(__dirname, '..', '..', '..', '.stylelintignore'), + }; - if (!failTypes.length) { - log.success('[sasslint] %d files linted successfully', files.length); - return; + const report = await stylelint.lint(options); + if (report.errored) { + log.error(report.output); + throw createFailError('[stylelint] errors'); + } else { + log.success('[stylelint] %d files linted successfully', files.length); } - - log.error(sassLint.format(report)); - throw createFailError(`[sasslint] ${failTypes.join(' & ')}`); } diff --git a/src/dev/sasslint/pick_files_to_lint.js b/src/dev/stylelint/pick_files_to_lint.js similarity index 75% rename from src/dev/sasslint/pick_files_to_lint.js rename to src/dev/stylelint/pick_files_to_lint.js index 57c38d0069e06..77274763fa600 100644 --- a/src/dev/sasslint/pick_files_to_lint.js +++ b/src/dev/stylelint/pick_files_to_lint.js @@ -17,17 +17,9 @@ * under the License. */ -import fs from 'fs'; -import { safeLoad } from 'js-yaml'; import { makeRe } from 'minimatch'; -import path from 'path'; -// load the include globs from .sass-lint.yml and convert them to regular expressions for filtering files -const sassLintPath = path.resolve(__dirname, '..', '..', '..', '.sass-lint.yml'); -const sassLintConfig = safeLoad(fs.readFileSync(sassLintPath)); -const { - files: { include: includeGlobs }, -} = sassLintConfig; +const includeGlobs = ['**/*.s+(a|c)ss']; const includeRegex = includeGlobs.map((glob) => makeRe(glob)); function matchesInclude(file) { diff --git a/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss b/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss index fd26677e93894..76c181a9ec256 100644 --- a/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss +++ b/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss @@ -17,7 +17,7 @@ * under the License. */ - .mgtAdvancedSettings__field { +.mgtAdvancedSettings__field { + * { margin-top: $euiSize; } @@ -29,11 +29,11 @@ &--unsaved .mgtAdvancedSettings__fieldTitle { // Simulates a left side border without shifting content - box-shadow: -$euiSizeXS 0px $euiColorWarning; + box-shadow: -$euiSizeXS 0 $euiColorWarning; } &--invalid .mgtAdvancedSettings__fieldTitle { // Simulates a left side border without shifting content - box-shadow: -$euiSizeXS 0px $euiColorDanger; + box-shadow: -$euiSizeXS 0 $euiColorDanger; } @include internetExplorerOnly { @@ -57,7 +57,7 @@ .mgtAdvancedSettingsForm__unsavedCountMessage { // Simulates a left side border without shifting content - box-shadow: -$euiSizeXS 0px $euiColorWarning; + box-shadow: -$euiSizeXS 0 $euiColorWarning; padding-left: $euiSizeS; } diff --git a/src/plugins/charts/public/static/components/legend_toggle.scss b/src/plugins/charts/public/static/components/legend_toggle.scss index 7eb85a5e08ec0..3df799d4bcda4 100644 --- a/src/plugins/charts/public/static/components/legend_toggle.scss +++ b/src/plugins/charts/public/static/components/legend_toggle.scss @@ -17,4 +17,3 @@ top: 0; } } - diff --git a/src/plugins/dashboard/public/application/embeddable/grid/_dashboard_grid.scss b/src/plugins/dashboard/public/application/embeddable/grid/_dashboard_grid.scss index a205e611531b6..bf593bd89b42b 100644 --- a/src/plugins/dashboard/public/application/embeddable/grid/_dashboard_grid.scss +++ b/src/plugins/dashboard/public/application/embeddable/grid/_dashboard_grid.scss @@ -31,7 +31,7 @@ * 1. Need to override the react grid layout height when a single panel is expanded. Important is required because * otherwise the height is set inline. */ - .dshLayout-isMaximizedPanel { +.dshLayout-isMaximizedPanel { height: 100% !important; /* 1. */ width: 100%; position: absolute !important; /* 1 */ @@ -66,7 +66,7 @@ /** * Disable transitions from the library on each grid element. */ - transition: none; + transition: none; /** * Copy over and overwrite the fill color with EUI color mixin (for theming) */ diff --git a/src/plugins/dashboard/public/application/embeddable/panel/_dashboard_panel.scss b/src/plugins/dashboard/public/application/embeddable/panel/_dashboard_panel.scss index 48961110db48b..f2d017e54dc7b 100644 --- a/src/plugins/dashboard/public/application/embeddable/panel/_dashboard_panel.scss +++ b/src/plugins/dashboard/public/application/embeddable/panel/_dashboard_panel.scss @@ -8,7 +8,7 @@ // Adjust borders/etc... for non-spaced out and expanded panels .dshLayout-withoutMargins, -.dshDashboardGrid__item--expanded { +.dshDashboardGrid__item--expanded { .embPanel { box-shadow: none; border-radius: 0; diff --git a/src/plugins/dashboard/public/application/top_nav/panel_toolbar/panel_toolbar.scss b/src/plugins/dashboard/public/application/top_nav/panel_toolbar/panel_toolbar.scss index 196ae68e3ed17..9ad6a5257df3e 100644 --- a/src/plugins/dashboard/public/application/top_nav/panel_toolbar/panel_toolbar.scss +++ b/src/plugins/dashboard/public/application/top_nav/panel_toolbar/panel_toolbar.scss @@ -6,7 +6,7 @@ .panelToolbarButton { line-height: $euiButtonHeight; // Keeps alignment of text and chart icon background-color: $euiColorEmptyShade; - + // Lighten the border color for all states border-color: $euiBorderColor !important; // sass-lint:disable-line no-important } diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss index 731c9f4d7f18d..d45f7040e5739 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss @@ -1,6 +1,6 @@ // SASSTODO: Probably not the right file for this selector, but temporary until the files get re-organized .globalQueryBar { - padding: 0px $euiSizeS $euiSizeS $euiSizeS; + padding: 0 $euiSizeS $euiSizeS $euiSizeS; } .globalQueryBar:first-child { diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss index b79b7c038f9cc..652c8b0b35b42 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss @@ -26,7 +26,7 @@ .globalFilterItem-isDisabled { color: $euiColorDarkShade; - background-color: transparentize($euiColorLightShade, 0.5); + background-color: transparentize($euiColorLightShade, .5); text-decoration: line-through; font-weight: $euiFontWeightRegular; font-style: italic; diff --git a/src/plugins/data/public/ui/shard_failure_modal/_shard_failure_modal.scss b/src/plugins/data/public/ui/shard_failure_modal/_shard_failure_modal.scss index 6527289f8021f..ee3912098afae 100644 --- a/src/plugins/data/public/ui/shard_failure_modal/_shard_failure_modal.scss +++ b/src/plugins/data/public/ui/shard_failure_modal/_shard_failure_modal.scss @@ -1,41 +1,41 @@ // set width and height to fixed values to prevent resizing when you switch tabs .shardFailureModal { - min-height: 75vh; - width: 768px; + min-height: 75vh; + width: 768px; } .shardFailureModal__desc { - // set for IE11, since without it depending on the content the width of the list - // could be much higher than the available screenspace - max-width: 686px; + // set for IE11, since without it depending on the content the width of the list + // could be much higher than the available screenspace + max-width: 686px; } .shardFailureModal__descTitle { - width: 20% !important; - margin-top: $euiSizeS; + width: 20% !important; + margin-top: $euiSizeS; } .shardFailureModal__descValue { - width: 80% !important; - margin-top: $euiSizeS; + width: 80% !important; + margin-top: $euiSizeS; } .shardFailureModal__keyValueTitle { - padding-right: $euiSizeS; + padding-right: $euiSizeS; } @include euiBreakpoint('xs','s') { - .shardFailureModal__keyValueTitle { - display: block; - width: 100%; - } - - .shardFailureModal__descTitle { - display: block; - width: 100% !important; - } - - .shardFailureModal__descValue { - display: block; - width: 100% !important; - } + .shardFailureModal__keyValueTitle { + display: block; + width: 100%; + } + + .shardFailureModal__descTitle { + display: block; + width: 100% !important; + } + + .shardFailureModal__descValue { + display: block; + width: 100% !important; + } } \ No newline at end of file diff --git a/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss b/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss index 7d05171622e7b..c4bba1a8bbf2b 100644 --- a/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss +++ b/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss @@ -1,6 +1,7 @@ /** * 1. Stack content vertically so the table can scroll when its constrained by a fixed container height. */ +// stylelint-disable selector-no-qualifying-type doc-table { @include euiScrollBar; overflow: auto; @@ -18,12 +19,12 @@ doc-table { left: 0; right: 0; z-index: $euiZLevel1; - opacity: 0.5; + opacity: .5; } } .kbnDocTable__container.loading { - opacity: 0.5; + opacity: .5; } .kbnDocTable { @@ -58,7 +59,7 @@ doc-table { } dt { - background-color: transparentize(shade($euiColorPrimary, 20%), 0.9); + background-color: transparentize(shade($euiColorPrimary, 20%), .9); color: $euiTextColor; padding: ($euiSizeXS / 2) $euiSizeXS; margin-right: $euiSizeXS; diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/_details.scss b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_details.scss index 75d4789de929a..cc6ccfe9fd29f 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row/_details.scss +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_details.scss @@ -22,4 +22,3 @@ border-top: none !important; } } - diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/_open.scss b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_open.scss index 96a2f2e0843cc..659481e0968b5 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row/_open.scss +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/_open.scss @@ -4,7 +4,7 @@ * re-render which is SLOW. */ - .kbnDocTableOpen__button { +.kbnDocTableOpen__button { appearance: none; background-color: transparent; padding: 0; diff --git a/src/plugins/discover/public/application/components/discover.scss b/src/plugins/discover/public/application/components/discover.scss index 665bd98c232a5..90bfd84c4d54e 100644 --- a/src/plugins/discover/public/application/components/discover.scss +++ b/src/plugins/discover/public/application/components/discover.scss @@ -70,7 +70,7 @@ discover-app { // SASSTODO: the visualizing component should have an option or a modifier .series > rect { - fill-opacity: 0.5; + fill-opacity: .5; stroke-width: 1; } } diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss index fba26f5fa4686..5bae3d64a6b69 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss @@ -13,7 +13,7 @@ white-space: pre-wrap; color: $euiColorFullShade; vertical-align: top; - padding-top: $euiSizeXS * 0.5; + padding-top: $euiSizeXS * .5; } .kbnDocViewer__field { padding-top: $euiSizeS; diff --git a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss index 6b8654f6c3528..d21911f10f82e 100644 --- a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss +++ b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss @@ -94,7 +94,7 @@ */ .embPanel__optionsMenuButton { - background-color: transparentize($euiColorDarkestShade, 0.9); + background-color: transparentize($euiColorDarkestShade, .9); border-bottom-right-radius: 0; border-top-left-radius: 0; @@ -156,7 +156,6 @@ justify-content: center; flex-direction: column; overflow: auto; - text-align: center; padding: $euiSizeS; } @@ -169,5 +168,6 @@ .embPanel__content[data-loading] { pointer-events: none; filter: grayscale(100%); + /* stylelint-disable-next-line color-named */ filter: gray; } diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/ace/_ui_ace_keyboard_mode.scss b/src/plugins/es_ui_shared/__packages_do_not_import__/ace/_ui_ace_keyboard_mode.scss index 5b637224c1784..2ad92f3506b20 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/ace/_ui_ace_keyboard_mode.scss +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/ace/_ui_ace_keyboard_mode.scss @@ -4,7 +4,7 @@ bottom: 0; right: 0; left: 0; - background: transparentize($euiColorEmptyShade, 0.3); + background: transparentize($euiColorEmptyShade, .3); display: flex; flex-direction: column; justify-content: center; diff --git a/src/plugins/home/public/application/components/_solutions_section.scss b/src/plugins/home/public/application/components/_solutions_section.scss index 53c5678ff0b09..86c111ee1a0ad 100644 --- a/src/plugins/home/public/application/components/_solutions_section.scss +++ b/src/plugins/home/public/application/components/_solutions_section.scss @@ -66,11 +66,6 @@ overflow: hidden; } -.homSolutionPanel__header { - color: $euiColorEmptyShade; - padding: $euiSize; -} - .homSolutionPanel__icon { background-color: $euiColorEmptyShade !important; box-shadow: none !important; @@ -93,6 +88,8 @@ } .homSolutionPanel__header { + color: $euiColorEmptyShade; + padding: $euiSize; background-color: $euiColorPrimary; background-image: url('data:image/png;base64,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'), url('data:image/png;base64,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'); diff --git a/src/plugins/kibana_legacy/public/font_awesome/font_awesome.scss b/src/plugins/kibana_legacy/public/font_awesome/font_awesome.scss index 876a920269c49..e641965fabd51 100644 --- a/src/plugins/kibana_legacy/public/font_awesome/font_awesome.scss +++ b/src/plugins/kibana_legacy/public/font_awesome/font_awesome.scss @@ -2,21 +2,21 @@ font-family: 'FontAwesome'; src: url('~font-awesome/fonts/fontawesome-webfont.eot?v=4.7.0'); src: url('~font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), - url('~font-awesome/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), - url('~font-awesome/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), - url('~font-awesome/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), - url('~font-awesome/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); + url('~font-awesome/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), + url('~font-awesome/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), + url('~font-awesome/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), + url('~font-awesome/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); font-weight: normal; font-style: normal; } -@import "font-awesome/scss/variables"; -@import "font-awesome/scss/core"; -@import "font-awesome/scss/icons"; +@import 'font-awesome/scss/variables'; +@import 'font-awesome/scss/core'; +@import 'font-awesome/scss/icons'; // new file icon .#{$fa-css-prefix}-file-new-o:before { content: $fa-var-file-o; } -.#{$fa-css-prefix}-file-new-o:after { content: $fa-var-plus; position: relative; margin-left: -1.0em; font-size: 0.5em; } +.#{$fa-css-prefix}-file-new-o:after { content: $fa-var-plus; position: relative; margin-left: -1.0em; font-size: .5em; } // alias for alert types - allows class="fa fa-{{alertType}}" .fa-success:before { content: $fa-var-check; } diff --git a/src/plugins/kibana_legacy/public/paginate/_paginate.scss b/src/plugins/kibana_legacy/public/paginate/_paginate.scss index e9c1acaf9ee0d..9824ff2d8dff3 100644 --- a/src/plugins/kibana_legacy/public/paginate/_paginate.scss +++ b/src/plugins/kibana_legacy/public/paginate/_paginate.scss @@ -36,7 +36,7 @@ paginate { text-decoration: underline; } - &.active a { + &.active a { // stylelint-disable-line selector-no-qualifying-type text-decoration: none !important; font-weight: $euiFontWeightBold; color: $euiColorDarkShade; @@ -54,4 +54,3 @@ paginate { } } } - diff --git a/src/plugins/kibana_react/public/markdown/_markdown.scss b/src/plugins/kibana_react/public/markdown/_markdown.scss index 515a301e648c2..3c9b1cd165bab 100644 --- a/src/plugins/kibana_react/public/markdown/_markdown.scss +++ b/src/plugins/kibana_react/public/markdown/_markdown.scss @@ -18,7 +18,6 @@ $kbnDefaultFontSize: 14px; } .kbnMarkdown__body { - // Font size variables $kbnMarkdownFontSizeS: canvasToEm(12px); $kbnMarkdownFontSize: canvasToEm(14px); @@ -34,14 +33,14 @@ $kbnDefaultFontSize: 14px; $kbnMarkdownSizeXXS: canvasToEm(4px); // Grayscale variables - $kbnMarkdownAlphaLightestShade: rgba($euiColorFullShade,.05); - $kbnMarkdownAlphaLightShade: rgba($euiColorFullShade,.15); - $kbnMarkdownAlphaDarkShade: rgba($euiColorFullShade,.65); + $kbnMarkdownAlphaLightestShade: rgba($euiColorFullShade, .05); + $kbnMarkdownAlphaLightShade: rgba($euiColorFullShade, .15); + $kbnMarkdownAlphaDarkShade: rgba($euiColorFullShade, .65); // Reverse grayscale for opposite of theme - $kbnMarkdownAlphaLightestShadeReversed: rgba($euiColorEmptyShade,.05); - $kbnMarkdownAlphaLightShadeReversed: rgba($euiColorEmptyShade,.15); - $kbnMarkdownAlphaDarkShadeReversed: rgba($euiColorEmptyShade,.65); + $kbnMarkdownAlphaLightestShadeReversed: rgba($euiColorEmptyShade, .05); + $kbnMarkdownAlphaLightShadeReversed: rgba($euiColorEmptyShade, .15); + $kbnMarkdownAlphaDarkShadeReversed: rgba($euiColorEmptyShade, .65); &--reversed { color: $euiColorLightestShade; @@ -171,19 +170,19 @@ $kbnDefaultFontSize: 14px; hr::before { display: table; - content: ""; + content: ''; } hr::after { display: table; clear: both; - content: ""; + content: ''; } // 6. Lists ul, ol { - padding-left: 0; + padding-left: $kbnMarkdownSizeL; margin-top: 0; margin-bottom: $kbnMarkdownSize; } @@ -215,11 +214,6 @@ $kbnDefaultFontSize: 14px; margin-left: 0; } - ul, - ol { - padding-left: $kbnMarkdownSizeL; - } - ul ul, ul ol, ol ol, @@ -297,8 +291,8 @@ $kbnDefaultFontSize: 14px; code::before, code::after { - letter-spacing: -0.2em; - content: "\00a0"; + letter-spacing: -.2em; + content: '\00a0'; } pre { diff --git a/src/plugins/maps_legacy/public/map/_leaflet_overrides.scss b/src/plugins/maps_legacy/public/map/_leaflet_overrides.scss index 3235bb63ca22d..c688a8c9b518c 100644 --- a/src/plugins/maps_legacy/public/map/_leaflet_overrides.scss +++ b/src/plugins/maps_legacy/public/map/_leaflet_overrides.scss @@ -1,7 +1,8 @@ +// stylelint-disable selector-no-qualifying-type // SASSTODO: Create these tooltip variables in EUI // And/Or create a tooltip mixin -$temp-euiTooltipBackground: tintOrShade($euiColorFullShade, 25%, 90%); -$temp-euiTooltipText: $euiColorGhost; +$tempEUITooltipBackground: tintOrShade($euiColorFullShade, 25%, 90%); +$tempEUITooltipText: $euiColorGhost; // Converted leaflet icon sprite into background svg for custom coloring (dark mode) $visMapLeafletSprite: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 60' height='60' width='600'%3E%3Cg fill='#{hexToRGB($euiTextColor)}'%3E%3Cg%3E%3Cpath d='M18 36v6h6v-6h-6zm4 4h-2v-2h2v2z'/%3E%3Cpath d='M36 18v6h6v-6h-6zm4 4h-2v-2h2v2z'/%3E%3Cpath d='M23.142 39.145l-2.285-2.29 16-15.998 2.285 2.285z'/%3E%3C/g%3E%3Cpath d='M100 24.565l-2.096 14.83L83.07 42 76 28.773 86.463 18z'/%3E%3Cpath d='M140 20h20v20h-20z'/%3E%3Cpath d='M221 30c0 6.078-4.926 11-11 11s-11-4.922-11-11c0-6.074 4.926-11 11-11s11 4.926 11 11z'/%3E%3Cpath d='M270,19c-4.971,0-9,4.029-9,9c0,4.971,5.001,12,9,14c4.001-2,9-9.029,9-14C279,23.029,274.971,19,270,19z M270,31.5c-2.484,0-4.5-2.014-4.5-4.5c0-2.484,2.016-4.5,4.5-4.5c2.485,0,4.5,2.016,4.5,4.5C274.5,29.486,272.485,31.5,270,31.5z'/%3E%3Cg%3E%3Cpath d='M337,30.156v0.407v5.604c0,1.658-1.344,3-3,3h-10c-1.655,0-3-1.342-3-3v-10c0-1.657,1.345-3,3-3h6.345 l3.19-3.17H324c-3.313,0-6,2.687-6,6v10c0,3.313,2.687,6,6,6h10c3.314,0,6-2.687,6-6v-8.809L337,30.156'/%3E%3Cpath d='M338.72 24.637l-8.892 8.892H327V30.7l8.89-8.89z'/%3E%3Cpath d='M338.697 17.826h4v4h-4z' transform='rotate(-134.99 340.703 19.817)'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M381 42h18V24h-18v18zm14-16h2v14h-2V26zm-4 0h2v14h-2V26zm-4 0h2v14h-2V26zm-4 0h2v14h-2V26z'/%3E%3Cpath d='M395 20v-4h-10v4h-6v2h22v-2h-6zm-2 0h-6v-2h6v2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A"; @@ -23,7 +24,7 @@ $visMapLeafletSprite: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/s .leaflet-clickable { &:hover { stroke-width: $euiSizeS; - stroke-opacity: 0.8; + stroke-opacity: .8; } } @@ -78,8 +79,8 @@ $visMapLeafletSprite: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/s .leaflet-popup-content-wrapper { margin: 0; padding: 0; - background: $temp-euiTooltipBackground; - color: $temp-euiTooltipText; + background: $tempEUITooltipBackground; + color: $tempEUITooltipText; border-radius: $euiBorderRadius !important; // Override all positions the popup might be at } @@ -146,7 +147,7 @@ $visMapLeafletSprite: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/s img.leaflet-tile { @if (lightness($euiTextColor) < 50) { - filter: brightness(1.03) grayscale(0.73); + filter: brightness(1.03) grayscale(.73); } @else { filter: invert(1) brightness(1.75) grayscale(1); } diff --git a/src/plugins/navigation/public/index.scss b/src/plugins/navigation/public/index.scss index 4734a2915c620..760f3dfcddbbf 100644 --- a/src/plugins/navigation/public/index.scss +++ b/src/plugins/navigation/public/index.scss @@ -1 +1 @@ -@import "top_nav_menu/index"; +@import 'top_nav_menu/index'; diff --git a/src/plugins/timelion/public/_base.scss b/src/plugins/timelion/public/_base.scss index 616ac9b3486e7..e71196a2e6df9 100644 --- a/src/plugins/timelion/public/_base.scss +++ b/src/plugins/timelion/public/_base.scss @@ -16,4 +16,3 @@ input[type='checkbox'], opacity: .8; } } - diff --git a/src/plugins/timelion/public/directives/_form.scss b/src/plugins/timelion/public/directives/_form.scss index 370dd25f8263f..37a0cc4c0f3e5 100644 --- a/src/plugins/timelion/public/directives/_form.scss +++ b/src/plugins/timelion/public/directives/_form.scss @@ -21,8 +21,7 @@ } } -// sass-lint:disable-block no-qualifying-elements -select.form-control { +select.form-control { // stylelint-disable-line selector-no-qualifying-type // Makes the select arrow similar to EUI's arrowDown icon background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"%3E%3Cpath fill="#{hexToRGB($euiTextColor)}" d="M13.0688508,5.15725038 L8.38423975,9.76827428 C8.17054415,9.97861308 7.82999214,9.97914095 7.61576025,9.76827428 L2.93114915,5.15725038 C2.7181359,4.94758321 2.37277319,4.94758321 2.15975994,5.15725038 C1.94674669,5.36691756 1.94674669,5.70685522 2.15975994,5.9165224 L6.84437104,10.5275463 C7.48517424,11.1582836 8.51644979,11.1566851 9.15562896,10.5275463 L13.8402401,5.9165224 C14.0532533,5.70685522 14.0532533,5.36691756 13.8402401,5.15725038 C13.6272268,4.94758321 13.2818641,4.94758321 13.0688508,5.15725038 Z"/%3E%3C/svg%3E'); background-size: $euiSize; diff --git a/src/plugins/timelion/public/directives/_index.scss b/src/plugins/timelion/public/directives/_index.scss index a407c1dfabdeb..2a015711062a6 100644 --- a/src/plugins/timelion/public/directives/_index.scss +++ b/src/plugins/timelion/public/directives/_index.scss @@ -5,4 +5,3 @@ @import './timelion_interval/index'; @import './saved_object_finder'; @import './form'; - diff --git a/src/plugins/timelion/public/directives/_saved_object_finder.scss b/src/plugins/timelion/public/directives/_saved_object_finder.scss index 3a2489afb5721..55882fe78e99e 100644 --- a/src/plugins/timelion/public/directives/_saved_object_finder.scss +++ b/src/plugins/timelion/public/directives/_saved_object_finder.scss @@ -24,7 +24,6 @@ } } - saved-object-finder { .timSearchBar { diff --git a/src/plugins/timelion/public/directives/cells/_cells.scss b/src/plugins/timelion/public/directives/cells/_cells.scss index 6cd71378a81de..d1e5e976fc8d2 100644 --- a/src/plugins/timelion/public/directives/cells/_cells.scss +++ b/src/plugins/timelion/public/directives/cells/_cells.scss @@ -18,7 +18,6 @@ opacity: .5; } - .timCell__actions { position: absolute; bottom: $euiSizeXS; diff --git a/src/plugins/vis_default_editor/public/_default.scss b/src/plugins/vis_default_editor/public/_default.scss index f189b1c9e64a1..985f2c73f6efe 100644 --- a/src/plugins/vis_default_editor/public/_default.scss +++ b/src/plugins/vis_default_editor/public/_default.scss @@ -13,7 +13,7 @@ .visEditor__collapsibleSidebar { background: $euiColorLightestShade; - min-width: $vis-editor-sidebar-min-width; + min-width: $visEditorSidebarMinWidth; max-width: 100%; position: relative; flex-shrink: 0; diff --git a/src/plugins/vis_default_editor/public/_sidebar.scss b/src/plugins/vis_default_editor/public/_sidebar.scss index 42f7c4e6a892c..23241abcff770 100644 --- a/src/plugins/vis_default_editor/public/_sidebar.scss +++ b/src/plugins/vis_default_editor/public/_sidebar.scss @@ -3,7 +3,7 @@ // .visEditorSidebar { - min-width: $vis-editor-sidebar-min-width; + min-width: $visEditorSidebarMinWidth; // a hack for IE, issue: https://github.com/elastic/kibana/issues/66586 > .visEditorSidebar__formWrapper { @@ -12,7 +12,7 @@ } .visEditorSidebar__form { - @include flex-parent(1, 1, auto); + @include flexParent(1, 1, auto); max-width: 100%; } @@ -24,7 +24,7 @@ } @include euiBreakpoint('l', 'xl') { - @include flex-parent(1, 1, 1px); + @include flexParent(1, 1, 1px); @include euiScrollBar; overflow: auto; } diff --git a/src/plugins/vis_default_editor/public/index.scss b/src/plugins/vis_default_editor/public/index.scss index 6abb45dc546a3..8f2675c1ba374 100644 --- a/src/plugins/vis_default_editor/public/index.scss +++ b/src/plugins/vis_default_editor/public/index.scss @@ -1,4 +1,4 @@ -$vis-editor-sidebar-min-width: 350px; +$visEditorSidebarMinWidth: 350px; // Main layout @import './default'; diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.scss b/src/plugins/vis_type_markdown/public/markdown_vis.scss index 2356562a86ed0..97cfc4b151c77 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis.scss +++ b/src/plugins/vis_type_markdown/public/markdown_vis.scss @@ -12,7 +12,7 @@ .visEditor--markdown { .visEditorSidebar__config > *, - .visEditor--markdown__textarea { + .visEditor--markdown__textarea { flex-grow: 1; } diff --git a/src/plugins/vis_type_table/public/legacy/_table_vis.scss b/src/plugins/vis_type_table/public/legacy/_table_vis.scss index fa12ef9a1cf39..6ae3ddcca81a9 100644 --- a/src/plugins/vis_type_table/public/legacy/_table_vis.scss +++ b/src/plugins/vis_type_table/public/legacy/_table_vis.scss @@ -12,7 +12,7 @@ .table-vis-container { kbn-agg-table-group > .table > tbody > tr > td { - border-top: 0px; + border-top: 0; } .pagination-other-pages { diff --git a/src/plugins/vis_type_table/public/legacy/agg_table/_agg_table.scss b/src/plugins/vis_type_table/public/legacy/agg_table/_agg_table.scss index 4bbc4eb034f8d..89c7f6664c2fa 100644 --- a/src/plugins/vis_type_table/public/legacy/agg_table/_agg_table.scss +++ b/src/plugins/vis_type_table/public/legacy/agg_table/_agg_table.scss @@ -38,5 +38,5 @@ kbn-agg-table-group { } .small { - font-size: 0.9em !important; + font-size: .9em !important; } diff --git a/src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss b/src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss index 9864c72b76628..73ef76931ff12 100644 --- a/src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss +++ b/src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss @@ -2,4 +2,3 @@ overflow-y: auto; overflow-x: hidden; } - diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss b/src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss index 9585711c73dd2..9c07721fa00b3 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss @@ -8,10 +8,10 @@ } .tvbVisTimeSeriesDark { .echReactiveChart_unavailable { - color: #dfe5ef; + color: #DFE5EF; } .echLegendItem { - color: #dfe5ef; + color: #DFE5EF; } } .tvbVisTimeSeriesLight { diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss b/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss index 92037c80e7176..ec16404df8e7f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss @@ -29,5 +29,3 @@ overflow: auto; } } - - diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss b/src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss index fb6be95dba2a2..27ccb6227666d 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss @@ -1,5 +1,4 @@ - .tvbVisTopN { position: relative; overflow: auto; diff --git a/src/plugins/vis_type_vega/public/components/vega_vis.scss b/src/plugins/vis_type_vega/public/components/vega_vis.scss index 004eed5a3972b..f0062869e0046 100644 --- a/src/plugins/vis_type_vega/public/components/vega_vis.scss +++ b/src/plugins/vis_type_vega/public/components/vega_vis.scss @@ -1,3 +1,4 @@ +// stylelint-disable selector-no-qualifying-type .vgaVis__wrapper { @include euiScrollBar; @@ -109,7 +110,6 @@ color: makeHighContrastColor($euiColorDanger, $calculateBackgroundColor); } - // Style tooltip popup (gets created dynamically at the top level if dashboard has a Vega vis) // Adapted from https://github.com/vega/vega-tooltip diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/_index.scss b/src/plugins/vis_type_vislib/public/vislib/lib/_index.scss index 6751e9f28a8ee..6ab284fd8d122 100644 --- a/src/plugins/vis_type_vislib/public/vislib/lib/_index.scss +++ b/src/plugins/vis_type_vislib/public/vislib/lib/_index.scss @@ -1,3 +1,2 @@ @import './alerts'; @import './layout/index'; - diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss b/src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss index b3fdf654f8ab7..a6896a9181b4e 100644 --- a/src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss +++ b/src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss @@ -25,123 +25,6 @@ // Numbers in here are brittle // -.visWrapper { - display: flex; - flex: 1 1 100%; - flex-direction: row; - min-height: 0; - min-width: 0; - overflow: hidden; - padding: $euiSizeS 0; -} - -.visWrapper__column { - display: flex; - flex: 1 0 0; - flex-direction: column; - min-height: 0; - min-width: 0; -} - -.visWrapper__splitCharts--column { - display: flex; - flex: 1 0 20px; - flex-direction: row; - min-height: 0; - min-width: 0; - - .visWrapper__chart { - margin-top: 0; - margin-bottom: 0; - } -} - -.visWrapper__splitCharts--row { - display: flex; - flex-direction: column; - flex: 1 1 100%; - min-height: 0; - min-width: 0; - - .visWrapper__chart { - margin-left: 0; - margin-right: 0; - } -} - -.visWrapper__chart { - display: flex; - flex: 1 0 0; - overflow: visible; - margin: 5px; - min-height: 0; - min-width: 0; -} - -.visWrapper__alerts { - position: relative; -} - -// General Axes - -.visAxis__column--top .axis-div svg { - margin-bottom: -5px; -} - -// Y Axes - -.visAxis--x, -.visAxis--y { - display: flex; - flex-direction: column; - min-height: 0; - min-width: 0; -} - -.visAxis--x { - overflow: visible; -} - -.visAxis__spacer--y { - min-height: 0; -} - -.visAxis__column--y { - display: flex; - flex-direction: row; - flex: 1 0 ($euiSizeXL + $euiSizeXS); - min-height: 0; - min-width: 0; -} - -.visAxis__splitTitles--y { - display: flex; - flex-direction: column; - min-height: $euiSizeM; - min-width: 0; -} - -.visAxis__splitTitles--x { - display: flex; - flex-direction: row; - min-height: 1px; - max-height: $euiSize; - min-width: $euiSize; -} - -.visAxis__splitAxes--x, -.visAxis__splitAxes--y { - display: flex; - flex-direction: column; - min-height: ($euiSize + $euiSizeXS); - min-width: 0; -} - -.visAxis__splitAxes--x { - flex-direction: row; - min-height: 0; -} - // // STYLE // @@ -155,6 +38,14 @@ // sass-lint:disable-block no-mergeable-selectors // Keep SVG and non-renamable selectors separately .visWrapper { + display: flex; + flex: 1 1 100%; + flex-direction: row; + min-height: 0; + min-width: 0; + overflow: hidden; + padding: $euiSizeS 0; + svg { overflow: visible; } @@ -340,3 +231,110 @@ fill: $visHoverBackgroundColor; } } + +.visWrapper__column { + display: flex; + flex: 1 0 0; + flex-direction: column; + min-height: 0; + min-width: 0; +} + +.visWrapper__splitCharts--column { + display: flex; + flex: 1 0 20px; + flex-direction: row; + min-height: 0; + min-width: 0; + + .visWrapper__chart { + margin-top: 0; + margin-bottom: 0; + } +} + +.visWrapper__splitCharts--row { + display: flex; + flex-direction: column; + flex: 1 1 100%; + min-height: 0; + min-width: 0; + + .visWrapper__chart { + margin-left: 0; + margin-right: 0; + } +} + +.visWrapper__chart { + display: flex; + flex: 1 0 0; + overflow: visible; + margin: 5px; + min-height: 0; + min-width: 0; +} + +.visWrapper__alerts { + position: relative; +} + +// General Axes + +.visAxis__column--top .axis-div svg { + margin-bottom: -5px; +} + +// Y Axes + +.visAxis--x, +.visAxis--y { + display: flex; + flex-direction: column; + min-height: 0; + min-width: 0; +} + +.visAxis--x { + overflow: visible; +} + +.visAxis__spacer--y { + min-height: 0; +} + +.visAxis__column--y { + display: flex; + flex-direction: row; + flex: 1 0 ($euiSizeXL + $euiSizeXS); + min-height: 0; + min-width: 0; +} + +.visAxis__splitTitles--y { + display: flex; + flex-direction: column; + min-height: $euiSizeM; + min-width: 0; +} + +.visAxis__splitTitles--x { + display: flex; + flex-direction: row; + min-height: 1px; + max-height: $euiSize; + min-width: $euiSize; +} + +.visAxis__splitAxes--x, +.visAxis__splitAxes--y { + display: flex; + flex-direction: column; + min-height: ($euiSize + $euiSizeXS); + min-width: 0; +} + +.visAxis__splitAxes--x { + flex-direction: row; + min-height: 0; +} diff --git a/src/plugins/visualizations/public/components/_visualization.scss b/src/plugins/visualizations/public/components/_visualization.scss index bde9621fd70b8..e8d4c5c6db445 100644 --- a/src/plugins/visualizations/public/components/_visualization.scss +++ b/src/plugins/visualizations/public/components/_visualization.scss @@ -27,14 +27,14 @@ display: flex; flex-direction: row; overflow: auto; - transition: opacity 0.01s; + transition: opacity .01s; // IE11 Hack // // Normally we would just set flex: 1 1 0%, which works as expected in IE11. // Unfortunately, a recent bug in Firefox causes this rule to be ignored, so we // have to use an IE-specific hack instead. - @include internetExplorerOnly(){ + @include internetExplorerOnly() { flex: 1 0; } @@ -45,7 +45,7 @@ // SASSTODO: Can't find exact usage .loading { - opacity: 0.5; + opacity: .5; } // SASSTODO: Can't find exact usage @@ -55,7 +55,7 @@ left: 0; right: 0; z-index: 20; - opacity: 0.5; + opacity: .5; } } diff --git a/src/plugins/visualizations/public/embeddable/_embeddables.scss b/src/plugins/visualizations/public/embeddable/_embeddables.scss index 23d3e189767d7..f0ec8479489d2 100644 --- a/src/plugins/visualizations/public/embeddable/_embeddables.scss +++ b/src/plugins/visualizations/public/embeddable/_embeddables.scss @@ -15,6 +15,11 @@ .visLegend__toggle { border-bottom-right-radius: 0; border-top-left-radius: 0; + opacity: 0; /* 1 */ + + &:focus { + opacity: 1; /* 2 */ + } } } @@ -26,23 +31,15 @@ * 3. Always show in editing mode */ -.embPanel_optionsMenuPopover[class*="-isOpen"], -.embPanel:hover { +.embPanel_optionsMenuPopover[class*='-isOpen'], +.embPanel:hover { .visLegend__toggle { opacity: 1; } } -.embPanel .visLegend__toggle { - opacity: 0; /* 1 */ - - &:focus { - opacity: 1; /* 2 */ - } -} - .embPanel--editing { .visLegend__toggle { opacity: 1; /* 3 */ } -} \ No newline at end of file +} diff --git a/src/plugins/visualize/public/application/components/visualize_editor.scss b/src/plugins/visualize/public/application/components/visualize_editor.scss index 3a542cacc44be..6ddf3c1264bff 100644 --- a/src/plugins/visualize/public/application/components/visualize_editor.scss +++ b/src/plugins/visualize/public/application/components/visualize_editor.scss @@ -1,5 +1,5 @@ .visEditor { - @include flex-parent(); + @include flexParent(); height: 100%; @@ -23,7 +23,7 @@ a tilemap in an iframe: https://github.com/elastic/kibana/issues/16457 */ } .visEditor__content { - @include flex-parent(); + @include flexParent(); width: 100%; z-index: 0; } diff --git a/src/plugins/visualize/public/application/components/visualize_listing.scss b/src/plugins/visualize/public/application/components/visualize_listing.scss index e1777112cdb3a..8eb2a8911ee02 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.scss +++ b/src/plugins/visualize/public/application/components/visualize_listing.scss @@ -12,7 +12,7 @@ .visListingTable__experimentalIcon { width: $euiSizeL; vertical-align: baseline; - padding: 0 $euiSizeS; + padding: 0 $euiSizeS; margin-left: $euiSizeS; } diff --git a/test/scripts/jenkins_unit.sh b/test/scripts/jenkins_unit.sh index 2edd66579f72f..00fd378b348a0 100755 --- a/test/scripts/jenkins_unit.sh +++ b/test/scripts/jenkins_unit.sh @@ -11,7 +11,7 @@ rename_coverage_file() { if [[ -z "$CODE_COVERAGE" ]] ; then # Lint ./test/scripts/lint/eslint.sh - ./test/scripts/lint/sasslint.sh + ./test/scripts/lint/stylelint.sh # Test ./test/scripts/test/jest_integration.sh diff --git a/test/scripts/lint/sasslint.sh b/test/scripts/lint/sasslint.sh deleted file mode 100755 index 72e341cdcda16..0000000000000 --- a/test/scripts/lint/sasslint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -checks-reporter-with-killswitch "Lint: sasslint" \ - node scripts/sasslint diff --git a/test/scripts/lint/stylelint.sh b/test/scripts/lint/stylelint.sh new file mode 100755 index 0000000000000..3dcb682c40f0c --- /dev/null +++ b/test/scripts/lint/stylelint.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +source src/dev/ci_setup/setup_env.sh + +checks-reporter-with-killswitch "Lint: stylelint" \ + node scripts/stylelint diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 18be6e69a2637..ec2af87caba56 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -24,7 +24,7 @@ def check() { def lint() { tasks([ kibanaPipeline.scriptTask('Lint: eslint', 'test/scripts/lint/eslint.sh'), - kibanaPipeline.scriptTask('Lint: sasslint', 'test/scripts/lint/sasslint.sh'), + kibanaPipeline.scriptTask('Lint: stylelint', 'test/scripts/lint/stylelint.sh'), ]) } diff --git a/x-pack/plugins/canvas/public/components/element_card/element_card.scss b/x-pack/plugins/canvas/public/components/element_card/element_card.scss index 87dc59edba299..e6a308cc6af48 100644 --- a/x-pack/plugins/canvas/public/components/element_card/element_card.scss +++ b/x-pack/plugins/canvas/public/components/element_card/element_card.scss @@ -25,7 +25,7 @@ .canvasElementCard { .euiCard__top { text-align: center; - width: calc(100% + #{$euiSize}* 2); + width: calc(100% + #{$euiSize} * 2); height: 85px; margin: calc(-1 * #{$euiSize}) calc(-1 * #{$euiSize}) 0; } diff --git a/x-pack/plugins/canvas/public/components/fullscreen/fullscreen.scss b/x-pack/plugins/canvas/public/components/fullscreen/fullscreen.scss index edd681a2c33e8..3ab04e31eb9c1 100644 --- a/x-pack/plugins/canvas/public/components/fullscreen/fullscreen.scss +++ b/x-pack/plugins/canvas/public/components/fullscreen/fullscreen.scss @@ -1,4 +1,5 @@ -body.canvas-isFullscreen { // sass-lint:disable-line no-qualifying-elements +/* stylelint-disable selector-no-qualifying-type */ +body.canvas-isFullscreen { // following two rules are for overriding the header bar padding &.euiBody--headerIsFixed { padding-top: 0; diff --git a/x-pack/plugins/canvas/public/components/toolbar/tray/tray.scss b/x-pack/plugins/canvas/public/components/toolbar/tray/tray.scss index 0ed47a761cd4f..69c4329ecedaf 100644 --- a/x-pack/plugins/canvas/public/components/toolbar/tray/tray.scss +++ b/x-pack/plugins/canvas/public/components/toolbar/tray/tray.scss @@ -7,4 +7,3 @@ background-color: $euiFormBackgroundColor; border-radius: 0; } - diff --git a/x-pack/plugins/canvas/public/components/workpad/workpad.scss b/x-pack/plugins/canvas/public/components/workpad/workpad.scss index 2aa69fb119317..b7c84507df61e 100644 --- a/x-pack/plugins/canvas/public/components/workpad/workpad.scss +++ b/x-pack/plugins/canvas/public/components/workpad/workpad.scss @@ -12,7 +12,7 @@ user-select: none; pointer-events: none; background-image: linear-gradient(to right, $euiColorMediumShade 1px, transparent 1px), - linear-gradient(to bottom, $euiColorMediumShade 1px, transparent 1px); + linear-gradient(to bottom, $euiColorMediumShade 1px, transparent 1px); background-size: 50px 50px; top: 0; } diff --git a/x-pack/plugins/canvas/public/components/workpad_loader/workpad_dropzone/workpad_dropzone.scss b/x-pack/plugins/canvas/public/components/workpad_loader/workpad_dropzone/workpad_dropzone.scss index 0392e1983df57..ac6838da97fbd 100644 --- a/x-pack/plugins/canvas/public/components/workpad_loader/workpad_dropzone/workpad_dropzone.scss +++ b/x-pack/plugins/canvas/public/components/workpad_loader/workpad_dropzone/workpad_dropzone.scss @@ -19,4 +19,4 @@ .euiHealth { width: 100%; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/x-pack/plugins/canvas/public/style/main.scss b/x-pack/plugins/canvas/public/style/main.scss index 4387557cbc9f2..4f293e9072e4f 100644 --- a/x-pack/plugins/canvas/public/style/main.scss +++ b/x-pack/plugins/canvas/public/style/main.scss @@ -3,7 +3,6 @@ */ $canvasElementCardWidth: 210px; - .canvas.canvasContainerWrapper { display: flex; flex-grow: 1; diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/footer.module.scss b/x-pack/plugins/canvas/shareable_runtime/components/footer/footer.module.scss index d7a38d0fc5916..54f5983bd2e5a 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/footer.module.scss +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/footer.module.scss @@ -10,7 +10,7 @@ } :global .kbnCanvas :local .bar { - transition: bottom 0.25s; + transition: bottom .25s; padding: $euiSizeM; } diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/scrubber.module.scss b/x-pack/plugins/canvas/shareable_runtime/components/footer/scrubber.module.scss index ce857f37b904a..a8b602d40e8cd 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/scrubber.module.scss +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/scrubber.module.scss @@ -9,7 +9,7 @@ left: 0; right: 0; padding: $euiSizeS 0 ($euiSizeL * 2) 0; - transition: bottom 0.25s; + transition: bottom .25s; height: 172px; } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field.scss index 15509b98d465b..80d60e2de67e2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field.scss +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_field.scss @@ -2,7 +2,7 @@ font-size: $euiFontSizeS; line-height: $euiLineHeight; display: grid; - grid-template-columns: 0.85fr $euiSizeXL 1fr; + grid-template-columns: .85fr $euiSizeXL 1fr; grid-gap: $euiSizeXS; &__separator { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss index d6b6bd3442590..71f16a830e5f8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss @@ -10,7 +10,7 @@ &__imageContainer { max-height: 115px; overflow: hidden; - background-color: #0076cc; + background-color: #0076CC; @include euiBreakpoint('s', 'm', 'l', 'xl') { max-height: none; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/layout.scss b/x-pack/plugins/enterprise_search/public/applications/shared/layout/layout.scss index e867e9cf5a445..d46696d9f978c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/layout.scss +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/layout.scss @@ -32,7 +32,7 @@ position: absolute; right: $euiSize; top: $sideBarMobileHeight / 2; - transform: translateY(-50%) scale(0.9); + transform: translateY(-50%) scale(.9); .euiButton { min-width: 0; @@ -52,7 +52,7 @@ width: $sideBarWidth; background-color: $euiColorLightestShade; - box-shadow: inset (-1 * $euiSizeXS) 0 $euiSizeS (-1 * $euiSizeXS) rgba($euiShadowColor, 0.25); + box-shadow: inset (-1 * $euiSizeXS) 0 $euiSizeS (-1 * $euiSizeXS) rgba($euiShadowColor, .25); @include euiBreakpoint('xs', 's', 'm') { position: relative; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.scss b/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.scss index 79cd7634cfaa0..84b5c8cdd40f7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.scss +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.scss @@ -52,7 +52,7 @@ $euiSizeML: $euiSize * 1.25; // 20px - between medium and large ¯\_(ツ)_/¯ font-weight: $euiFontWeightMedium; line-height: $euiFontSizeM; - $activeBgColor: rgba($euiColorFullShade, 0.05); + $activeBgColor: rgba($euiColorFullShade, .05); &--isActive { background-color: $activeBgColor; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/truncate/truncated_content.scss b/x-pack/plugins/enterprise_search/public/applications/shared/truncate/truncated_content.scss index 701834acfed9d..890d7a331cc5b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/truncate/truncated_content.scss +++ b/x-pack/plugins/enterprise_search/public/applications/shared/truncate/truncated_content.scss @@ -23,7 +23,7 @@ padding: 0 2px; display: none; align-items: center; - box-shadow: 0 1px 3px rgba(black, 0.1); + box-shadow: 0 1px 3px $euiColorInk; border: 1px solid $euiBorderColor; width: auto; white-space: nowrap; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/license_badge/license_badge.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/license_badge/license_badge.scss index f295194b7a130..4cc9263d1747c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/license_badge/license_badge.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/license_badge/license_badge.scss @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -.wsLicenseBadge { - &__text { - color: white; - } +.wsLicenseBadge__text { + color: $euiColorGhost; } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/user_icon/user_icon.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/user_icon/user_icon.scss index a47abba723fc8..bd061d6d00760 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/user_icon/user_icon.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/user_icon/user_icon.scss @@ -24,7 +24,7 @@ } &__text { - text-shadow: 0 1px 2px rgba(black, 0.08); + text-shadow: 0 1px 2px rgba($euiColorInk, .08); justify-content: center; align-items: center; font-size: 1.125rem; @@ -81,7 +81,7 @@ font-weight: 500; color: $euiColorEmptyShade; font-size: .875rem; - text-shadow: 0 1px 2px rgba(black, 0.08); + text-shadow: 0 1px 2px rgba($euiColorInk, .08); } &__image { @@ -105,8 +105,8 @@ display: flex; flex-direction: column; align-items: flex-start; - box-shadow: 0px 0px 12px rgba(black, 0.15); - background: white; + box-shadow: 0 0 12px rgba($euiColorInk, .15); + background: $euiColorGhost; text-align: left; text-overflow: ellipsis; min-width: 125px; @@ -130,7 +130,7 @@ position:absolute; border-left: 6px solid transparent; border-right: 6px solid transparent; - border-bottom: 6px solid white; + border-bottom: 6px solid $euiColorGhost; top: -6px; left: 16px; } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.scss index 27935104f4ef6..af8c850f275b6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.scss @@ -8,16 +8,16 @@ // Custom Source display settings // -------------------------------------------------- -@mixin source_name { +@mixin sourceName { font-size: .6875em; text-transform: uppercase; font-weight: 600; - letter-spacing: 0.06em; + letter-spacing: .06em; } -@mixin example_result_box_shadow { +@mixin exampleResultBoxShadow { box-shadow: - 0 1px 3px rgba(black, 0.1), + 0 1px 3px rgba($euiColorInk, .1), 0 0 20px $euiColorLightestShade; } @@ -82,7 +82,7 @@ .example-standout-result { border-radius: 4px; overflow: hidden; - @include example_result_box_shadow; + @include exampleResultBoxShadow; &__header, &__content { @@ -97,7 +97,7 @@ &__source-name { line-height: 34px; - @include source_name; + @include sourceName; } } @@ -116,7 +116,7 @@ &__source-name { line-height: 1.75em; - @include source_name; + @include sourceName; } &__content { @@ -171,7 +171,7 @@ } .example-result-detail-card { - @include example_result_box_shadow; + @include exampleResultBoxShadow; &__header { position: relative; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.scss index fbd4e6f87d19b..d8c70e45112c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.scss @@ -35,7 +35,7 @@ color: $euiColorFullShade; box-shadow: inset 0 0 0 1px $euiColorLightShade, - 0 2px 4px rgba(black, .05); + 0 2px 4px rgba($euiColorInk, .05); } &:after { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.scss index 2d1e474c03faa..36016d8da93ba 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.scss @@ -13,13 +13,13 @@ &--error { font-weight: $euiFontWeightSemiBold; color: $euiColorDanger; - background: rgba($euiColorDanger, 0.1); + background: rgba($euiColorDanger, .1); &__label { margin-left: $euiSizeS * 1.75; font-weight: $euiFontWeightRegular; text-decoration: underline; - opacity: 0.7; + opacity: .7; } } diff --git a/x-pack/plugins/graph/public/_main.scss b/x-pack/plugins/graph/public/_main.scss index 0d17015385292..6b32de32c06d0 100644 --- a/x-pack/plugins/graph/public/_main.scss +++ b/x-pack/plugins/graph/public/_main.scss @@ -20,7 +20,7 @@ * Utilities */ -.gphNoUserSelect{ +.gphNoUserSelect { user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; diff --git a/x-pack/plugins/graph/public/angular/templates/_graph.scss b/x-pack/plugins/graph/public/angular/templates/_graph.scss index 4ba65e7ec6b96..5c2f5d5f7a881 100644 --- a/x-pack/plugins/graph/public/angular/templates/_graph.scss +++ b/x-pack/plugins/graph/public/angular/templates/_graph.scss @@ -11,13 +11,12 @@ * 1. Calculated px values come from the open/closed state of the global nav sidebar */ - - #graphBasic { - display: flex; - flex-direction: column; - flex: 1; - overflow: hidden; - } +#graphBasic { + display: flex; + flex-direction: column; + flex: 1; + overflow: hidden; +} .gphGraph__container { display: flex; diff --git a/x-pack/plugins/graph/public/angular/templates/_sidebar.scss b/x-pack/plugins/graph/public/angular/templates/_sidebar.scss index e54158e2ad8ce..e784649b250fa 100644 --- a/x-pack/plugins/graph/public/angular/templates/_sidebar.scss +++ b/x-pack/plugins/graph/public/angular/templates/_sidebar.scss @@ -17,7 +17,7 @@ } } -.gphSidebar__header{ +.gphSidebar__header { margin-top: $euiSizeS; color: $euiColorEmptyShade; background-color: $euiColorDarkShade; @@ -26,7 +26,7 @@ margin-bottom: $euiSizeXS; } -.gphSidebar__panel{ +.gphSidebar__panel { max-height: $euiSizeL * 10; overflow-y: hidden auto; } diff --git a/x-pack/plugins/graph/public/components/field_manager/_field_editor.scss b/x-pack/plugins/graph/public/components/field_manager/_field_editor.scss index 381d4778cf13b..20773d98ce9c3 100644 --- a/x-pack/plugins/graph/public/components/field_manager/_field_editor.scss +++ b/x-pack/plugins/graph/public/components/field_manager/_field_editor.scss @@ -4,7 +4,7 @@ .gphFieldEditor__badge--disabled, .gphFieldEditor__badge--disabled:focus { - opacity: 0.7; + opacity: .7; text-decoration: line-through; } diff --git a/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss b/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss index 975e4e84d6b0d..caef2b6987ddd 100644 --- a/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss +++ b/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss @@ -13,16 +13,16 @@ fill: $euiColorMediumShade; stroke: $euiColorMediumShade; stroke-width: 2; - stroke-opacity: 0.5; + stroke-opacity: .5; &:hover { - stroke-opacity: 0.95; + stroke-opacity: .95; cursor: pointer; } &--selected { stroke: $euiColorDarkShade; - stroke-opacity: 0.95; + stroke-opacity: .95; } } @@ -54,7 +54,7 @@ fill: $euiColorMediumShade; &--selected { stroke-width: $euiSizeXS; - stroke: transparentize($euiColorPrimary, 0.25); + stroke: transparentize($euiColorPrimary, .25); } } diff --git a/x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss b/x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss index 56fc1b31a9ffa..6e443d7105c53 100644 --- a/x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss +++ b/x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss @@ -8,7 +8,7 @@ &--pickable { margin: $euiSizeXS; cursor: pointer; - opacity: 0.7; + opacity: .7; } &--pickable:hover, diff --git a/x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss b/x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss index 98b11112099e0..755eaca75664a 100644 --- a/x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss +++ b/x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss @@ -1,9 +1,9 @@ .gphVennDiagram__left { fill: $euiColorDanger; - fill-opacity: 0.5; + fill-opacity: .5; } .gphVennDiagram__right { fill: $euiColorPrimary; - fill-opacity: 0.5; + fill-opacity: .5; } diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates.scss b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates.scss index 8e196936e4073..c603224394919 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates.scss +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates.scss @@ -1,5 +1,4 @@ - /** * [1] Will center vertically the empty search result */ diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_list_item.scss b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_list_item.scss index b454d8697c5fc..44ba20eed44ae 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_list_item.scss +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_list_item.scss @@ -1,5 +1,5 @@ .componentTemplatesListItem { - background-color: white; + background-color: $euiColorGhost; padding: $euiSizeM; border-bottom: $euiBorderThin; position: relative; @@ -8,7 +8,7 @@ &--selected { &::before { content: ''; - background-color: rgba(255, 255, 255, 0.7); + background-color: rgba(255, 255, 255, .7); height: 100%; left: 0; position: absolute; diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.scss b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.scss index aeccc6a513fe2..db16b14071aea 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.scss +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.scss @@ -32,10 +32,10 @@ &__count { font-weight: 600; } - } + } - &__content { - mask-image: none; - } + &__content { + mask-image: none; + } } } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss index f8c1b5e086993..8e8ca5c571860 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss @@ -49,67 +49,67 @@ .mappingsEditor__fieldsList { .mappingsEditor__fieldsList .mappingsEditor__fieldsListItem__content, .mappingsEditor__createFieldContent { - &::before { - border-bottom: 1px solid $euiColorMediumShade; - content: ''; - left: $euiSize; - position: absolute; - top: 50%; - width: $euiSizeS; - } - &::after { - border-left: 1px solid $euiColorMediumShade; - content: ''; - left: $euiSize; - position: absolute; - top: calc(50% - #{$euiSizeS}); - height: $euiSizeS; - } + &::before { + border-bottom: 1px solid $euiColorMediumShade; + content: ''; + left: $euiSize; + position: absolute; + top: 50%; + width: $euiSizeS; } - - .mappingsEditor__createFieldContent { - padding-left: $euiSizeXXL - $euiSizeXS; // [1] + &::after { + border-left: 1px solid $euiColorMediumShade; + content: ''; + left: $euiSize; + position: absolute; + top: calc(50% - #{$euiSizeS}); + height: $euiSizeS; } + } - .mappingsEditor__createFieldWrapper { - &--multiField { - .mappingsEditor__createFieldContent { - padding-left: $euiSize; - } + .mappingsEditor__createFieldContent { + padding-left: $euiSizeXXL - $euiSizeXS; // [1] + } - .mappingsEditor__createFieldContent { - &::before, &::after { - content: none; - } - } - } + .mappingsEditor__createFieldWrapper { + &--multiField { - &--toggle { - .mappingsEditor__createFieldContent { - padding-left: $euiSizeXXL - $euiSizeXS; // [1] - } - } - } - - .mappingsEditor__fieldsList .mappingsEditor__fieldsListItem__content { - padding-left: $euiSizeXL; // [2] + .mappingsEditor__createFieldContent { + padding-left: $euiSize; - &--toggle, &--multiField { &::before, &::after { content: none; } } + } - &--toggle { - padding-left: 0; + &--toggle { + .mappingsEditor__createFieldContent { + padding-left: $euiSizeXXL - $euiSizeXS; // [1] } + } + } - &--multiField { - padding-left: $euiSizeS; + .mappingsEditor__fieldsList .mappingsEditor__fieldsListItem__content { + padding-left: $euiSizeXL; // [2] + + &--toggle, &--multiField { + &::before, &::after { + content: none; } } + + &--toggle { + padding-left: 0; + } + + &--multiField { + padding-left: $euiSizeS; + } + } } +// stylelint-disable selector-no-qualifying-type ul.esUiTree { padding: 0; margin: 0; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss index e117271dba309..cc733972808e3 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss @@ -29,7 +29,7 @@ } .mappingsEditor__fieldsListItem__field--dim { - opacity: 0.3; + opacity: .3; &:hover { background-color: initial; diff --git a/x-pack/plugins/index_management/public/index.scss b/x-pack/plugins/index_management/public/index.scss index 02686c4f7d6f3..11074af372f8c 100644 --- a/x-pack/plugins/index_management/public/index.scss +++ b/x-pack/plugins/index_management/public/index.scss @@ -11,7 +11,7 @@ .indTable { // The index table is a bespoke table and can't make use of EuiBasicTable's width settings - thead th.indTable__header--name { + thead th.indTable__header--name { // stylelint-disable-line selector-no-qualifying-type width: 25%; } diff --git a/x-pack/plugins/infra/public/index.scss b/x-pack/plugins/infra/public/index.scss index a3d74e3afebe3..990092c792b14 100644 --- a/x-pack/plugins/infra/public/index.scss +++ b/x-pack/plugins/infra/public/index.scss @@ -14,4 +14,3 @@ display: flex; flex-direction: column; } - diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss index f1e399428cdf2..b04020cb59c69 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss @@ -31,7 +31,7 @@ } } $dropZoneButtonHeight: 60px; - $dropZoneButtonOffsetY: $dropZoneButtonHeight * 0.5; + $dropZoneButtonOffsetY: $dropZoneButtonHeight * .5; &__dropZoneButton { position: absolute; diff --git a/x-pack/plugins/lens/public/_mixins.scss b/x-pack/plugins/lens/public/_mixins.scss index 0db72d118cef1..f9b8ce466040e 100644 --- a/x-pack/plugins/lens/public/_mixins.scss +++ b/x-pack/plugins/lens/public/_mixins.scss @@ -5,10 +5,10 @@ $hideHeight: $euiScrollBarCorner * 1.25; mask-image: linear-gradient( to right, - transparentize(red, .9) 0%, - transparentize(red, 0) $hideHeight, - transparentize(red, 0) calc(100% - #{$hideHeight}), - transparentize(red, .9) 100% + transparentize($euiColorDanger, .9) 0%, + transparentize($euiColorDanger, 0) $hideHeight, + transparentize($euiColorDanger, 0) calc(100% - #{$hideHeight}), + transparentize($euiColorDanger, .9) 100% ); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.scss index e0031d051df81..0a4f7b0debf22 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.scss @@ -12,7 +12,7 @@ } // Targeting img as this won't target normal EuiIcon's only the custom svgs's -img.lnsChartSwitch__chartIcon { // sass-lint:disable-line no-qualifying-elements +img.lnsChartSwitch__chartIcon { // stylelint-disable-line selector-no-qualifying-type // The large icons aren't square so max out the width to fill the height width: 100%; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss index 15464bb204f17..715d15e99ec20 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss @@ -38,5 +38,3 @@ margin-right: $euiSizeS; } } - - diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/color/_color_stops.scss b/x-pack/plugins/maps/public/classes/styles/vector/components/color/_color_stops.scss index 09a9ad59bce3c..e927894957dd8 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/color/_color_stops.scss +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/color/_color_stops.scss @@ -5,4 +5,3 @@ margin-top: $euiSizeS; } } - diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/_vector_legend.scss b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/_vector_legend.scss index d260f6effb2cb..2d6d1837cfb0b 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/_vector_legend.scss +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/_vector_legend.scss @@ -1,4 +1,4 @@ -.vectorStyleLegendSpacer { +.vectorStyleLegendSpacer { &:not(:last-child) { margin-bottom: $euiSizeS; } diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/_mixins.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/_mixins.scss index 88ae78fde6ace..97557f1312e86 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/_mixins.scss +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/_mixins.scss @@ -1,12 +1,8 @@ @mixin mapOverlayIsTextOnly { text-shadow: - 0 0 2px $euiColorEmptyShade, - // Multiple shadows helps turn it into an outline since - // text shadows have no spread value - 0 0 1px $euiColorEmptyShade, - 0 0 1px $euiColorEmptyShade, - 0 0 1px $euiColorEmptyShade, - 0 0 1px $euiColorEmptyShade, - 0 0 1px $euiColorEmptyShade, - 0 0 1px $euiColorEmptyShade; + 0 0 2px $euiColorEmptyShade, + 0 0 1px $euiColorEmptyShade, + 0 0 1px $euiColorEmptyShade, + 0 0 1px $euiColorEmptyShade, + 0 0 1px $euiColorEmptyShade 0 0 1px $euiColorEmptyShade 0 0 1px $euiColorEmptyShade; } diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss index 764bf841f6207..53e5d2e0bb6b6 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss @@ -45,7 +45,6 @@ background-color: tintOrShade($euiColorLightShade, 60%, 20%); } - .mapTocEntry-visible, .mapTocEntry-notVisible { display: flex; diff --git a/x-pack/plugins/ml/public/application/_variables.scss b/x-pack/plugins/ml/public/application/_variables.scss index 159a1ffd45dd5..761b266cac189 100644 --- a/x-pack/plugins/ml/public/application/_variables.scss +++ b/x-pack/plugins/ml/public/application/_variables.scss @@ -1,8 +1,8 @@ -$mlColorCritical: #fe5050; -$mlColorMajor: #fba740; -$mlColorMinor: #fdec25; -$mlColorWarning: #8bc8fb; -$mlColorUnknown: #c0c0c0; +$mlColorCritical: #FE5050; +$mlColorMajor: #FBA740; +$mlColorMinor: #FDEC25; +$mlColorWarning: #8BC8FB; +$mlColorUnknown: #C0C0C0; $mlColorCriticalText: makeHighContrastColor($mlColorCritical, $euiColorEmptyShade); $mlColorMajorText: makeHighContrastColor($mlColorMajor, $euiColorEmptyShade); diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/_anomalies_table.scss b/x-pack/plugins/ml/public/application/components/anomalies_table/_anomalies_table.scss index e95eabdf510ad..813fbdcbe6f1f 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/_anomalies_table.scss +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/_anomalies_table.scss @@ -1,4 +1,4 @@ - // SASSTODO: This file has several direct EUI overwrites that need to be removed +// SASSTODO: This file has several direct EUI overwrites that need to be removed .ml-anomalies-table { .ml-icon-severity-critical, .ml-icon-severity-major, @@ -53,7 +53,7 @@ .detector-rules-icon { margin-left: $euiSizeXS; - opacity: 0.5; + opacity: .5; } } @@ -98,7 +98,7 @@ .anomaly-description-list { .euiDescriptionList__title { - margin-top: 0px; + margin-top: 0; flex-basis: 15%; font-size: inherit; line-height: 1.5rem; @@ -106,7 +106,7 @@ } .euiDescriptionList__description { - margin-top: 0px; + margin-top: 0; flex-basis: 85%; font-size: inherit; line-height: 1.5rem; @@ -116,4 +116,3 @@ } } - diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss b/x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss index 25be39f3ea2d7..4e04edd2e8e73 100644 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss @@ -35,7 +35,7 @@ } &__rowHighlighted { - background-color: transparentize($euiColorGhost, 0.9); + background-color: transparentize($euiColorGhost, .9); } &--hidden { diff --git a/x-pack/plugins/ml/public/application/components/controls/_controls.scss b/x-pack/plugins/ml/public/application/components/controls/_controls.scss index 2b46e2d2cd35d..d491e88dffa24 100644 --- a/x-pack/plugins/ml/public/application/components/controls/_controls.scss +++ b/x-pack/plugins/ml/public/application/components/controls/_controls.scss @@ -1,7 +1,7 @@ .ml-table-controls { label { font-size: $euiFontSizeXS; - padding: 0px 0px $euiSizeXS $euiSizeXS; + padding: 0 0 $euiSizeXS $euiSizeXS; } .ml-table-controls-element { diff --git a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss index 37d8871ab3562..e07c8a7b81692 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss +++ b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss @@ -10,7 +10,7 @@ color: $euiColorMediumShade; display: block; overflow-x: hidden; - margin: $euiSizeXS 0px 0px 0px; + margin: $euiSizeXS 0 0 0; font-style: italic; font-weight: normal; text-align: left; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss index 2e2f4e7af0a25..f6958ef66770e 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss +++ b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss @@ -3,4 +3,3 @@ text-transform: none; } } - diff --git a/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.scss b/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.scss index 34b181723df10..60c1a0820fbc9 100644 --- a/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.scss +++ b/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.scss @@ -9,7 +9,7 @@ } .filter-button { - opacity: 0.3; + opacity: .3; min-width: 14px; -webkit-transform: translateY(-1px); transform: translateY(-1px); diff --git a/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss b/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss index e4bb9c22fda82..d2d57544de41a 100644 --- a/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss +++ b/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss @@ -19,7 +19,7 @@ width: calc(100% - 34px); // SASSTODO: Calc proper value height: 22px; min-width: 70px; - margin-bottom: 0px; + margin-bottom: 0; color: $euiColorDarkShade; background-color: transparent; diff --git a/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss b/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss index d8d0f87a589a0..5cf57358b57ea 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss +++ b/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss @@ -7,33 +7,33 @@ } .mlJobSelector__ganttBar { - background-color: #79adda; + background-color: #79ADDA; height: $euiSizeM; border-radius: 2px; } .mlJobSelector__ganttBarBackEdge { height: $euiSize; - border-left: 1px solid #d6d6d6; - border-right: 1px solid #d6d6d6; + border-left: 1px solid #D6D6D6; + border-right: 1px solid #D6D6D6; margin-bottom: -14px; padding-top: $euiSizeS; } .mlJobSelector__ganttBarDashed { height: 1px; - border-top: 1px dashed #d6d6d6; + border-top: 1px dashed #D6D6D6; } .mlJobSelector__ganttBarRunning { background-image: linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, - transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); + rgba(255, 255, 255, .15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, + transparent 75%, + transparent); background-size: $euiSizeXXL $euiSizeXXL; animation: progress-bar-stripes 2s linear infinite; } diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss b/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss index d033a255a3d16..26767dd1e24db 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss +++ b/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss @@ -7,13 +7,13 @@ } .select-rule-action-panel { - padding: $euiSizeS 0px; + padding: $euiSizeS 0; // SASSTODO: Dangerous EUI overwrite .euiDescriptionList { .euiDescriptionList__title { flex-basis: 15%; - padding: 0px $euiSize; + padding: 0 $euiSize; } .euiDescriptionList__description { @@ -70,7 +70,7 @@ .condition-edit-value-field { width: 170px; height: 28px; - margin: 0px 2px; + margin: 0 2px; input { height: 28px; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss index 83d48c452d927..d1c507c5241d5 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss @@ -15,7 +15,7 @@ and just uses a legacy approach for a two column layout so we don't break IE11. */ .mlDataFrameAnalyticsClassification__confusionMatrix:after { - content: ""; + content: ''; display: table; clear: both; } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss index 5a4d1b3190402..9a169f6856f39 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss @@ -6,4 +6,3 @@ .mlExpandedRowJobMessages .euiTable, .mlExpandedRowJobMessages .euiTableRowCell { background-color: transparent !important; } - diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/_experimental_badge.scss b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/_experimental_badge.scss index 4c90d6beed51a..016d5cd579e3f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/_experimental_badge.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/_experimental_badge.scss @@ -2,6 +2,6 @@ font-size: 10px; vertical-align: middle; margin-bottom: 5px; - padding: 0px 20px; + padding: 0 20px; line-height: 20px; } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss index f6851fcb8eca4..d0af6d3f01d2f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss @@ -1,7 +1,7 @@ .card-container { display: inline-grid; display: -ms-inline-grid; - padding: 0px 10px 10px 0px; + padding: 0 10px 10px 0; } .ml-field-data-card { @@ -70,7 +70,7 @@ } .stat.heading { - padding-bottom: 0px; + padding-bottom: 0; } .stat.min, @@ -92,7 +92,7 @@ } .not-exist-message { - padding: 50px 30px 0px 30px; + padding: 50px 30px 0 30px; text-align: center; } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_fields_stats.scss b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_fields_stats.scss index ac04fad0ef3e3..5decacfe1b7b8 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_fields_stats.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_fields_stats.scss @@ -4,5 +4,3 @@ .field { margin-bottom: 10px; } - - diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/_results_view.scss b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/_results_view.scss index 6f5c2826515e6..1c5ea1dd26fd7 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/_results_view.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/_results_view.scss @@ -1,10 +1,10 @@ .results { - .euiDescriptionList{ + .euiDescriptionList { dd, dt { margin-top: 5px; } dd:nth-child(1), dt:nth-child(1), { - margin-top: 0px; + margin-top: 0; } } } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss index 45c0937b6723a..2ab26f1564a1f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss @@ -61,7 +61,7 @@ } .mlFieldDataCard__stats { - padding: $euiSizeS $euiSizeS 0px $euiSizeS; + padding: $euiSizeS $euiSizeS 0 $euiSizeS; text-align: center; } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/_index.scss index 64545c886a112..e9ecfc8b19100 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/_index.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/_index.scss @@ -1,4 +1,4 @@ -@import "components/field_data_expanded_row/number_content"; +@import 'components/field_data_expanded_row/number_content'; .mlDataVisualizerFieldExpandedRow { padding-left: $euiSize * 4; @@ -19,15 +19,15 @@ .mlDataVisualizer { .euiTableRow > .euiTableRowCell { - border-bottom: 0px; + border-bottom: 0; border-top: $euiBorderThin; } .euiTableRow-isExpandedRow { - .euiTableRowCell{ + .euiTableRowCell { background-color: $euiColorEmptyShade !important; - border-top: 0px; + border-top: 0; border-bottom: $euiBorderThin; &:hover { background-color: $euiColorEmptyShade !important; @@ -35,12 +35,11 @@ } } .mlDataVisualizerSummaryTable { - .euiTableRow > .euiTableRowCell{ - border-bottom: 0px; + .euiTableRow > .euiTableRowCell { + border-bottom: 0; } .euiTableHeaderCell { display: none; } } } - diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_index.scss index 54fa24604f8f8..fdc591a140fea 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_index.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_index.scss @@ -1 +1 @@ -@import "number_content"; +@import 'number_content'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_number_content.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_number_content.scss index 4b1da4b414045..066f405b39cd6 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_number_content.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_number_content.scss @@ -2,4 +2,3 @@ padding-top: $euiSizeXS; width: 100%; } - diff --git a/x-pack/plugins/ml/public/application/explorer/_explorer.scss b/x-pack/plugins/ml/public/application/explorer/_explorer.scss index d16a84a23c813..c08020325428d 100644 --- a/x-pack/plugins/ml/public/application/explorer/_explorer.scss +++ b/x-pack/plugins/ml/public/application/explorer/_explorer.scss @@ -11,7 +11,7 @@ $borderRadius: $euiBorderRadius / 2; } .results-container { - padding: 0px 0px $euiSize 0px; + padding: 0 0 $euiSize 0; // SASSTODO: Overwrite of bootstrap .col-xs-12 { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss b/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss index 32d589581929d..c89c91b744cdd 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss @@ -1,3 +1,4 @@ +// stylelint-disable selector-no-qualifying-type .ml-explorer-chart-container { overflow: hidden; @@ -12,10 +13,10 @@ } rect.selected-interval { - fill: rgba(200, 200, 200, 0.1); + fill: rgba(200, 200, 200, .1); stroke: $euiColorDarkShade; stroke-width: $euiSizeXS / 2; - stroke-opacity: 0.8; + stroke-opacity: .8; } rect.scheduled-event-marker { @@ -34,7 +35,7 @@ } .axis .tick line.ml-tick-emphasis { - stroke: rgba(0, 0, 0, 0.2); + stroke: rgba(0, 0, 0, .2); } .axis text { @@ -42,46 +43,41 @@ } .axis .tick line { - stroke: rgba(0, 0, 0, 0.05); + stroke: rgba(0, 0, 0, .05); stroke-width: 1px; } .values-line { fill: none; - stroke: #32a7c2; + stroke: #32A7C2; stroke-width: 2; } .values-dots circle, .values-dots-circle { - fill: #32a7c2; + fill: #32A7C2; stroke-width: 0; } .values-dots circle.values-dots-circle-blur { - fill: #aaa; - } - - .metric-value { - opacity: 1; - fill: #32a7c2; + fill: #AAA; } .metric-value { opacity: 1; fill: transparent; - stroke: #32a7c2; - stroke-width: 0px; + stroke: #32A7C2; + stroke-width: 0; } .anomaly-marker { stroke-width: 1px; - stroke: #aaaaaa; + stroke: #AAAAAA; } .anomaly-marker:hover { stroke-width: 6px; - stroke: #32a7c2; + stroke: #32A7C2; } .anomaly-marker.critical { @@ -101,13 +97,13 @@ } .anomaly-marker.low { - fill: #d2e9f7; + fill: #D2E9F7; } .metric-value:hover, .anomaly-marker:hover { stroke-width: 6px; - stroke-opacity: 0.65; + stroke-opacity: .65; } } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_charts_container.scss b/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_charts_container.scss index 13eb6da97131a..cc94e122254e3 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_charts_container.scss +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_charts_container.scss @@ -16,7 +16,7 @@ .chart-controls { label { - padding: 0px 0px 10px 0px; + padding: 0 0 10px 0; } } @@ -37,7 +37,7 @@ font-size: $euiFontSizeXS; td { - padding: 0px 0px 2px 0px; + padding: 0 0 2px 0; } table tr > td:first-child { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/_edit_job_flyout.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/_edit_job_flyout.scss index a625b1274c9b4..7018a991ce1dc 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/_edit_job_flyout.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/_edit_job_flyout.scss @@ -3,6 +3,6 @@ position: relative; float: right; top: -$euiSizeXS; - right: 0px; + right: 0; } } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/_job_details.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/_job_details.scss index 1f68ec67ded47..fe61be550ecac 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/_job_details.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/_job_details.scss @@ -22,20 +22,20 @@ background-color: $euiColorLightestShade; border: 1px solid $euiColorLightShade; border-radius: $euiBorderRadius; - margin: $euiSizeXS 0px; + margin: $euiSizeXS 0; .euiTable { background-color: transparent; .euiTableRow:first-child { .euiTableRowCell { - border-top: 0px; + border-top: 0; } } .euiTableRow:last-child { .euiTableRowCell { - border-bottom: 0px; + border-bottom: 0; } } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/_job_filter_bar.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/_job_filter_bar.scss index 92c3ae53cbeee..ecea309314dce 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/_job_filter_bar.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/_job_filter_bar.scss @@ -15,7 +15,7 @@ text-decoration: none; .inline-group { border: 1px solid $euiColorDarkShade; - box-shadow: 0px 1px 2px $euiColorMediumShade; + box-shadow: 0 1px 2px $euiColorMediumShade; } } } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/_job_group.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/_job_group.scss index 665f0a746e4a2..fc5bce54afb6a 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/_job_group.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/_job_group.scss @@ -4,7 +4,7 @@ padding: 2px 5px; border-radius: 2px; display: inline-block; - margin: 0px 3px; + margin: 0 3px; color: $euiColorEmptyShade; vertical-align: text-top; } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/_jobs_list.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/_jobs_list.scss index 421aa28cb9bf9..28b1a4259406a 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/_jobs_list.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/_jobs_list.scss @@ -45,7 +45,7 @@ & > tbody { td:nth-child(4) .euiTableCellContent { - padding: 0px; + padding: 0; } td:nth-child(10) { @@ -68,10 +68,10 @@ } .euiContextMenuPanel { - .euiContextMenuItem { - white-space: nowrap; - } - .euiContextMenuItem:last-child:not(.euiContextMenuItem-isDisabled) { + .euiContextMenuItem { + white-space: nowrap; + } + .euiContextMenuItem:last-child:not(.euiContextMenuItem-isDisabled) { color: $euiColorDanger; } } @@ -94,7 +94,7 @@ width: 1px; display: inline-block; vertical-align: middle; - margin: 0px $euiSizeXS; + margin: 0 $euiSizeXS; } .job-description { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/_multi_job_actions.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/_multi_job_actions.scss index 4298ce2691b68..131e4ea12ce50 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/_multi_job_actions.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/_multi_job_actions.scss @@ -1,4 +1,4 @@ - // SASSTODO: This looks like it needs some rewriting for all the pixel values +// SASSTODO: This looks like it needs some rewriting for all the pixel values .multi-select-actions { padding-right: $euiSizeS; padding-bottom: $euiSizeM; @@ -11,15 +11,14 @@ width: 1px; display: inline-block; vertical-align: middle; - margin: 0px 5px; + margin: 0 5px; } .actions-border-large { height: 35px; - margin: 0px 15px; + margin: 0 15px; margin-top: -5px; } - .results-button { margin-right: 5px; } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/_group_list.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/_group_list.scss index a0d4ed68fa323..b4fd5786fa2d9 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/_group_list.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/_group_list.scss @@ -1,21 +1,21 @@ - // SASSTODO: proper calcs all through this. Replace shadows +// SASSTODO: proper calcs all through this. Replace shadows .group-list { max-height: 350px; overflow: auto; .group-item { line-height: 18px; - padding: 6px 0px; + padding: 6px 0; border-bottom: $euiBorderThin; cursor: pointer; &:focus { - background-color: #eef6f9; + background-color: #EEF6F9; box-shadow: none; } .check { - // SASSTODO: proper calc + // SASSTODO: proper calc width: 20px; display: inline-block; } @@ -28,13 +28,13 @@ .group-item:hover { .inline-group { border: 1px solid $euiColorDarkShade; - // SASSTODO: Replace with eui shadow mixin - box-shadow: 0px 1px 2px $euiColorMediumShade; + // SASSTODO: Replace with eui shadow mixin + box-shadow: 0 1px 2px $euiColorMediumShade; } } .group-item:last-child { - margin-bottom: 0px; + margin-bottom: 0; border-bottom: none; } } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/new_group_input/_new_group_input.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/new_group_input/_new_group_input.scss index 943fc78a9d0fa..1c5a66d049e0e 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/new_group_input/_new_group_input.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/new_group_input/_new_group_input.scss @@ -1,3 +1,3 @@ .new-group-input { - padding-bottom: 0px; + padding-bottom: 0; } \ No newline at end of file diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/_time_range_selector.scss b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/_time_range_selector.scss index ac20a6abcc694..d946a0cf94032 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/_time_range_selector.scss +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/_time_range_selector.scss @@ -1,3 +1,4 @@ +// stylelint-disable selector-no-qualifying-type // SASSTODO: Looks like this could use a rewrite. Needs selectors .time-range-selector { .time-range-section-container { @@ -9,7 +10,7 @@ } .time-range-section { flex: 50%; - padding: 0px $euiSizeS; + padding: 0 $euiSizeS; border-right: $euiBorderThin; } @@ -47,12 +48,12 @@ display: block; } } - & > li.has-body.active { + & > li.has-body.active { & > a { - border-radius: $euiBorderRadius $euiBorderRadius 0px 0px; + border-radius: $euiBorderRadius $euiBorderRadius 0 0; } .react-datepicker { - border-radius: 0px 0px $euiBorderRadius $euiBorderRadius; + border-radius: 0 0 $euiBorderRadius $euiBorderRadius; border-top: none; } } diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_buttons.scss b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_buttons.scss index d235c832ffaf1..584d8ca5bdb37 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_buttons.scss +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_buttons.scss @@ -1,8 +1,6 @@ // Refresh button style -.managementJobsList{ +.managementJobsList { clear: both; } - - diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_expanded_row.scss b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_expanded_row.scss index aeddbbd1a5108..1909b597dfe9c 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_expanded_row.scss +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_expanded_row.scss @@ -23,20 +23,20 @@ background-color: $euiColorLightestShade; border: 1px solid $euiColorLightShade; border-radius: $euiBorderRadius; - margin: $euiSizeXS 0px; + margin: $euiSizeXS 0; .euiTable { background-color: transparent; .euiTableRow:first-child { .euiTableRowCell { - border-top: 0px; + border-top: 0; } } .euiTableRow:last-child { .euiTableRowCell { - border-bottom: 0px; + border-bottom: 0; } } diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_stats_bar.scss b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_stats_bar.scss index 05315fb4148de..b35f9afb42fdf 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_stats_bar.scss +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/_stats_bar.scss @@ -14,4 +14,3 @@ padding: 14px; background-color: $euiColorLightestShade; } - diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/_edit.scss b/x-pack/plugins/ml/public/application/settings/calendars/edit/_edit.scss index 4027f519bc915..087b8e4638aed 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/_edit.scss +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/_edit.scss @@ -3,6 +3,6 @@ max-width: map-get($euiBreakpoints, 'xl'); width: 100%; margin-top: $euiSize; - margin-bottom: $euiSize; + margin-bottom: $euiSize; } } diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/_filter_lists.scss b/x-pack/plugins/ml/public/application/settings/filter_lists/_filter_lists.scss index c1d5287575578..8afa0fed51890 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/_filter_lists.scss +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/_filter_lists.scss @@ -13,4 +13,3 @@ } } - diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/_edit.scss b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/_edit.scss index 55978f1fb704a..484dcb4f06b5a 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/_edit.scss +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/_edit.scss @@ -16,7 +16,7 @@ } .euiButtonEmpty .euiButtonEmpty__content { - padding: 0px $euiSizeXS; + padding: 0 $euiSizeXS; } } } diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss index 48774b9818226..af40e69af4124 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss @@ -1,3 +1,4 @@ +// stylelint-disable selector-no-qualifying-type .ml-time-series-explorer { color: $euiColorDarkShade; @@ -64,7 +65,7 @@ } .axis .tick line { - stroke: rgba(0, 0, 0, 0.1); + stroke: rgba(0, 0, 0, .1); } .chart-border { @@ -88,24 +89,24 @@ } .area.bounds { - fill: rgba(50, 167, 194, 0.25); // Needs variable + fill: rgba(50, 167, 194, .25); // Needs variable pointer-events: none; } .values-line { fill: none; - stroke: #32a7c2; // Needs variable + stroke: #32A7C2; // Needs variable stroke-width: 2; pointer-events: none; } .values-line.forecast { - stroke: #cca300; // Needs variable + stroke: #CCA300; // Needs variable pointer-events: none; } .values-dots circle { - fill: #32a7c2; // Needs variable + fill: #32A7C2; // Needs variable stroke-width: 0; } @@ -114,20 +115,20 @@ } .area.forecast { - fill: rgba(204, 163, 0, 0.25); // Needs variable + fill: rgba(204, 163, 0, .25); // Needs variable pointer-events: none; } .metric-value { opacity: 1; fill: transparent; - stroke: #32a7c2; - stroke-width: 0px; + stroke: #32A7C2; + stroke-width: 0; } .anomaly-marker { stroke-width: 1px; - stroke: #aaaaaa; // Needs variable + stroke: #AAAAAA; // Needs variable } .anomaly-marker.critical { @@ -147,15 +148,15 @@ } .anomaly-marker.low { - fill: #d2e9f7; // Needs variable + fill: #D2E9F7; // Needs variable } .metric-value:hover, .anomaly-marker:hover, .anomaly-marker.highlighted { stroke-width: 6px; - stroke-opacity: 0.65; - stroke: #32a7c2; + stroke-opacity: .65; + stroke: #32A7C2; } rect.scheduled-event-marker { @@ -168,7 +169,7 @@ .forecast { .metric-value, .metric-value:hover { - stroke: #cca300; + stroke: #CCA300; } } @@ -214,7 +215,7 @@ } .area.context { - fill: rgba(50, 167, 194, 0.25); // Needs variable + fill: rgba(50, 167, 194, .25); // Needs variable } .values-line { @@ -222,25 +223,25 @@ } .area.context.forecast { - fill: rgba(204, 163, 0, 0.25); // Needs variable + fill: rgba(204, 163, 0, .25); // Needs variable } .mask { polygon { - fill-opacity: 0.1; + fill-opacity: .1; } .area.bounds { - fill: #d6d6d6; + fill: #D6D6D6; } .values-line { stroke-width: 1; - stroke: #b8b8b8; + stroke: #B8B8B8; } .values-dots circle { - fill: #b8b8b8; + fill: #B8B8B8; stroke-width: 0; } } @@ -255,12 +256,8 @@ } .brush .extent { - stroke: #aaaaaa; fill-opacity: 0; shape-rendering: crispEdges; - } - - .brush .extent { stroke: $euiColorDarkShade; stroke-width: 2; cursor: move; @@ -292,11 +289,11 @@ } div.brush-handle-inner-left { - border-radius: $euiBorderRadius 0px 0px $euiBorderRadius; + border-radius: $euiBorderRadius 0 0 $euiBorderRadius; } div.brush-handle-inner-right { - border-radius: 0px $euiBorderRadius $euiBorderRadius 0px; + border-radius: 0 $euiBorderRadius $euiBorderRadius 0; } rect.brush-handle { diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss index 0c38d8e7ca171..8c2d139157602 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss @@ -15,8 +15,8 @@ $mlAnnotationBorderWidth: 2px; // Instead of different EUI colors we use opacity settings // here to avoid opaque layers on top of existing chart elements. -$mlAnnotationRectDefaultStrokeOpacity: 0.2; -$mlAnnotationRectDefaultFillOpacity: 0.05; +$mlAnnotationRectDefaultStrokeOpacity: .2; +$mlAnnotationRectDefaultFillOpacity: .05; .mlAnnotationRect { stroke: $euiColorFullShade; @@ -32,7 +32,7 @@ $mlAnnotationRectDefaultFillOpacity: 0.05; } .mlAnnotationRect-isHighlight { - stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity * 2;; + stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity * 2; transition: stroke-opacity $euiAnimSpeedFast; fill-opacity: $mlAnnotationRectDefaultFillOpacity * 2; diff --git a/x-pack/plugins/observability/public/components/app/news_feed/index.scss b/x-pack/plugins/observability/public/components/app/news_feed/index.scss index 1222fe489c732..82b1c88dc4038 100644 --- a/x-pack/plugins/observability/public/components/app/news_feed/index.scss +++ b/x-pack/plugins/observability/public/components/app/news_feed/index.scss @@ -1,3 +1,3 @@ -.obsNewsFeed__itemImg{ +.obsNewsFeed__itemImg { @include euiBottomShadowSmall; } \ No newline at end of file diff --git a/x-pack/plugins/remote_clusters/public/application/_hacks.scss b/x-pack/plugins/remote_clusters/public/application/_hacks.scss index b7d81885e716d..fd974400a7936 100644 --- a/x-pack/plugins/remote_clusters/public/application/_hacks.scss +++ b/x-pack/plugins/remote_clusters/public/application/_hacks.scss @@ -20,6 +20,6 @@ /** * 1. Prevent inherited flexbox layout from compressing this element on IE. */ - .remoteClustersConnectionStatus__message { +.remoteClustersConnectionStatus__message { flex-basis: auto !important; /* 1 */ } diff --git a/x-pack/plugins/searchprofiler/public/application/_app.scss b/x-pack/plugins/searchprofiler/public/application/_app.scss index dc48db9a8e751..6a2d1eb5e2189 100644 --- a/x-pack/plugins/searchprofiler/public/application/_app.scss +++ b/x-pack/plugins/searchprofiler/public/application/_app.scss @@ -37,19 +37,19 @@ $headerHeightOffset: $euiHeaderHeightCompensation * 3; flex-shrink: 1; } - .prfDevTool__main { - height: 100%; - order: 2; - margin-left: $euiSize; - display: flex; - overflow: hidden; - flex-direction: column; +.prfDevTool__main { + height: 100%; + order: 2; + margin-left: $euiSize; + display: flex; + overflow: hidden; + flex-direction: column; - // Make only the tab content scroll - .search-profiler-tabs { - flex-shrink: 0; - } + // Make only the tab content scroll + .search-profiler-tabs { + flex-shrink: 0; } +} @include euiPanel('.prfDevTool__main'); @@ -63,4 +63,3 @@ $headerHeightOffset: $euiHeaderHeightCompensation * 3; margin: $euiSize 0; } } - diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/_home.scss b/x-pack/plugins/snapshot_restore/public/application/sections/home/_home.scss index 741ee76985937..468ada5c2712a 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/_home.scss +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/_home.scss @@ -36,11 +36,11 @@ .snapshotRestorePolicyTableSnapshotFailureContainer { max-width: 200px; > .euiFlexItem:last-child { - min-width: 0; - .euiText { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } + min-width: 0; + .euiText { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } } diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/_wizard.scss b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/_wizard.scss index b235e9ebf7c21..1b493e9e74490 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/_wizard.scss +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/_wizard.scss @@ -4,7 +4,7 @@ .transform__steps { .euiStep__content { - padding-right: 0px; + padding-right: 0; } } diff --git a/yarn.lock b/yarn.lock index 8595ed43bf6d0..d8ea1d34b193a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -83,7 +83,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.7.5", "@babel/core@^7.9.0": +"@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== @@ -4170,6 +4170,21 @@ resolve-from "^5.0.0" store2 "^2.7.1" +"@stylelint/postcss-css-in-js@^0.37.2": + version "0.37.2" + resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz#7e5a84ad181f4234a2480803422a47b8749af3d2" + integrity sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA== + dependencies: + "@babel/core" ">=7.9.0" + +"@stylelint/postcss-markdown@^0.36.2": + version "0.36.2" + resolved "https://registry.yarnpkg.com/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz#0a540c4692f8dcdfc13c8e352c17e7bfee2bb391" + integrity sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ== + dependencies: + remark "^13.0.0" + unist-util-find-all-after "^3.0.2" + "@svgr/babel-plugin-add-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" @@ -6446,13 +6461,6 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= - dependencies: - acorn "^3.0.4" - acorn-jsx@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" @@ -6477,16 +6485,11 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn@5.X, acorn@^5.0.3, acorn@^5.5.0: +acorn@5.X, acorn@^5.0.3: version "5.7.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= - acorn@^6.0.1, acorn@^6.0.4, acorn@^6.4.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" @@ -6598,24 +6601,11 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59" integrity sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk= -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" - integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= - ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== -ajv@^4.7.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.5.5, ajv@^6.9.1: version "6.12.4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" @@ -6716,11 +6706,6 @@ ansi-colors@^1.0.1: dependencies: ansi-wrap "^0.1.0" -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - ansi-escapes@^3.0.0, ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -7376,7 +7361,7 @@ array.prototype.map@^1.0.1: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.4" -arrify@^1.0.0, arrify@^1.0.1: +arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= @@ -7485,6 +7470,11 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-cache@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/async-cache/-/async-cache-1.1.0.tgz#4a9a5a89d065ec5d8e5254bd9ee96ba76c532b5a" @@ -7597,7 +7587,7 @@ autobind-decorator@^1.3.4: resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-1.4.3.tgz#4c96ffa77b10622ede24f110f5dbbf56691417d1" integrity sha1-TJb/p3sQYi7eJPEQ9du/VmkUF9E= -autoprefixer@^9.6.1: +autoprefixer@^9.6.1, autoprefixer@^9.8.6: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== @@ -9020,13 +9010,6 @@ caller-callsite@^2.0.0: dependencies: callsites "^2.0.0" -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= - dependencies: - callsites "^0.2.0" - caller-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" @@ -9034,11 +9017,6 @@ caller-path@^2.0.0: dependencies: caller-callsite "^2.0.0" -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= - callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" @@ -9441,11 +9419,6 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== - cjs-module-lexer@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" @@ -9497,7 +9470,7 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== -cli-cursor@^1.0.1, cli-cursor@^1.0.2: +cli-cursor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= @@ -9664,6 +9637,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone-regexp@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" + integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== + dependencies: + is-regexp "^2.0.0" + clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -9895,7 +9875,7 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -commander@2, commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1, commander@^2.8.1, commander@^2.9.0: +commander@2, commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1, commander@^2.9.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -10001,7 +9981,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@^1.6.2, concat-stream@~1.6.0: +concat-stream@1.6.2, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@^1.6.2, concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -10315,6 +10295,17 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + cp-file@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-7.0.0.tgz#b9454cfd07fe3b974ab9ea0e5f29655791a9b8cd" @@ -11234,7 +11225,7 @@ debug-fabulous@1.X: memoizee "0.4.X" object-assign "4.X" -debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -11269,6 +11260,13 @@ debug@4.1.0: dependencies: ms "^2.1.1" +debug@^4.2.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -11478,19 +11476,6 @@ del-cli@^3.0.1: del "^5.1.0" meow "^6.1.1" -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - integrity sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag= - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - del@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" @@ -11819,7 +11804,7 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" -doctrine@1.5.0, doctrine@^1.2.2: +doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= @@ -12583,7 +12568,7 @@ es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-map@^0.1.3, es6-map@^0.1.5: +es6-map@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= @@ -12707,16 +12692,6 @@ escodegen@~1.2.0: optionalDependencies: source-map "~0.1.30" -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - eslint-config-prettier@^6.15.0: version "6.15.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" @@ -12959,45 +12934,6 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^2.7.0: - version "2.13.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.13.1.tgz#e4cc8fa0f009fb829aaae23855a29360be1f6c11" - integrity sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE= - dependencies: - chalk "^1.1.3" - concat-stream "^1.4.6" - debug "^2.1.1" - doctrine "^1.2.2" - es6-map "^0.1.3" - escope "^3.6.0" - espree "^3.1.6" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^1.1.1" - glob "^7.0.3" - globals "^9.2.0" - ignore "^3.1.2" - imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" - is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" - levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" - optionator "^0.8.1" - path-is-absolute "^1.0.0" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.6.0" - strip-json-comments "~1.0.1" - table "^3.7.8" - text-table "~0.2.0" - user-home "^2.0.0" - eslint@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" @@ -13041,14 +12977,6 @@ eslint@^6.8.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^3.1.6: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" - integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - espree@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" @@ -13208,6 +13136,13 @@ execa@^4.0.0, execa@^4.0.2: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execall@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" + integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== + dependencies: + clone-regexp "^2.1.0" + executable@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -13461,6 +13396,18 @@ fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.2: micromatch "^4.0.2" picomatch "^2.2.1" +fast-glob@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-parse@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" @@ -13498,6 +13445,11 @@ fast-stream-to-buffer@^1.0.0: dependencies: end-of-stream "^1.4.1" +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + fastest-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" @@ -13591,7 +13543,7 @@ figures@2.0.0, figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^1.3.5, figures@^1.7.0: +figures@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= @@ -13606,14 +13558,6 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^1.1.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-1.3.1.tgz#44c61ea607ae4be9c1402f41f44270cbfe334ff8" - integrity sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g= - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - file-entry-cache@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" @@ -13621,6 +13565,13 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +file-entry-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" + integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== + dependencies: + flat-cache "^3.0.4" + file-loader@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.2.0.tgz#5fb124d2369d7075d70a9a5abecd12e60a95215e" @@ -13822,16 +13773,6 @@ flagged-respawn@^1.0.0: resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" integrity sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c= -flat-cache@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" - integrity sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE= - dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" - flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -13841,6 +13782,14 @@ flat-cache@^2.0.1: rimraf "2.6.3" write "1.0.3" +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + flat@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" @@ -13853,6 +13802,11 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== +flatted@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.0.tgz#a5d06b4a8b01e3a63771daa5cb7a1903e2e57067" + integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA== + flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" @@ -14119,13 +14073,6 @@ fromentries@^1.2.0: resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.2.0.tgz#e6aa06f240d6267f913cea422075ef88b63e7897" integrity sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ== -front-matter@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-2.1.2.tgz#f75983b9f2f413be658c93dfd7bd8ce4078f5cdb" - integrity sha1-91mDufL0E75ljJPf172M5AePXNs= - dependencies: - js-yaml "^3.4.6" - fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -14152,15 +14099,6 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" - integrity sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE= - dependencies: - graceful-fs "^4.1.2" - jsonfile "^3.0.0" - universalify "^0.1.0" - fs-extra@^7.0.0, fs-extra@^7.0.1, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -14312,20 +14250,6 @@ geckodriver@^1.21.0: https-proxy-agent "5.0.0" tar "6.0.2" -generate-function@^2.0.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= - dependencies: - is-property "^1.0.0" - generic-pool@^3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.7.1.tgz#36fe5bb83e7e0e032e5d32cd05dc00f5ff119aa8" @@ -14406,6 +14330,11 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -14713,7 +14642,7 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" -globals@^9.18.0, globals@^9.2.0: +globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== @@ -14764,18 +14693,6 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - integrity sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0= - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -14801,6 +14718,11 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM= + globule@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" @@ -14817,12 +14739,12 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" -gonzales-pe-sl@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/gonzales-pe-sl/-/gonzales-pe-sl-4.2.3.tgz#6a868bc380645f141feeb042c6f97fcc71b59fe6" - integrity sha1-aoaLw4BkXxQf7rBCxvl/zHG1n+Y= +gonzales-pe@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== dependencies: - minimist "1.1.x" + minimist "^1.2.5" good-listener@^1.2.2: version "1.2.2" @@ -15732,6 +15654,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" integrity sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg== +hosted-git-info@^3.0.6: + version "3.0.7" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.7.tgz#a30727385ea85acfcee94e0aad9e368c792e036c" + integrity sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ== + dependencies: + lru-cache "^6.0.0" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -16097,7 +16026,7 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= -ignore@^3.1.2, ignore@^3.3.5: +ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== @@ -16107,7 +16036,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.5, ignore@^5.1.1, ignore@^5.1.4: +ignore@^5.0.5, ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -16154,7 +16083,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.1.0: +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -16174,6 +16103,11 @@ import-lazy@^2.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -16333,25 +16267,6 @@ inquirer@7.0.4: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34= - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^2.0.0" - figures "^1.3.5" - lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - inquirer@^7.0.0, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -16814,22 +16729,6 @@ is-map@^2.0.1: resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== -is-my-ip-valid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" - integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== - -is-my-json-valid@^2.10.0: - version "2.20.5" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.20.5.tgz#5eca6a8232a687f68869b7361be1612e7512e5df" - integrity sha512-VTPuvvGQtxvCeghwspQu1rBgjYUT6FGxPlvFKbYuFtgc4ADsX3U5ihZOYN0qyU6u+d4X9xXb0IT5O6QpXKt87A== - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - is-my-ip-valid "^1.0.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - is-native@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-native/-/is-native-1.0.1.tgz#cd18cc162e8450d683b5babe79ac99c145449675" @@ -16909,23 +16808,11 @@ is-odd@^2.0.0: dependencies: is-number "^4.0.0" -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= - is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" - integrity sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw= - dependencies: - is-path-inside "^1.0.0" - is-path-in-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" @@ -16933,13 +16820,6 @@ is-path-in-cwd@^2.0.0: dependencies: is-path-inside "^2.1.0" -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" - is-path-inside@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" @@ -16991,11 +16871,6 @@ is-promise@^2.1, is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= -is-property@^1.0.0, is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= - is-redirect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" @@ -17008,6 +16883,11 @@ is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.1: dependencies: has-symbols "^1.0.1" +is-regexp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" + integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== + is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" @@ -17015,11 +16895,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" @@ -18058,7 +17933,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.13.1, js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@^3.9.0, js-yaml@~3.13.1: +js-yaml@3.13.1, js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.9.0, js-yaml@~3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -18266,13 +18141,6 @@ jsonfile@^2.1.0: optionalDependencies: graceful-fs "^4.1.6" -jsonfile@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" - integrity sha1-pezG9l9T9mLEQVx2daAzHQmS7GY= - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -18307,11 +18175,6 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= -jsonpointer@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.1.0.tgz#501fb89986a2389765ba09e6053299ceb4f2c2cc" - integrity sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg== - jsonwebtoken@^8.3.0, jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" @@ -18497,10 +18360,10 @@ kleur@^3.0.2: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.2.tgz#83c7ec858a41098b613d5998a7b653962b504f68" integrity sha512-3h7B2WRT5LNXOtQiAaWonilegHcPSf9nLVXlSTci8lu1dZUuui61+EsPEZqSVxY7rXYmB2DVKMQILxaO5WL61Q== -known-css-properties@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.3.0.tgz#a3d135bbfc60ee8c6eacf2f7e7e6f2d4755e49a4" - integrity sha512-QMQcnKAiQccfQTqtBh/qwquGZ2XK/DXND1jrcN9M8gMMy99Gwla7GQjndVUsEqIaRyP6bsFRuhwRj5poafBGJQ== +known-css-properties@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.20.0.tgz#0570831661b47dd835293218381166090ff60e96" + integrity sha512-URvsjaA9ypfreqJ2/ylDr5MUERhJZ+DhguoWRr2xgS5C7aGCalXo+ewL+GixgKBfhT2vuL02nbIgNGqVWgTOYw== knuth-shuffle-seeded@^1.0.6: version "1.0.6" @@ -18980,11 +18843,6 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= -lodash.capitalize@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" - integrity sha1-+CbJtOKoUR2E46yinbBeGk87cqk= - lodash.clone@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" @@ -19105,11 +18963,6 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= -lodash.kebabcase@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" - integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= - lodash.map@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" @@ -19200,7 +19053,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.19, lodash@~4.17.20: +lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.19, lodash@~4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -19291,6 +19144,11 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +longest-streak@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" + integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== + longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -19546,6 +19404,11 @@ material-colors@^1.2.1: resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.5.tgz#5292593e6754cb1bcc2b98030e4e0d6a3afc9ea1" integrity sha1-UpJZPmdUyxvMK5gDDk4Najr8nqE= +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + md5.js@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" @@ -19598,6 +19461,17 @@ mdast-util-definitions@^4.0.0: dependencies: unist-util-visit "^2.0.0" +mdast-util-from-markdown@^0.8.0: + version "0.8.4" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.4.tgz#2882100c1b9fc967d3f83806802f303666682d32" + integrity sha512-jj891B5pV2r63n2kBTFh8cRI2uR9LQHsXG1zSDqfhXkIlDzrTcIlbB5+5aaYEkl8vOPIOPLf8VT7Ere1wWTMdw== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^2.0.0" + micromark "~2.11.0" + parse-entities "^2.0.0" + unist-util-stringify-position "^2.0.0" + mdast-util-to-hast@9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-9.1.0.tgz#6ef121dd3cd3b006bf8650b1b9454da0faf79ffe" @@ -19629,11 +19503,28 @@ mdast-util-to-hast@^10.0.0: unist-util-position "^3.0.0" unist-util-visit "^2.0.0" +mdast-util-to-markdown@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.1.tgz#0e07d3f871e056bffc38a0cf50c7298b56d9e0d6" + integrity sha512-4qJtZ0qdyYeexAXoOZiU0uHIFVncJAmCkHkSluAsvDaVWODtPyNEo9I1ns0T4ulxu2EHRH5u/bt1cV0pdHCX+A== + dependencies: + "@types/unist" "^2.0.0" + longest-streak "^2.0.0" + mdast-util-to-string "^2.0.0" + parse-entities "^2.0.0" + repeat-string "^1.0.0" + zwitch "^1.0.0" + mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" @@ -19766,6 +19657,23 @@ meow@^7.0.1: type-fest "^0.13.1" yargs-parser "^18.1.3" +meow@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.0.0.tgz#1aa10ee61046719e334ffdc038bb5069250ec99a" + integrity sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + merge-deep@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" @@ -19797,11 +19705,6 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -merge@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" - integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== - methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -19812,6 +19715,14 @@ microevent.ts@~0.1.1: resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== +micromark@~2.11.0: + version "2.11.2" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.2.tgz#e8b6a05f54697d2d3d27fc89600c6bc40dd05f35" + integrity sha512-IXuP76p2uj8uMg4FQc1cRE7lPCLsfAXuEfdjtdO55VRiFO1asrCSQ5g43NmPqFtRwzEnEhafRVzn2jg0UiKArQ== + dependencies: + debug "^4.0.0" + parse-entities "^2.0.0" + micromatch@3.1.10, micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -19941,7 +19852,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimist-options@^4.0.2: +minimist-options@4.1.0, minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== @@ -19950,7 +19861,7 @@ minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@0.0.8, minimist@1.1.x, minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0: +minimist@0.0.8, minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -20283,6 +20194,11 @@ ms@2.1.1, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + msgpackr-extract@^0.3.5, msgpackr-extract@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-0.3.6.tgz#f20c0a278e44377471b1fa2a3a75a32c87693755" @@ -20372,11 +20288,6 @@ mute-stdout@^1.0.0: resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= - mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -20840,6 +20751,16 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.0.tgz#1f8a7c423b3d2e85eb36985eaf81de381d01301a" + integrity sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw== + dependencies: + hosted-git-info "^3.0.6" + resolve "^1.17.0" + semver "^7.3.2" + validate-npm-package-license "^3.0.1" + normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -20857,6 +20778,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-selector@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" + integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= + normalize-url@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -21891,7 +21817,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -22182,11 +22108,6 @@ pluralize@3.1.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-3.1.0.tgz#84213d0a12356069daa84060c559242633161368" integrity sha1-hCE9ChI1YGnaqEBgxVkkJjMWE2g= -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" - integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= - pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -22257,6 +22178,20 @@ postcss-flexbugs-fixes@^4.1.0: dependencies: postcss "^7.0.0" +postcss-html@^0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-0.36.0.tgz#b40913f94eaacc2453fd30a1327ad6ee1f88b204" + integrity sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw== + dependencies: + htmlparser2 "^3.10.0" + +postcss-less@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-3.1.4.tgz#369f58642b5928ef898ffbc1a6e93c958304c5ad" + integrity sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA== + dependencies: + postcss "^7.0.14" + postcss-load-config@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.0.0.tgz#f1312ddbf5912cd747177083c5ef7a19d62ee484" @@ -22275,6 +22210,11 @@ postcss-loader@^3.0.0: postcss-load-config "^2.0.0" schema-utils "^1.0.0" +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= + postcss-modules-extract-imports@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" @@ -22315,6 +22255,33 @@ postcss-prefix-selector@^1.7.2: dependencies: postcss "^7.0.0" +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" + integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4= + +postcss-safe-parser@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" + integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== + dependencies: + postcss "^7.0.26" + +postcss-sass@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.4.4.tgz#91f0f3447b45ce373227a98b61f8d8f0785285a3" + integrity sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg== + dependencies: + gonzales-pe "^4.3.0" + postcss "^7.0.21" + +postcss-scss@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-2.1.1.tgz#ec3a75fa29a55e016b90bf3269026c53c1d2b383" + integrity sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA== + dependencies: + postcss "^7.0.6" + postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" @@ -22324,12 +22291,27 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" + integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + util-deprecate "^1.0.2" + +postcss-syntax@^0.36.2: + version "0.36.2" + resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c" + integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w== + postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: version "7.0.32" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== @@ -22338,6 +22320,15 @@ postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.32, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" +postcss@^7.0.35: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + potpack@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.1.tgz#d1b1afd89e4c8f7762865ec30bd112ab767e2ebf" @@ -23844,15 +23835,6 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -24270,6 +24252,13 @@ remark-parse@^5.0.0: vfile-location "^2.0.0" xtend "^4.0.1" +remark-parse@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" + integrity sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw== + dependencies: + mdast-util-from-markdown "^0.8.0" + remark-rehype@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-8.0.0.tgz#5a8afc8262a59d205fba21dafb27a673fb3b92fa" @@ -24293,6 +24282,22 @@ remark-squeeze-paragraphs@4.0.0: dependencies: mdast-squeeze-paragraphs "^4.0.0" +remark-stringify@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-9.0.1.tgz#576d06e910548b0a7191a71f27b33f1218862894" + integrity sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg== + dependencies: + mdast-util-to-markdown "^0.6.0" + +remark@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/remark/-/remark-13.0.0.tgz#d15d9bf71a402f40287ebe36067b66d54868e425" + integrity sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA== + dependencies: + remark-parse "^9.0.0" + remark-stringify "^9.0.0" + unified "^9.1.0" + remedial@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" @@ -24341,7 +24346,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= -repeat-string@^1.5.0, repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.0.0, repeat-string@^1.5.0, repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -24474,14 +24479,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -require-uncached@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - requirefresh@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/requirefresh/-/requirefresh-2.2.0.tgz#68298ae66af9da3d6843375adf8351dd29d73789" @@ -24536,11 +24533,6 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1: expand-tilde "^2.0.0" global-modules "^1.0.0" -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= - resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -24805,13 +24797,6 @@ rtl-css-js@^1.9.0: dependencies: "@babel/runtime" "^7.1.2" -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k= - dependencies: - once "^1.3.0" - run-async@^2.2.0, run-async@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" @@ -24841,11 +24826,6 @@ rw@~0.1.4: resolved "https://registry.yarnpkg.com/rw/-/rw-0.1.4.tgz#4903cbd80248ae0ede685bf58fd236a7a9b29a3e" integrity sha1-SQPL2AJIrg7eaFv1j9I2p6mymj4= -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= - rxjs-marbles@^5.0.6: version "5.0.6" resolved "https://registry.yarnpkg.com/rxjs-marbles/-/rxjs-marbles-5.0.6.tgz#e8e71df3b82b49603555f017f2fd3d8c359c4c24" @@ -24920,26 +24900,6 @@ sass-graph@2.2.5: scss-tokenizer "^0.2.3" yargs "^13.3.2" -sass-lint@^1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/sass-lint/-/sass-lint-1.12.1.tgz#630f69c216aa206b8232fb2aa907bdf3336b6d83" - integrity sha1-Yw9pwhaqIGuCMvsqqQe98zNrbYM= - dependencies: - commander "^2.8.1" - eslint "^2.7.0" - front-matter "2.1.2" - fs-extra "^3.0.1" - glob "^7.0.0" - globule "^1.0.0" - gonzales-pe-sl "^4.2.3" - js-yaml "^3.5.4" - known-css-properties "^0.3.0" - lodash.capitalize "^4.1.0" - lodash.kebabcase "^4.0.0" - merge "^1.2.0" - path-is-absolute "^1.0.0" - util "^0.10.3" - sass-loader@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" @@ -25376,11 +25336,6 @@ shell-quote@1.7.2, shell-quote@^1.4.2, shell-quote@^1.6.1: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== -shelljs@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" - integrity sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg= - shelljs@^0.8.3, shelljs@^0.8.4: version "0.8.4" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" @@ -25479,6 +25434,15 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slide@^1.1.5, slide@~1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -25802,6 +25766,11 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" +specificity@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" + integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== + split-on-first@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" @@ -26374,11 +26343,6 @@ strip-json-comments@^3.0.1, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" - integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= - strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -26403,6 +26367,11 @@ style-loader@^1.1.3, style-loader@^1.2.1: loader-utils "^2.0.0" schema-utils "^2.6.6" +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI= + style-to-object@0.3.0, style-to-object@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" @@ -26426,6 +26395,71 @@ styled-components@^5.1.0: shallowequal "^1.1.0" supports-color "^5.5.0" +stylelint-scss@^3.18.0: + version "3.18.0" + resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-3.18.0.tgz#8f06371c223909bf3f62e839548af1badeed31e9" + integrity sha512-LD7+hv/6/ApNGt7+nR/50ft7cezKP2HM5rI8avIdGaUWre3xlHfV4jKO/DRZhscfuN+Ewy9FMhcTq0CcS0C/SA== + dependencies: + lodash "^4.17.15" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +stylelint@13.8.0: + version "13.8.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.8.0.tgz#446765dbe25e3617f819a0165956faf2563ddc23" + integrity sha512-iHH3dv3UI23SLDrH4zMQDjLT9/dDIz/IpoFeuNxZmEx86KtfpjDOscxLTFioQyv+2vQjPlRZnK0UoJtfxLICXQ== + dependencies: + "@stylelint/postcss-css-in-js" "^0.37.2" + "@stylelint/postcss-markdown" "^0.36.2" + autoprefixer "^9.8.6" + balanced-match "^1.0.0" + chalk "^4.1.0" + cosmiconfig "^7.0.0" + debug "^4.2.0" + execall "^2.0.0" + fast-glob "^3.2.4" + fastest-levenshtein "^1.0.12" + file-entry-cache "^6.0.0" + get-stdin "^8.0.0" + global-modules "^2.0.0" + globby "^11.0.1" + globjoin "^0.1.4" + html-tags "^3.1.0" + ignore "^5.1.8" + import-lazy "^4.0.0" + imurmurhash "^0.1.4" + known-css-properties "^0.20.0" + lodash "^4.17.20" + log-symbols "^4.0.0" + mathml-tag-names "^2.1.3" + meow "^8.0.0" + micromatch "^4.0.2" + normalize-selector "^0.2.0" + postcss "^7.0.35" + postcss-html "^0.36.0" + postcss-less "^3.1.4" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^4.0.2" + postcss-sass "^0.4.4" + postcss-scss "^2.1.1" + postcss-selector-parser "^6.0.4" + postcss-syntax "^0.36.2" + postcss-value-parser "^4.1.0" + resolve-from "^5.0.0" + slash "^3.0.0" + specificity "^0.4.1" + string-width "^4.2.0" + strip-ansi "^6.0.0" + style-search "^0.1.0" + sugarss "^2.0.0" + svg-tags "^1.0.0" + table "^6.0.3" + v8-compile-cache "^2.2.0" + write-file-atomic "^3.0.3" + stylis-rule-sheet@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" @@ -26453,6 +26487,13 @@ success-symbol@^0.1.0: resolved "https://registry.yarnpkg.com/success-symbol/-/success-symbol-0.1.0.tgz#24022e486f3bf1cdca094283b769c472d3b72897" integrity sha1-JAIuSG878c3KCUKDt2nEctO3KJc= +sugarss@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-2.0.0.tgz#ddd76e0124b297d40bf3cca31c8b22ecb43bc61d" + integrity sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ== + dependencies: + postcss "^7.0.2" + superagent@3.8.2: version "3.8.2" resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.2.tgz#e4a11b9d047f7d3efeb3bbe536d9ec0021d16403" @@ -26589,6 +26630,11 @@ svg-parser@^2.0.2: resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= + svg-to-pdfkit@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/svg-to-pdfkit/-/svg-to-pdfkit-0.1.8.tgz#5921765922044843f0c1a5b25ec1ef8a4a33b8af" @@ -26657,18 +26703,6 @@ tabbable@^3.0.0: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-3.1.2.tgz#f2d16cccd01f400e38635c7181adfe0ad965a4a2" integrity sha512-wjB6puVXTYO0BSFtCmWQubA/KIn7Xvajw0x0l6eJUudMG/EAiJvIUnyNX6xO4NpGrJ16lbD0eUseB9WxW0vlpQ== -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" - integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8= - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - table@^5.2.3: version "5.2.3" resolved "https://registry.yarnpkg.com/table/-/table-5.2.3.tgz#cde0cc6eb06751c009efab27e8c820ca5b67b7f2" @@ -26679,6 +26713,16 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +table@^6.0.3: + version "6.0.4" + resolved "https://registry.yarnpkg.com/table/-/table-6.0.4.tgz#c523dd182177e926c723eb20e1b341238188aa0d" + integrity sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw== + dependencies: + ajv "^6.12.4" + lodash "^4.17.20" + slice-ansi "^4.0.0" + string-width "^4.2.0" + tapable@^0.1.8: version "0.1.10" resolved "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" @@ -26941,7 +26985,7 @@ text-hex@1.0.x: resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== -text-table@0.2.0, text-table@^0.2.0, text-table@~0.2.0: +text-table@0.2.0, text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= @@ -27543,6 +27587,11 @@ type-fest@^0.13.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + type-fest@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -27864,7 +27913,7 @@ unified@^6.1.5: x-is-function "^1.0.4" x-is-string "^0.1.0" -unified@^9.2.0: +unified@^9.1.0, unified@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== @@ -27932,6 +27981,13 @@ unist-builder@2.0.3, unist-builder@^2.0.0: resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== +unist-util-find-all-after@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz#fdfecd14c5b7aea5e9ef38d5e0d5f774eeb561f6" + integrity sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ== + dependencies: + unist-util-is "^4.0.0" + unist-util-generated@^1.0.0: version "1.1.5" resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.5.tgz#1e903e68467931ebfaea386dae9ea253628acd42" @@ -28286,13 +28342,6 @@ use@^2.0.0: isobject "^3.0.0" lazy-cache "^2.0.2" -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8= - dependencies: - os-homedir "^1.0.0" - utif@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/utif/-/utif-2.0.1.tgz#9e1582d9bbd20011a6588548ed3266298e711759" @@ -28333,7 +28382,7 @@ util.promisify@^1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" -util@0.10.3, util@^0.10.3: +util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= @@ -28399,7 +28448,7 @@ uuid@^8.0.0, uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== -v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1: +v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1, v8-compile-cache@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== @@ -29638,7 +29687,7 @@ write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^3.0.0: +write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== @@ -29676,13 +29725,6 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= - dependencies: - mkdirp "^0.5.1" - ws@^6.1.2, ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" @@ -29826,7 +29868,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.7.2: +yaml@^1.10.0, yaml@^1.7.2: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== @@ -29860,6 +29902,11 @@ yargs-parser@^20.0.0, yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.2.tgz#84562c6b1c41ccec2f13d346c7dd83f8d1a0dc70" integrity sha512-XmrpXaTl6noDsf1dKpBuUNCOHqjs0g3jRMXf/ztRxdOmb+er8kE5z5b55Lz3p5u2T8KJ59ENBnASS8/iapVJ5g== +yargs-parser@^20.2.3: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + yargs-unparser@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" From 7845e61438d0477e9d6f49ae120acbb7d714a042 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Fri, 15 Jan 2021 12:55:45 -0500 Subject: [PATCH 19/38] fix copy (#88481) --- src/plugins/dashboard/public/dashboard_strings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/dashboard/public/dashboard_strings.ts b/src/plugins/dashboard/public/dashboard_strings.ts index 9bede02c75b94..5747a383b4bcd 100644 --- a/src/plugins/dashboard/public/dashboard_strings.ts +++ b/src/plugins/dashboard/public/dashboard_strings.ts @@ -92,7 +92,7 @@ export const dashboardFeatureCatalog = { export const dashboardAddToLibraryAction = { getDisplayName: () => i18n.translate('dashboard.panel.AddToLibrary', { - defaultMessage: 'Add to library', + defaultMessage: 'Save to library', }), getSuccessMessage: (panelTitle: string) => i18n.translate('dashboard.panel.addToLibrary.successMessage', { From 9eb993aaef03302c0b876a2a15b6101b5b5b7e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Fri, 15 Jan 2021 19:33:38 +0100 Subject: [PATCH 20/38] [APM] Consistent terminology for latency and throughput (#88452) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Casper Hübertz --- x-pack/plugins/apm/common/alert_types.ts | 4 ++-- x-pack/plugins/apm/e2e/cypress/integration/apm.feature | 2 +- .../application/action_menu/alerting_popover_flyout.tsx | 4 ++-- .../public/components/alerting/register_apm_alerts.ts | 9 ++++----- .../app/ServiceMap/Popover/ServiceStatsList.tsx | 4 ++-- .../components/app/Settings/anomaly_detection/index.tsx | 2 +- .../public/components/app/TraceOverview/TraceList.tsx | 4 ++-- .../app/TransactionDetails/Distribution/index.tsx | 2 +- .../Waterfall/waterfall_helpers/waterfall_helpers.ts | 4 ++-- .../app/transaction_overview/TransactionList/index.tsx | 2 +- .../shared/charts/transaction_charts/ml_header.tsx | 2 +- .../lib/transactions/get_throughput_charts/transform.ts | 2 +- 12 files changed, 20 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/apm/common/alert_types.ts b/x-pack/plugins/apm/common/alert_types.ts index bb42c8acd167a..57236ea58063c 100644 --- a/x-pack/plugins/apm/common/alert_types.ts +++ b/x-pack/plugins/apm/common/alert_types.ts @@ -46,7 +46,7 @@ export const ALERT_TYPES_CONFIG: Record< }, [AlertType.TransactionDuration]: { name: i18n.translate('xpack.apm.transactionDurationAlert.name', { - defaultMessage: 'Transaction duration threshold', + defaultMessage: 'Latency threshold', }), actionGroups: [THRESHOLD_MET_GROUP], defaultActionGroupId: THRESHOLD_MET_GROUP_ID, @@ -55,7 +55,7 @@ export const ALERT_TYPES_CONFIG: Record< }, [AlertType.TransactionDurationAnomaly]: { name: i18n.translate('xpack.apm.transactionDurationAnomalyAlert.name', { - defaultMessage: 'Transaction duration anomaly', + defaultMessage: 'Latency anomaly', }), actionGroups: [THRESHOLD_MET_GROUP], defaultActionGroupId: THRESHOLD_MET_GROUP_ID, diff --git a/x-pack/plugins/apm/e2e/cypress/integration/apm.feature b/x-pack/plugins/apm/e2e/cypress/integration/apm.feature index 72c060c48f755..0cc8f00d48dfd 100644 --- a/x-pack/plugins/apm/e2e/cypress/integration/apm.feature +++ b/x-pack/plugins/apm/e2e/cypress/integration/apm.feature @@ -1,6 +1,6 @@ Feature: APM - Scenario: Transaction duration charts + Scenario: Transaction latency charts Given a user browses the APM UI application When the user inspects the opbeans-node service Then should redirect to correct path diff --git a/x-pack/plugins/apm/public/application/action_menu/alerting_popover_flyout.tsx b/x-pack/plugins/apm/public/application/action_menu/alerting_popover_flyout.tsx index 395233735a9d5..3204f4b183b4f 100644 --- a/x-pack/plugins/apm/public/application/action_menu/alerting_popover_flyout.tsx +++ b/x-pack/plugins/apm/public/application/action_menu/alerting_popover_flyout.tsx @@ -21,7 +21,7 @@ const alertLabel = i18n.translate('xpack.apm.home.alertsMenu.alerts', { }); const transactionDurationLabel = i18n.translate( 'xpack.apm.home.alertsMenu.transactionDuration', - { defaultMessage: 'Transaction duration' } + { defaultMessage: 'Latency' } ); const transactionErrorRateLabel = i18n.translate( 'xpack.apm.home.alertsMenu.transactionErrorRate', @@ -112,7 +112,7 @@ export function AlertingPopoverAndFlyout({ ], }, - // transaction duration panel + // latency panel { id: CREATE_TRANSACTION_DURATION_ALERT_PANEL_ID, title: transactionDurationLabel, diff --git a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts index 78d771aec13e0..1e35b10d83b7c 100644 --- a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts +++ b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts @@ -46,7 +46,7 @@ export function registerApmAlerts( 'xpack.apm.alertTypes.transactionDuration.description', { defaultMessage: - 'Alert when the duration of a specific transaction type in a service exceeds a defined threshold.', + 'Alert when the latency of a specific transaction type in a service exceeds a defined threshold.', } ), iconClass: 'bell', @@ -68,8 +68,8 @@ export function registerApmAlerts( - Service name: \\{\\{context.serviceName\\}\\} - Type: \\{\\{context.transactionType\\}\\} - Environment: \\{\\{context.environment\\}\\} -- Threshold: \\{\\{context.threshold\\}\\}ms -- Triggered value: \\{\\{context.triggerValue\\}\\} over the last \\{\\{context.interval\\}\\}`, +- Latency threshold: \\{\\{context.threshold\\}\\}ms +- Latency observed: \\{\\{context.triggerValue\\}\\} over the last \\{\\{context.interval\\}\\}`, } ), }); @@ -113,8 +113,7 @@ export function registerApmAlerts( description: i18n.translate( 'xpack.apm.alertTypes.transactionDurationAnomaly.description', { - defaultMessage: - 'Alert when the overall transaction duration of a service is considered anomalous.', + defaultMessage: 'Alert when the latency of a service is abnormal.', } ), iconClass: 'bell', diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx index cc41c254ffb50..377496cf2ac9b 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx @@ -41,7 +41,7 @@ export function ServiceStatsList({ title: i18n.translate( 'xpack.apm.serviceMap.avgTransDurationPopoverStat', { - defaultMessage: 'Trans. duration (avg.)', + defaultMessage: 'Latency (avg.)', } ), description: isNumber(transactionStats.avgTransactionDuration) @@ -52,7 +52,7 @@ export function ServiceStatsList({ title: i18n.translate( 'xpack.apm.serviceMap.avgReqPerMinutePopoverMetric', { - defaultMessage: 'Req. per minute (avg.)', + defaultMessage: 'Throughput (avg.)', } ), description: asTransactionRate(transactionStats.avgRequestsPerMinute), diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx index addfd64a9ef62..01544901f3849 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx @@ -75,7 +75,7 @@ export function AnomalyDetection() { {i18n.translate('xpack.apm.settings.anomalyDetection.descriptionText', { - defaultMessage: `Machine Learning's anomaly detection integration enables application health status indicators for services in each configured environment by identifying transaction duration anomalies.`, + defaultMessage: `Machine Learning's anomaly detection integration enables application health status indicators for services in each configured environment by identifying anomalies in latency.`, })} diff --git a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx index b216ab5498cf6..a2ab8a365c711 100644 --- a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx @@ -68,7 +68,7 @@ const traceListColumns: Array> = [ { field: 'averageResponseTime', name: i18n.translate('xpack.apm.tracesTable.avgResponseTimeColumnLabel', { - defaultMessage: 'Avg. response time', + defaultMessage: 'Latency (avg.)', }), sortable: true, dataType: 'number', @@ -91,7 +91,7 @@ const traceListColumns: Array> = [ 'xpack.apm.tracesTable.impactColumnDescription', { defaultMessage: - "The most used and slowest endpoints in your service. It's calculated by taking the relative average duration times the number of transactions per minute.", + 'The most used and slowest endpoints in your service. It is the result of multiplying latency and throughput', } )} > diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx index 8ab09eccd9bdb..a94c48f02c101 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx @@ -162,7 +162,7 @@ export function TransactionDistribution({ {i18n.translate( 'xpack.apm.transactionDetails.transactionsDurationDistributionChartTitle', { - defaultMessage: 'Transactions duration distribution', + defaultMessage: 'Latency distribution', } )}{' '} { parentId?: string; /** - * Duration in us + * Latency in us */ duration: number; diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/TransactionList/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/TransactionList/index.tsx index d2a3dc54c2a48..877a4d22aa628 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/TransactionList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/TransactionList/index.tsx @@ -126,7 +126,7 @@ export function TransactionList({ items, isLoading }: Props) { 'xpack.apm.transactionsTable.impactColumnDescription', { defaultMessage: - "The most used and slowest endpoints in your service. It's calculated by taking the relative average duration times the number of transactions per minute.", + 'The most used and slowest endpoints in your service. It is the result of multiplying latency and throughput', } )} /> diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx index 33dcbf02ccda7..89bf370c90ad6 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx @@ -62,7 +62,7 @@ export function MLHeader({ hasValidMlLicense, mlJobId }: Props) { 'xpack.apm.metrics.transactionChart.machineLearningTooltip', { defaultMessage: - 'The stream around the average duration shows the expected bounds. An annotation is shown for anomaly scores ≥ 75.', + 'The stream displays the expected bounds of the average latency. A red vertical annotation indicates anomalies with an anomaly score of 75 or above.', } )} /> diff --git a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts index 0ebf1446265fd..a12e36c0e9de4 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts @@ -37,7 +37,7 @@ export function getThroughputBuckets({ .map((bucket) => bucket.count.value) .reduce((a, b) => a + b, 0); - // calculate request/minute + // calculate average throughput const avg = docCountTotal / durationAsMinutes; return { key, dataPoints, avg }; From 73cd1a088cc2589f1164bf6959be352af2a7412d Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Fri, 15 Jan 2021 13:37:30 -0500 Subject: [PATCH 21/38] [Uptime] clear ping state when PingList component in unmounted (#88321) * clear ping state when PingList component in unmounted * update ping list content Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__snapshots__/ping_list.test.tsx.snap | 114 ----------------- .../monitor/ping_list/ping_list.test.tsx | 117 +++++++++++------- .../monitor/ping_list/ping_list.tsx | 16 +++ .../uptime/public/lib/helper/test_helpers.ts | 4 + .../uptime/public/state/actions/ping.ts | 2 + .../uptime/public/state/reducers/ping_list.ts | 6 +- 6 files changed, 98 insertions(+), 161 deletions(-) delete mode 100644 x-pack/plugins/uptime/public/components/monitor/ping_list/__snapshots__/ping_list.test.tsx.snap diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/__snapshots__/ping_list.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ping_list/__snapshots__/ping_list.test.tsx.snap deleted file mode 100644 index 7d7da0b7dd74c..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/__snapshots__/ping_list.test.tsx.snap +++ /dev/null @@ -1,114 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PingList component renders sorted list without errors 1`] = ` - - - - - -`; diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.test.tsx index d0fac09f683fc..60e65580027a3 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.test.tsx @@ -5,58 +5,58 @@ */ import React from 'react'; -import { shallowWithIntl } from '@kbn/test/jest'; import { PingList } from './ping_list'; import { Ping, PingsResponse } from '../../../../common/runtime_types'; import { ExpandedRowMap } from '../../overview/monitor_list/types'; import { rowShouldExpand, toggleDetails } from './columns/expand_row'; import * as pingListHook from './use_pings'; -import { mockReduxHooks } from '../../../lib/helper/test_helpers'; +import { mockDispatch } from '../../../lib/helper/test_helpers'; +import { render } from '../../../lib/helper/rtl_helpers'; -mockReduxHooks(); +mockDispatch(); describe('PingList component', () => { - let response: PingsResponse; + const defaultPings = [ + { + docId: 'fewjio21', + timestamp: '2019-01-28T17:47:08.078Z', + error: { + message: 'dial tcp 127.0.0.1:9200: connect: connection refused', + type: 'io', + }, + monitor: { + duration: { us: 1430 }, + id: 'auto-tcp-0X81440A68E839814F', + ip: '127.0.0.1', + name: '', + status: 'down', + type: 'tcp', + }, + }, + { + docId: 'fewjoo21', + timestamp: '2019-01-28T17:47:09.075Z', + error: { + message: 'dial tcp 127.0.0.1:9200: connect: connection refused', + type: 'io', + }, + monitor: { + duration: { us: 1370 }, + id: 'auto-tcp-0X81440A68E839814D', + ip: '255.255.255.0', + name: '', + status: 'down', + type: 'tcp', + }, + }, + ]; - beforeAll(() => { - response = { - total: 9231, - pings: [ - { - docId: 'fewjio21', - timestamp: '2019-01-28T17:47:08.078Z', - error: { - message: 'dial tcp 127.0.0.1:9200: connect: connection refused', - type: 'io', - }, - monitor: { - duration: { us: 1430 }, - id: 'auto-tcp-0X81440A68E839814F', - ip: '127.0.0.1', - name: '', - status: 'down', - type: 'tcp', - }, - }, - { - docId: 'fewjoo21', - timestamp: '2019-01-28T17:47:09.075Z', - error: { - message: 'dial tcp 127.0.0.1:9200: connect: connection refused', - type: 'io', - }, - monitor: { - duration: { us: 1370 }, - id: 'auto-tcp-0X81440A68E839814D', - ip: '127.0.0.1', - name: '', - status: 'down', - type: 'tcp', - }, - }, - ], - }; + const response: PingsResponse = { + total: 9231, + pings: defaultPings, + }; + beforeEach(() => { jest.spyOn(pingListHook, 'usePingsList').mockReturnValue({ ...response, error: undefined, @@ -65,9 +65,34 @@ describe('PingList component', () => { }); }); - it('renders sorted list without errors', () => { - const component = shallowWithIntl(); - expect(component).toMatchSnapshot(); + it('renders loading state when pings are loading', () => { + jest.spyOn(pingListHook, 'usePingsList').mockReturnValue({ + pings: [], + total: 0, + error: undefined, + loading: true, + failedSteps: { steps: [], checkGroup: '1-f-4d-4f' }, + }); + const { getByText } = render(); + expect(getByText('Loading history...')).toBeInTheDocument(); + }); + + it('renders no pings state when pings are not found', () => { + jest.spyOn(pingListHook, 'usePingsList').mockReturnValue({ + pings: [], + total: 0, + error: undefined, + loading: false, + failedSteps: { steps: [], checkGroup: '1-f-4d-4f' }, + }); + const { getByText } = render(); + expect(getByText('No history found')).toBeInTheDocument(); + }); + + it('renders list without errors', () => { + const { getByText } = render(); + expect(getByText(`${response.pings[0].monitor.ip}`)).toBeInTheDocument(); + expect(getByText(`${response.pings[1].monitor.ip}`)).toBeInTheDocument(); }); describe('toggleDetails', () => { @@ -139,7 +164,7 @@ describe('PingList component', () => { "us": 1370, }, "id": "auto-tcp-0X81440A68E839814D", - "ip": "127.0.0.1", + "ip": "255.255.255.0", "name": "", "status": "down", "type": "tcp", diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 75f261f1e42fa..3da788cc23a7a 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -25,6 +25,7 @@ import { PingTimestamp } from './columns/ping_timestamp'; import { FailedStep } from './columns/failed_step'; import { usePingsList } from './use_pings'; import { PingListHeader } from './ping_list_header'; +import { clearPings } from '../../../state/actions'; export const SpanWithMargin = styled.span` margin-right: 16px; @@ -54,6 +55,12 @@ export const PingList = () => { Object.keys(expandedRows).filter((e) => !pings.some(({ docId }) => docId === e)) ); + useEffect(() => { + return () => { + dispatch(clearPings()); + }; + }, [dispatch]); + useEffect(() => { const parsed = JSON.parse(expandedIdsToRemove); if (parsed.length) { @@ -200,6 +207,15 @@ export const PingList = () => { itemId="docId" itemIdToExpandedRowMap={expandedRows} pagination={pagination} + noItemsMessage={ + loading + ? i18n.translate('xpack.uptime.pingList.pingsLoadingMesssage', { + defaultMessage: 'Loading history...', + }) + : i18n.translate('xpack.uptime.pingList.pingsUnavailableMessage', { + defaultMessage: 'No history found', + }) + } onChange={(criteria: any) => { setPageSize(criteria.page!.size); setPageIndex(criteria.page!.index); diff --git a/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts b/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts index b56b55f9146ae..ad842e7f34d1f 100644 --- a/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts +++ b/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts @@ -62,6 +62,10 @@ export function mockReduxHooks(response?: any) { jest.spyOn(redux, 'useSelector').mockReturnValue(response); } +export function mockDispatch() { + jest.spyOn(redux, 'useDispatch').mockReturnValue(jest.fn()); +} + export function mockReactRouterDomHooks({ useParamsResponse }: { useParamsResponse: any }) { jest.spyOn(reactRouterDom, 'useParams').mockReturnValue(useParamsResponse); } diff --git a/x-pack/plugins/uptime/public/state/actions/ping.ts b/x-pack/plugins/uptime/public/state/actions/ping.ts index 70918a4cc70e5..af940ea00f3a2 100644 --- a/x-pack/plugins/uptime/public/state/actions/ping.ts +++ b/x-pack/plugins/uptime/public/state/actions/ping.ts @@ -12,6 +12,8 @@ import { GetPingsParams, } from '../../../common/runtime_types'; +export const clearPings = createAction('CLEAR PINGS'); + export const getPingHistogram = createAction('GET_PING_HISTOGRAM'); export const getPingHistogramSuccess = createAction('GET_PING_HISTOGRAM_SUCCESS'); export const getPingHistogramFail = createAction('GET_PING_HISTOGRAM_FAIL'); diff --git a/x-pack/plugins/uptime/public/state/reducers/ping_list.ts b/x-pack/plugins/uptime/public/state/reducers/ping_list.ts index 1fbdc6302f113..da05a3ed3a036 100644 --- a/x-pack/plugins/uptime/public/state/reducers/ping_list.ts +++ b/x-pack/plugins/uptime/public/state/reducers/ping_list.ts @@ -6,7 +6,7 @@ import { handleActions, Action } from 'redux-actions'; import { PingsResponse } from '../../../common/runtime_types'; -import { getPings, getPingsSuccess, getPingsFail } from '../actions'; +import { clearPings, getPings, getPingsSuccess, getPingsFail } from '../actions'; export interface PingListState { pingList: PingsResponse; @@ -26,6 +26,10 @@ type PingListPayload = PingsResponse & Error; export const pingListReducer = handleActions( { + [String(clearPings)]: (state) => ({ + ...state, + ...initialState, + }), [String(getPings)]: (state) => ({ ...state, loading: true, From 68288ebfdb31fadaff79c0ebceab0fd8eafbeb83 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 15 Jan 2021 13:43:27 -0500 Subject: [PATCH 22/38] [Monitoring] Change cloud messaging on no data page (#88375) * WIP * Update tests * Update copy * Fix translations and update copy * Fix tests and update copy --- .../no_data/blurbs/cloud_deployment.js | 64 +++++++++---------- .../__snapshots__/exporters.test.js.snap | 35 ++++++---- .../public/components/no_data/no_data.js | 49 +++++++++++++- .../__snapshots__/reason_found.test.js.snap | 35 ++++++---- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 6 files changed, 124 insertions(+), 63 deletions(-) diff --git a/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js b/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js index 073c84ee382aa..c01243fdeec47 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js +++ b/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js @@ -4,45 +4,41 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; -import { EuiTitle, EuiText, EuiTextColor, EuiLink, EuiSpacer } from '@elastic/eui'; +import React from 'react'; +import { EuiText, EuiTextColor, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; export const CloudDeployment = () => { return ( - - -

+ + +

-

-
- - - -

- - - cloud dashboard. - {' '} - - - the documentation. - -

-
-
-
+ + Elasticsearch Service Console + {' '} + + + Logs and metrics + {' '} + + + the documentation page. + +

+ + ); }; diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap index ed45933fa9471..99f5a979c812b 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap @@ -87,29 +87,40 @@ Array [ exports[`ExplainExportersCloud should explain about xpack.monitoring.exporters setting in a cloud environment 1`] = ` Array [ -

- Your monitoring data is not available here. -

, -

- Please return to your + Configure monitoring through + + Elasticsearch Service Console + + + (opens in a new tab or window) + + + Go to - cloud dashboard. + Logs and metrics - For more information on Monitoring in Elastic Cloud, please see + section for a deployment to configure monitoring. For more information visit - the documentation. + the documentation page. + +

+ +

+ + + + + + +

+ +

+
+ + +

+ +

+
+
+ + +
+
+ + ); + } + if (useInternalCollection) { return ( diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap index 9e3b7c0e25d5d..e3d25f97c9f78 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap @@ -162,29 +162,40 @@ Array [ exports[`ReasonFound should load ExplainExportersCloud component 1`] = ` Array [ -

- Your monitoring data is not available here. -

, -

- Please return to your + Configure monitoring through + + Elasticsearch Service Console + + + (opens in a new tab or window) + + + Go to - cloud dashboard. + Logs and metrics - For more information on Monitoring in Elastic Cloud, please see + section for a deployment to configure monitoring. For more information visit - the documentation. + the documentation page. Date: Fri, 15 Jan 2021 11:45:04 -0700 Subject: [PATCH 23/38] Change DELETE to POST for _bulk_delete to avoid incompatibility issues (#87914) ## Summary Changes `DELETE` to `POST` for _bulk_delete on the client only for a variety of reasons. According to the RFC, not all servers and proxies need to honor DELETE having a body. From: https://tools.ietf.org/html/rfc7231 ``` A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request. ``` Within at least one proxy, h2o2, we have found that it does indeed change request headers which will cause NodeJS to not attach the body of a `DELETE`: https://github.com/hapijs/h2o2/issues/124 Also from other communities such as OpenAPI where they debated this, they allow it but discourage it for reasons outlined there that I will not repeat here: https://github.com/OAI/OpenAPI-Specification/pull/1937 Elastic Search API's and other Kibana API's use `POST` rather than `DELETE` for their bodies that are attached to `DELETE`: https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html We still support bodies in `DELETE` and `POST` but are just changing the web client to utilize `POST` moving forward. ### Checklist Reviewed and we already have unit tests and end to end tests for these use cases so we are good with just updating them. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../detections/containers/detection_engine/rules/api.test.ts | 2 +- .../public/detections/containers/detection_engine/rules/api.ts | 2 +- .../server/lib/detection_engine/scripts/delete_bulk.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts index e94cc8845c5a5..fce9974b458c5 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts @@ -377,7 +377,7 @@ describe('Detections Rules API', () => { await deleteRules({ ids: ['mySuperRuleId', 'mySuperRuleId_II'] }); expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_bulk_delete', { body: '[{"id":"mySuperRuleId"},{"id":"mySuperRuleId_II"}]', - method: 'DELETE', + method: 'POST', }); }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts index a5dddd6d9afd3..da33b7841c7a9 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts @@ -205,7 +205,7 @@ export const enableRules = async ({ ids, enabled }: EnableRulesProps): Promise => KibanaServices.get().http.fetch(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, { - method: 'DELETE', + method: 'POST', body: JSON.stringify(ids.map((id) => ({ id }))), }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/delete_bulk.sh b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/delete_bulk.sh index 8f540e14ecdf1..6264a8e017ce3 100755 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/delete_bulk.sh +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/delete_bulk.sh @@ -17,6 +17,6 @@ curl -s -k \ -H 'Content-Type: application/json' \ -H 'kbn-xsrf: 123' \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X DELETE ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/_bulk_delete \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/_bulk_delete \ -d @${RULES} \ | jq .; From 208c76889de705770ede2d2b976d63e2ffc4657e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Fri, 15 Jan 2021 20:00:32 +0100 Subject: [PATCH 24/38] [Security Solution] Fix Timeline event details layout (#88377) --- .../timelines/components/timeline/expandable_event/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx index 27d28aa525d56..9a15d5dadcc94 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx @@ -58,7 +58,10 @@ const StyledFlexGroup = styled(EuiFlexGroup)` `; const StyledEuiFlexItem = styled(EuiFlexItem)` - overflow: hidden; + &.euiFlexItem { + flex: 1 0 0; + overflow: hidden; + } `; export const ExpandableEventTitle = React.memo( From 039b2ff05223ea93d9a43eaecdd17e43a3d442a7 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 15 Jan 2021 12:45:51 -0700 Subject: [PATCH 25/38] [Docs] clean-up vega map reference documenation (#88487) * [Docs] clean-up vega map reference documenation * Update docs/user/dashboard/vega-reference.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/user/dashboard/vega-reference.asciidoc | 58 ++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/user/dashboard/vega-reference.asciidoc b/docs/user/dashboard/vega-reference.asciidoc index d6593143e4f6d..f05d58918a2eb 100644 --- a/docs/user/dashboard/vega-reference.asciidoc +++ b/docs/user/dashboard/vega-reference.asciidoc @@ -216,24 +216,8 @@ on the currently picked range: `"interval": {"%autointerval%": 10}` will try to get about 10-15 data points (buckets). [float] -[[vega-esmfiles]] -=== Access Elastic Map Service files - -experimental[] Access the Elastic Map Service files via the same mechanism: - -[source,yaml] ----- -url: { - // "type" defaults to "elasticsearch" otherwise - type: emsfile - // Name of the file, exactly as in the Region map visualization - name: World Countries -} -// The result is a geojson file, get its features to use -// this data source with the "shape" marks -// https://vega.github.io/vega/docs/marks/shape/ -format: {property: "features"} ----- +[[vega-with-a-map]] +=== Vega with a Map To enable Maps, the graph must specify `type=map` in the host configuration: @@ -282,6 +266,22 @@ Additionally, you can use `latitude`, `longitude`, and `zoom` signals. These signals can be used in the graph, or can be updated to modify the position of the map. +experimental[] You can use Vega's https://vega.github.io/vega/docs/data/[data] element to access https://www.elastic.co/elastic-maps-service[Elastic Maps Service (EMS)] vector shapes of administrative boundaries in your Vega map by setting `url.data` to `emsFile`: + +[source,yaml] +---- +url: { + // "type" defaults to "elasticsearch" otherwise + type: emsfile + // Name of the file, exactly as in the Region map visualization + name: World Countries +} +// The result is a geojson file, get its features to use +// this data source with the "shape" marks +// https://vega.github.io/vega/docs/marks/shape/ +format: {property: "features"} +---- + [float] [[vega-tooltip]] ==== Additional tooltip styling @@ -308,22 +308,22 @@ a configuration option for changing the tooltip position and padding: [[vega-url-loading]] ==== Advanced setting to enable URL loading from any domain -Vega can load data from any URL, but this is disabled by default in {kib}. +Vega can load data from any URL, but this is disabled by default in {kib}. To change this, set `vis_type_vega.enableExternalUrls: true` in `kibana.yml`, then restart {kib}. [float] [[vega-inspector]] ==== Vega Inspector -Use the contextual *Inspect* tool to gain insights into different elements. +Use the contextual *Inspect* tool to gain insights into different elements. For Vega visualizations, there are two different views: *Request* and *Vega debug*. [float] [[inspect-elasticsearch-requests]] ===== Inspect {es} requests -Vega uses the {ref}/search-search.html[{es} search API] to get documents and aggregation -results from {es}. To troubleshoot these requests, click *Inspect*, which shows the most recent requests. +Vega uses the {ref}/search-search.html[{es} search API] to get documents and aggregation +results from {es}. To troubleshoot these requests, click *Inspect*, which shows the most recent requests. In case your specification has more than one request, you can switch between the views using the *View* dropdown. [role="screenshot"] @@ -333,10 +333,10 @@ image::visualize/images/vega_tutorial_inspect_requests.png[] [[vega-debugging]] ===== Vega debugging -With the *Vega debug* view, you can inspect the *Data sets* and *Signal Values* runtime data. - -The runtime data is read from the -https://vega.github.io/vega/docs/api/debugging/#scope[runtime scope]. +With the *Vega debug* view, you can inspect the *Data sets* and *Signal Values* runtime data. + +The runtime data is read from the +https://vega.github.io/vega/docs/api/debugging/#scope[runtime scope]. [role="screenshot"] image::visualize/images/vega_tutorial_inspect_data_sets.png[] @@ -348,15 +348,15 @@ the <>. [[asking-for-help-with-a-vega-spec]] ===== Asking for help with a Vega spec -Because of the dynamic nature of the data in {es}, it is hard to help you with +Because of the dynamic nature of the data in {es}, it is hard to help you with Vega specs unless you can share a dataset. To do this, click *Inspect*, select the *Vega debug* view, then select the *Spec* tab: [role="screenshot"] image::visualize/images/vega_tutorial_getting_help.png[] -To copy the response, click *Copy to clipboard*. Paste the copied data to -https://gist.github.com/[gist.github.com], possibly with a .json extension. Use the [raw] button, +To copy the response, click *Copy to clipboard*. Paste the copied data to +https://gist.github.com/[gist.github.com], possibly with a .json extension. Use the [raw] button, and share that when asking for help. [float] From b9106282ec2a6fd5891d32a9113f713841b33f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Fri, 15 Jan 2021 21:01:53 +0100 Subject: [PATCH 26/38] [APM] Disable Create custom link button on Transaction details page for read-only users --- .../plugins/apm/common/custom_link/index.ts | 8 +++++ .../CustomLink/CreateCustomLinkButton.tsx | 14 ++------- .../CustomLinkToolbar.test.tsx | 20 ++++++++++-- .../CustomLinkToolbar.tsx | 30 +++++++++++------- .../CustomLinkMenuSection/index.tsx | 31 +++++++++++++------ .../TransactionActionMenu.test.tsx | 20 ++++++++++-- 6 files changed, 86 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/apm/common/custom_link/index.ts b/x-pack/plugins/apm/common/custom_link/index.ts index bc0ffefd79c4d..00162f50d889a 100644 --- a/x-pack/plugins/apm/common/custom_link/index.ts +++ b/x-pack/plugins/apm/common/custom_link/index.ts @@ -12,3 +12,11 @@ export const INVALID_LICENSE = i18n.translate( "To create custom links, you must be subscribed to an Elastic Gold license or above. With it, you'll have the ability to create custom links to improve your workflow when analyzing your services.", } ); + +export const NO_PERMISSION_LABEL = i18n.translate( + 'xpack.apm.settings.customizeUI.customLink.noPermissionTooltipLabel', + { + defaultMessage: + "Your user role doesn't have permissions to create custom links", + } +); diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx index 3b4c127aab1e5..0a3d8f8b1e510 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx @@ -6,24 +6,14 @@ import { EuiButton, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { NO_PERMISSION_LABEL } from '../../../../../../common/custom_link'; import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; export function CreateCustomLinkButton({ onClick }: { onClick: () => void }) { const { core } = useApmPluginContext(); const canSave = core.application.capabilities.apm.save; return ( - + - {children} + + {children} + ); } diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.tsx index 36b370b4069ae..0fe3c461a4937 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.tsx @@ -12,6 +12,8 @@ import { EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { NO_PERMISSION_LABEL } from '../../../../../common/custom_link'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { APMLink } from '../../Links/apm/APMLink'; export function CustomLinkToolbar({ @@ -21,6 +23,9 @@ export function CustomLinkToolbar({ onClickCreate: () => void; showCreateButton?: boolean; }) { + const { core } = useApmPluginContext(); + const canSave = !!core.application.capabilities.apm.save; + return ( @@ -42,17 +47,20 @@ export function CustomLinkToolbar({ {showCreateButton && ( - - - {i18n.translate('xpack.apm.customLink.buttom.create.title', { - defaultMessage: 'Create', - })} - - + + + + {i18n.translate('xpack.apm.customLink.buttom.create.title', { + defaultMessage: 'Create', + })} + + + )} diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.tsx index a32302d246423..ae22718af8b57 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.tsx @@ -13,6 +13,9 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; +import { EuiToolTip } from '@elastic/eui'; +import { NO_PERMISSION_LABEL } from '../../../../../common/custom_link'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { ActionMenuDivider, Section, @@ -147,6 +150,9 @@ function BottomSection({ toggleShowAll: () => void; onClickCreate: () => void; }) { + const { core } = useApmPluginContext(); + const canSave = !!core.application.capabilities.apm.save; + if (status === FETCH_STATUS.LOADING) { return ; } @@ -154,7 +160,7 @@ function BottomSection({ // render empty prompt if there are no custom links if (isEmpty(customLinks)) { return ( - + {i18n.translate('xpack.apm.customLink.empty', { @@ -163,15 +169,20 @@ function BottomSection({ })} - - {i18n.translate('xpack.apm.customLink.buttom.create', { - defaultMessage: 'Create custom link', - })} - + + + + + {i18n.translate('xpack.apm.customLink.buttom.create', { + defaultMessage: 'Create custom link', + })} + + ); diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.test.tsx index 6ff395db594f1..48c863b460482 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.test.tsx @@ -9,7 +9,11 @@ import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { License } from '../../../../../licensing/common/license'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; -import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context'; +import { + mockApmPluginContextValue, + MockApmPluginContextWrapper, +} from '../../../context/apm_plugin/mock_apm_plugin_context'; import { LicenseContext } from '../../../context/license/license_context'; import * as hooks from '../../../hooks/use_fetcher'; import * as apmApi from '../../../services/rest/createCallApmApi'; @@ -20,10 +24,22 @@ import { import { TransactionActionMenu } from './TransactionActionMenu'; import * as Transactions from './__fixtures__/mockData'; +function getMockAPMContext({ canSave }: { canSave: boolean }) { + return ({ + ...mockApmPluginContextValue, + core: { + ...mockApmPluginContextValue.core, + application: { capabilities: { apm: { save: canSave }, ml: {} } }, + }, + } as unknown) as ApmPluginContextValue; +} + function Wrapper({ children }: { children?: React.ReactNode }) { return ( - {children} + + {children} + ); } From 5b38816a97b4f2bdd43700b46a94a5371cfc90ae Mon Sep 17 00:00:00 2001 From: Constance Date: Fri, 15 Jan 2021 12:39:26 -0800 Subject: [PATCH 27/38] [Enterprise Search] Automatically mock shared logic files (#88494) * Update shared logic mocks to automatically mock their exports * Update FlashMessages to also mock its helper fns * Fix tests that were relying on component exports from shared logic files * Fix broken flash_messages tests - Caused by leaking auto mocks - work around this by importing from files directly and not from index.ts - Also clean up / use new auto mocks (e.g. for kibana) - Convert old instances of useValues mock to setMockValues * [AS] Update AnalyticsLogic test to use new auto mockers + move LogicMounter and destructured mock values to top of describe block * [AS] Update CredentialsLogic + udpate to use new clearFlashMessages helper * [AS] Update documents logic tests * [AS] Update engines logic tests * [AS] Update log retention logic test * [Shared] Update IndexingStatus tests + update to use LogicMounter * [Shared] Update telemetry logic test * [WS] Update AddSourceLogic + update to use new clearFlashMessages helper * [WS] Update groups logic files + update to use new flash message helpers * [WS] Update OverviewLogic test * [WS] Update AddSource component test + consolidate mocks imports * [Shared] Clean up KibanaLogic imports - all jest.mocks come along from the ride when the __mocks__ folder is imported, so HttpLogic is now automatically already mocked * [AS] Update EngineRouter test --- .../__mocks__/flash_messages_logic.mock.ts | 18 +++ .../applications/__mocks__/http_logic.mock.ts | 4 + .../public/applications/__mocks__/index.ts | 6 +- .../__mocks__/kibana_logic.mock.ts | 4 + .../__mocks__/licensing_logic.mock.ts | 4 + .../__mocks__/telemetry_logic.mock.ts | 5 + .../analytics/analytics_logic.test.ts | 62 ++++---- .../credentials/credentials_logic.test.ts | 29 ++-- .../credentials/credentials_logic.ts | 4 +- .../document_creation_logic.test.ts | 26 +--- .../documents/document_detail_logic.test.ts | 35 ++--- .../components/engine/engine_logic.test.ts | 10 +- .../components/engine/engine_router.test.tsx | 8 +- .../engine_overview_logic.test.ts | 23 ++- .../components/engines/engines_logic.test.ts | 20 ++- .../log_retention/log_retention_logic.test.ts | 23 ++- .../flash_messages/flash_messages.test.tsx | 11 +- .../flash_messages_logic.test.ts | 14 +- .../flash_messages/handle_api_errors.test.ts | 14 +- .../flash_messages/handle_api_errors.ts | 2 +- .../set_message_helpers.test.ts | 12 +- .../flash_messages/set_message_helpers.ts | 2 +- .../indexing_status_logic.test.ts | 36 ++--- .../shared/kibana/kibana_logic.test.ts | 5 +- .../shared/telemetry/telemetry_logic.test.ts | 11 +- .../components/add_source/add_source.test.tsx | 21 +-- .../add_source/add_source_logic.test.ts | 107 ++++++-------- .../components/add_source/add_source_logic.ts | 8 +- .../views/groups/group_logic.test.ts | 136 +++++++----------- .../views/groups/group_logic.ts | 18 ++- .../views/groups/groups_logic.test.ts | 66 +++------ .../views/groups/groups_logic.ts | 10 +- .../views/overview/overview_logic.test.ts | 7 +- 33 files changed, 318 insertions(+), 443 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/flash_messages_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/flash_messages_logic.mock.ts index a610ea0238ac0..f36a565ac5416 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/flash_messages_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/flash_messages_logic.mock.ts @@ -15,3 +15,21 @@ export const mockFlashMessagesActions = { setQueuedMessages: jest.fn(), clearQueuedMessages: jest.fn(), }; + +export const mockFlashMessageHelpers = { + flashAPIErrors: jest.fn(), + setSuccessMessage: jest.fn(), + setErrorMessage: jest.fn(), + setQueuedSuccessMessage: jest.fn(), + setQueuedErrorMessage: jest.fn(), + clearFlashMessages: jest.fn(), +}; + +jest.mock('../shared/flash_messages', () => ({ + ...(jest.requireActual('../shared/flash_messages') as object), + ...mockFlashMessageHelpers, + FlashMessagesLogic: { + values: mockFlashMessagesValues, + actions: mockFlashMessagesActions, + }, +})); diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/http_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/http_logic.mock.ts index e77863c70c23a..4b151d8676734 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/http_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/http_logic.mock.ts @@ -11,3 +11,7 @@ export const mockHttpValues = { errorConnecting: false, readOnlyMode: false, }; + +jest.mock('../shared/http', () => ({ + HttpLogic: { values: mockHttpValues }, +})); diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts index 85b2821cd40ee..f4e13eca11a5e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts @@ -9,7 +9,11 @@ export { mockKibanaValues } from './kibana_logic.mock'; export { mockLicensingValues } from './licensing_logic.mock'; export { mockHttpValues } from './http_logic.mock'; export { mockTelemetryActions } from './telemetry_logic.mock'; -export { mockFlashMessagesValues, mockFlashMessagesActions } from './flash_messages_logic.mock'; +export { + mockFlashMessagesValues, + mockFlashMessagesActions, + mockFlashMessageHelpers, +} from './flash_messages_logic.mock'; export { mockAllValues, mockAllActions, diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts index e1e20adbe5759..15870001d9e44 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts @@ -20,3 +20,7 @@ export const mockKibanaValues = { setDocTitle: jest.fn(), renderHeaderActions: jest.fn(), }; + +jest.mock('../shared/kibana', () => ({ + KibanaLogic: { values: mockKibanaValues }, +})); diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts index 51b32e7a877b2..0cdba1d33c5c2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts @@ -11,3 +11,7 @@ export const mockLicensingValues = { hasPlatinumLicense: false, hasGoldLicense: false, }; + +jest.mock('../shared/licensing', () => ({ + LicensingLogic: { values: mockLicensingValues }, +})); diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/telemetry_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/telemetry_logic.mock.ts index 437e920ef008c..4f915ce4d9cea 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/telemetry_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/telemetry_logic.mock.ts @@ -10,3 +10,8 @@ export const mockTelemetryActions = { sendAppSearchTelemetry: jest.fn(), sendWorkplaceSearchTelemetry: jest.fn(), }; + +jest.mock('../shared/telemetry', () => ({ + ...(jest.requireActual('../shared/telemetry') as object), + TelemetryLogic: { actions: mockTelemetryActions }, +})); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts index 7be811b7b27db..9d1c6bf09fc21 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts @@ -4,22 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter, expectedAsyncError } from '../../../__mocks__'; - -jest.mock('../../../shared/kibana', () => ({ - KibanaLogic: { values: { history: { location: { search: '' } } } }, -})); -import { KibanaLogic } from '../../../shared/kibana'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: { http: { get: jest.fn() } } }, -})); -import { HttpLogic } from '../../../shared/http'; - -jest.mock('../../../shared/flash_messages', () => ({ - flashAPIErrors: jest.fn(), -})); -import { flashAPIErrors } from '../../../shared/flash_messages'; +import { + LogicMounter, + mockKibanaValues, + mockHttpValues, + mockFlashMessageHelpers, + expectedAsyncError, +} from '../../../__mocks__'; jest.mock('../engine', () => ({ EngineLogic: { values: { engineName: 'test-engine' } }, @@ -28,6 +19,11 @@ jest.mock('../engine', () => ({ import { AnalyticsLogic } from './'; describe('AnalyticsLogic', () => { + const { mount } = new LogicMounter(AnalyticsLogic); + const { history } = mockKibanaValues; + const { http } = mockHttpValues; + const { flashAPIErrors } = mockFlashMessageHelpers; + const DEFAULT_VALUES = { dataLoading: true, analyticsUnavailable: false, @@ -88,11 +84,9 @@ describe('AnalyticsLogic', () => { topClicksForQuery: MOCK_TOP_CLICKS, }; - const { mount } = new LogicMounter(AnalyticsLogic); - beforeEach(() => { jest.clearAllMocks(); - KibanaLogic.values.history.location.search = ''; + history.location.search = ''; }); it('has expected default values', () => { @@ -158,14 +152,14 @@ describe('AnalyticsLogic', () => { it('should make an API call and set state based on the response', async () => { const promise = Promise.resolve(MOCK_ANALYTICS_RESPONSE); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount(); jest.spyOn(AnalyticsLogic.actions, 'onAnalyticsDataLoad'); AnalyticsLogic.actions.loadAnalyticsData(); await promise; - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/app_search/engines/test-engine/analytics/queries', { query: { size: 20 }, @@ -177,14 +171,13 @@ describe('AnalyticsLogic', () => { }); it('parses and passes the current search query string', async () => { - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce({}); - KibanaLogic.values.history.location.search = - '?start=1970-01-01&end=1970-01-02&&tag=some_tag'; + (http.get as jest.Mock).mockReturnValueOnce({}); + history.location.search = '?start=1970-01-01&end=1970-01-02&&tag=some_tag'; mount(); AnalyticsLogic.actions.loadAnalyticsData(); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/app_search/engines/test-engine/analytics/queries', { query: { @@ -199,7 +192,7 @@ describe('AnalyticsLogic', () => { it('calls onAnalyticsUnavailable if analyticsUnavailable is in response', async () => { const promise = Promise.resolve({ analyticsUnavailable: true }); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount(); jest.spyOn(AnalyticsLogic.actions, 'onAnalyticsUnavailable'); @@ -211,7 +204,7 @@ describe('AnalyticsLogic', () => { it('handles errors', async () => { const promise = Promise.reject('error'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount(); jest.spyOn(AnalyticsLogic.actions, 'onAnalyticsUnavailable'); @@ -237,14 +230,14 @@ describe('AnalyticsLogic', () => { it('should make an API call and set state based on the response', async () => { const promise = Promise.resolve(MOCK_QUERY_RESPONSE); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount(); jest.spyOn(AnalyticsLogic.actions, 'onQueryDataLoad'); AnalyticsLogic.actions.loadQueryData('some-query'); await promise; - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/app_search/engines/test-engine/analytics/queries/some-query', expect.any(Object) // empty query obj ); @@ -252,14 +245,13 @@ describe('AnalyticsLogic', () => { }); it('parses and passes the current search query string', async () => { - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce({}); - KibanaLogic.values.history.location.search = - '?start=1970-12-30&end=1970-12-31&&tag=another_tag'; + (http.get as jest.Mock).mockReturnValueOnce({}); + history.location.search = '?start=1970-12-30&end=1970-12-31&&tag=another_tag'; mount(); AnalyticsLogic.actions.loadQueryData('some-query'); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/app_search/engines/test-engine/analytics/queries/some-query', { query: { @@ -273,7 +265,7 @@ describe('AnalyticsLogic', () => { it('calls onAnalyticsUnavailable if analyticsUnavailable is in response', async () => { const promise = Promise.resolve({ analyticsUnavailable: true }); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount(); jest.spyOn(AnalyticsLogic.actions, 'onAnalyticsUnavailable'); @@ -285,7 +277,7 @@ describe('AnalyticsLogic', () => { it('handles errors', async () => { const promise = Promise.reject('error'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount(); jest.spyOn(AnalyticsLogic.actions, 'onAnalyticsUnavailable'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts index b33d1eba572ec..cdd055fd367ef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts @@ -4,23 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: mockHttpValues }, -})); -const { http } = mockHttpValues; - -jest.mock('../../../shared/flash_messages', () => ({ - FlashMessagesLogic: { actions: { clearFlashMessages: jest.fn() } }, - setSuccessMessage: jest.fn(), - flashAPIErrors: jest.fn(), -})); import { - FlashMessagesLogic, - setSuccessMessage, - flashAPIErrors, -} from '../../../shared/flash_messages'; + LogicMounter, + mockFlashMessageHelpers, + mockHttpValues, + expectedAsyncError, +} from '../../../__mocks__'; jest.mock('../../app_logic', () => ({ AppLogic: { @@ -34,6 +23,10 @@ import { ApiTokenTypes } from './constants'; import { CredentialsLogic } from './credentials_logic'; describe('CredentialsLogic', () => { + const { mount } = new LogicMounter(CredentialsLogic); + const { http } = mockHttpValues; + const { clearFlashMessages, setSuccessMessage, flashAPIErrors } = mockFlashMessageHelpers; + const DEFAULT_VALUES = { activeApiToken: { name: '', @@ -56,8 +49,6 @@ describe('CredentialsLogic', () => { fullEngineAccessChecked: false, }; - const { mount } = new LogicMounter(CredentialsLogic); - const newToken = { id: 1, name: 'myToken', @@ -955,7 +946,7 @@ describe('CredentialsLogic', () => { describe('listener side-effects', () => { it('should clear flashMessages whenever the credentials form flyout is opened', () => { CredentialsLogic.actions.showCredentialsForm(); - expect(FlashMessagesLogic.actions.clearFlashMessages).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts index 166cbae9a4512..8afb7696ec166 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts @@ -11,7 +11,7 @@ import { ApiTokenTypes, CREATE_MESSAGE, UPDATE_MESSAGE, DELETE_MESSAGE } from '. import { HttpLogic } from '../../../shared/http'; import { - FlashMessagesLogic, + clearFlashMessages, setSuccessMessage, flashAPIErrors, } from '../../../shared/flash_messages'; @@ -227,7 +227,7 @@ export const CredentialsLogic = kea({ }), listeners: ({ actions, values }) => ({ showCredentialsForm: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, initializeCredentialsData: () => { actions.fetchCredentials(); 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 c2a0d29cc1f40..2256d5ae7946a 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 @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; +import { LogicMounter, mockHttpValues } from '../../../__mocks__'; import dedent from 'dedent'; @@ -13,11 +13,6 @@ jest.mock('./utils', () => ({ })); import { readUploadedFileAsText } from './utils'; -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: { http: { post: jest.fn() } } }, -})); -import { HttpLogic } from '../../../shared/http'; - jest.mock('../engine', () => ({ EngineLogic: { values: { engineName: 'test-engine' } }, })); @@ -27,6 +22,9 @@ import { DocumentCreationStep } from './types'; import { DocumentCreationLogic } from './'; describe('DocumentCreationLogic', () => { + const { mount } = new LogicMounter(DocumentCreationLogic); + const { http } = mockHttpValues; + const DEFAULT_VALUES = { isDocumentCreationOpen: false, creationMode: 'text', @@ -40,8 +38,6 @@ describe('DocumentCreationLogic', () => { }; const mockFile = new File(['mockFile'], 'mockFile.json'); - const { mount } = new LogicMounter(DocumentCreationLogic); - beforeEach(() => { jest.clearAllMocks(); }); @@ -447,10 +443,7 @@ describe('DocumentCreationLogic', () => { }); it('should set and show summary from the returned response', async () => { - const { http } = HttpLogic.values; - const promise = (http.post as jest.Mock).mockReturnValueOnce( - Promise.resolve(mockValidResponse) - ); + const promise = http.post.mockReturnValueOnce(Promise.resolve(mockValidResponse)); await DocumentCreationLogic.actions.uploadDocuments({ documents: mockValidDocuments }); await promise; @@ -469,8 +462,7 @@ describe('DocumentCreationLogic', () => { }); it('handles API errors', async () => { - const { http } = HttpLogic.values; - const promise = (http.post as jest.Mock).mockReturnValueOnce( + const promise = http.post.mockReturnValueOnce( Promise.reject({ body: { statusCode: 400, @@ -489,7 +481,6 @@ describe('DocumentCreationLogic', () => { }); it('handles client-side errors', async () => { - const { http } = HttpLogic.values; const promise = (http.post as jest.Mock).mockReturnValueOnce(new Error()); await DocumentCreationLogic.actions.uploadDocuments({ documents: [{}] }); @@ -502,8 +493,7 @@ describe('DocumentCreationLogic', () => { // NOTE: I can't seem to reproduce this in a production setting. it('handles errors returned from the API', async () => { - const { http } = HttpLogic.values; - const promise = (http.post as jest.Mock).mockReturnValueOnce( + const promise = http.post.mockReturnValueOnce( Promise.resolve({ errors: ['JSON cannot be empty'], }) @@ -546,7 +536,6 @@ describe('DocumentCreationLogic', () => { }); it('should correctly merge multiple API calls into a single summary obj', async () => { - const { http } = HttpLogic.values; const promise = (http.post as jest.Mock) .mockReturnValueOnce(mockFirstResponse) .mockReturnValueOnce(mockSecondResponse); @@ -573,7 +562,6 @@ describe('DocumentCreationLogic', () => { }); it('should correctly merge response errors', async () => { - const { http } = HttpLogic.values; const promise = (http.post as jest.Mock) .mockReturnValueOnce({ ...mockFirstResponse, errors: ['JSON cannot be empty'] }) .mockReturnValueOnce({ ...mockSecondResponse, errors: ['Too large to render'] }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts index 015a9abd5405c..f7476083009df 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts @@ -4,39 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: mockHttpValues }, -})); -const { http } = mockHttpValues; +import { + LogicMounter, + mockHttpValues, + mockKibanaValues, + mockFlashMessageHelpers, + expectedAsyncError, +} from '../../../__mocks__'; jest.mock('../engine', () => ({ EngineLogic: { values: { engineName: 'engine1' } }, })); -jest.mock('../../../shared/kibana', () => ({ - KibanaLogic: { values: { navigateToUrl: jest.fn() } }, -})); -import { KibanaLogic } from '../../../shared/kibana'; - -jest.mock('../../../shared/flash_messages', () => ({ - setQueuedSuccessMessage: jest.fn(), - flashAPIErrors: jest.fn(), -})); -import { setQueuedSuccessMessage, flashAPIErrors } from '../../../shared/flash_messages'; - import { DocumentDetailLogic } from './document_detail_logic'; import { InternalSchemaTypes } from '../../../shared/types'; describe('DocumentDetailLogic', () => { + const { mount } = new LogicMounter(DocumentDetailLogic); + const { http } = mockHttpValues; + const { navigateToUrl } = mockKibanaValues; + const { setQueuedSuccessMessage, flashAPIErrors } = mockFlashMessageHelpers; + const DEFAULT_VALUES = { dataLoading: true, fields: [], }; - const { mount } = new LogicMounter(DocumentDetailLogic); - beforeEach(() => { jest.clearAllMocks(); }); @@ -84,7 +77,7 @@ describe('DocumentDetailLogic', () => { await expectedAsyncError(promise); expect(flashAPIErrors).toHaveBeenCalledWith('An error occurred', { isQueued: true }); - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith('/engines/engine1/documents'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/engine1/documents'); }); }); @@ -112,7 +105,7 @@ describe('DocumentDetailLogic', () => { expect(setQueuedSuccessMessage).toHaveBeenCalledWith( 'Successfully marked document for deletion. It will be deleted momentarily.' ); - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith('/engines/engine1/documents'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/engine1/documents'); }); it('will do nothing if not confirmed', async () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts index 8d855bebdb20f..62f444cf8f6ab 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts @@ -6,14 +6,12 @@ import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: mockHttpValues }, -})); -const { http } = mockHttpValues; - import { EngineLogic } from './'; describe('EngineLogic', () => { + const { mount } = new LogicMounter(EngineLogic); + const { http } = mockHttpValues; + const mockEngineData = { name: 'some-engine', type: 'default', @@ -45,8 +43,6 @@ describe('EngineLogic', () => { engineNotFound: false, }; - const { mount } = new LogicMounter(EngineLogic); - beforeEach(() => { jest.clearAllMocks(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx index 26c7b3f677fc1..362454c31f0d9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx @@ -6,17 +6,12 @@ import '../../../__mocks__/react_router_history.mock'; import { unmountHandler } from '../../../__mocks__/shallow_useeffect.mock'; -import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock'; +import { mockFlashMessageHelpers, setMockValues, setMockActions } from '../../../__mocks__'; import React from 'react'; import { shallow } from 'enzyme'; import { Switch, Redirect, useParams } from 'react-router-dom'; -jest.mock('../../../shared/flash_messages', () => ({ - setQueuedErrorMessage: jest.fn(), -})); -import { setQueuedErrorMessage } from '../../../shared/flash_messages'; - import { Loading } from '../../../shared/loading'; import { EngineOverview } from '../engine_overview'; import { AnalyticsRouter } from '../analytics'; @@ -58,6 +53,7 @@ describe('EngineRouter', () => { }); it('redirects to engines list and flashes an error if the engine param was not found', () => { + const { setQueuedErrorMessage } = mockFlashMessageHelpers; setMockValues({ ...values, engineNotFound: true, engineName: '404-engine' }); const wrapper = shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts index 46d5a7b6608a5..b6620756699d5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts @@ -4,17 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: mockHttpValues }, -})); -const { http } = mockHttpValues; - -jest.mock('../../../shared/flash_messages', () => ({ - flashAPIErrors: jest.fn(), -})); -import { flashAPIErrors } from '../../../shared/flash_messages'; +import { + LogicMounter, + mockHttpValues, + mockFlashMessageHelpers, + expectedAsyncError, +} from '../../../__mocks__'; jest.mock('../engine', () => ({ EngineLogic: { values: { engineName: 'some-engine' } }, @@ -23,6 +18,10 @@ jest.mock('../engine', () => ({ import { EngineOverviewLogic } from './'; describe('EngineOverviewLogic', () => { + const { mount, unmount } = new LogicMounter(EngineOverviewLogic); + const { http } = mockHttpValues; + const { flashAPIErrors } = mockFlashMessageHelpers; + const mockEngineMetrics = { apiLogsUnavailable: true, documentCount: 10, @@ -45,8 +44,6 @@ describe('EngineOverviewLogic', () => { timeoutId: null, }; - const { mount, unmount } = new LogicMounter(EngineOverviewLogic); - beforeEach(() => { jest.clearAllMocks(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts index 157ae396319ac..5a83717aa0030 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts @@ -4,17 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter } from '../../../__mocks__/kea.mock'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: { http: { get: jest.fn() } } }, -})); -import { HttpLogic } from '../../../shared/http'; +import { LogicMounter, mockHttpValues } from '../../../__mocks__'; import { EngineDetails } from '../engine/types'; import { EnginesLogic } from './'; describe('EnginesLogic', () => { + const { mount } = new LogicMounter(EnginesLogic); + const { http } = mockHttpValues; + const DEFAULT_VALUES = { dataLoading: true, engines: [], @@ -43,8 +41,6 @@ describe('EnginesLogic', () => { }, }; - const { mount } = new LogicMounter(EnginesLogic); - beforeEach(() => { jest.clearAllMocks(); }); @@ -129,14 +125,14 @@ describe('EnginesLogic', () => { describe('loadEngines', () => { it('should call the engines API endpoint and set state based on the results', async () => { const promise = Promise.resolve(MOCK_ENGINES_API_RESPONSE); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount({ enginesPage: 10 }); jest.spyOn(EnginesLogic.actions, 'onEnginesLoad'); EnginesLogic.actions.loadEngines(); await promise; - expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/app_search/engines', { + expect(http.get).toHaveBeenCalledWith('/api/app_search/engines', { query: { type: 'indexed', pageIndex: 10 }, }); expect(EnginesLogic.actions.onEnginesLoad).toHaveBeenCalledWith({ @@ -149,14 +145,14 @@ describe('EnginesLogic', () => { describe('loadMetaEngines', () => { it('should call the engines API endpoint and set state based on the results', async () => { const promise = Promise.resolve(MOCK_ENGINES_API_RESPONSE); - (HttpLogic.values.http.get as jest.Mock).mockReturnValueOnce(promise); + http.get.mockReturnValueOnce(promise); mount({ metaEnginesPage: 99 }); jest.spyOn(EnginesLogic.actions, 'onMetaEnginesLoad'); EnginesLogic.actions.loadMetaEngines(); await promise; - expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/app_search/engines', { + expect(http.get).toHaveBeenCalledWith('/api/app_search/engines', { query: { type: 'meta', pageIndex: 99 }, }); expect(EnginesLogic.actions.onMetaEnginesLoad).toHaveBeenCalledWith({ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts index b1920b6ddea8a..bfdca6791edc1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts @@ -4,22 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LogicMounter, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { values: mockHttpValues }, -})); -const { http } = mockHttpValues; - -jest.mock('../../../shared/flash_messages', () => ({ - flashAPIErrors: jest.fn(), -})); -import { flashAPIErrors } from '../../../shared/flash_messages'; +import { + LogicMounter, + mockHttpValues, + mockFlashMessageHelpers, + expectedAsyncError, +} from '../../../__mocks__'; import { LogRetentionOptions } from './types'; import { LogRetentionLogic } from './log_retention_logic'; describe('LogRetentionLogic', () => { + const { mount } = new LogicMounter(LogRetentionLogic); + const { http } = mockHttpValues; + const { flashAPIErrors } = mockFlashMessageHelpers; + const TYPICAL_SERVER_LOG_RETENTION = { analytics: { disabled_at: null, @@ -52,8 +51,6 @@ describe('LogRetentionLogic', () => { isLogRetentionUpdating: false, }; - const { mount } = new LogicMounter(LogRetentionLogic); - beforeEach(() => { jest.clearAllMocks(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx index 59bb7ee5b9625..746ac20016cc4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../__mocks__/kea.mock'; +import { setMockValues } from '../../__mocks__/kea.mock'; -import { useValues } from 'kea'; import React from 'react'; import { shallow } from 'enzyme'; import { EuiCallOut } from '@elastic/eui'; -import { FlashMessages } from './'; +import { FlashMessages } from './flash_messages'; describe('FlashMessages', () => { beforeEach(() => { @@ -19,7 +18,7 @@ describe('FlashMessages', () => { }); it('does not render if no messages exist', () => { - (useValues as jest.Mock).mockImplementationOnce(() => ({ messages: [] })); + setMockValues({ messages: [] }); const wrapper = shallow(); @@ -38,7 +37,7 @@ describe('FlashMessages', () => { { type: 'warning', message: 'Uh oh' }, { type: 'info', message: 'Testing multiples of same type' }, ]; - (useValues as jest.Mock).mockImplementationOnce(() => ({ messages: mockMessages })); + setMockValues({ messages: mockMessages }); const wrapper = shallow(); @@ -49,7 +48,7 @@ describe('FlashMessages', () => { }); it('renders any children', () => { - (useValues as jest.Mock).mockImplementationOnce(() => ({ messages: [{ type: 'success' }] })); + setMockValues({ messages: [{ type: 'success' }] }); const wrapper = shallow( diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.test.ts index ff1ec7428e828..029c89340ca8b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.test.ts @@ -6,12 +6,10 @@ import { resetContext } from 'kea'; -import { mockHistory } from '../../__mocks__'; -jest.mock('../kibana', () => ({ - KibanaLogic: { values: { history: mockHistory } }, -})); +import { mockKibanaValues } from '../../__mocks__/kibana_logic.mock'; +const { history } = mockKibanaValues; -import { FlashMessagesLogic, mountFlashMessagesLogic, IFlashMessage } from './'; +import { FlashMessagesLogic, mountFlashMessagesLogic, IFlashMessage } from './flash_messages_logic'; describe('FlashMessagesLogic', () => { const mount = () => mountFlashMessagesLogic(); @@ -98,7 +96,7 @@ describe('FlashMessagesLogic', () => { describe('on mount', () => { it('listens for history changes and clears messages on change', () => { mount(); - expect(mockHistory.listen).toHaveBeenCalled(); + expect(history.listen).toHaveBeenCalled(); FlashMessagesLogic.actions.setQueuedMessages(['queuedMessages'] as any); jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages'); @@ -106,7 +104,7 @@ describe('FlashMessagesLogic', () => { jest.spyOn(FlashMessagesLogic.actions, 'clearQueuedMessages'); jest.spyOn(FlashMessagesLogic.actions, 'setHistoryListener'); - const mockHistoryChange = (mockHistory.listen.mock.calls[0] as any)[0]; + const mockHistoryChange = (history.listen.mock.calls[0] as any)[0]; mockHistoryChange(); expect(FlashMessagesLogic.actions.clearFlashMessages).toHaveBeenCalled(); expect(FlashMessagesLogic.actions.setFlashMessages).toHaveBeenCalledWith([ @@ -119,7 +117,7 @@ describe('FlashMessagesLogic', () => { describe('on unmount', () => { it('removes history listener', () => { const mockUnlistener = jest.fn(); - mockHistory.listen.mockReturnValueOnce(mockUnlistener); + history.listen.mockReturnValueOnce(mockUnlistener); const unmount = mount(); unmount(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.test.ts index c30631329b79d..fbdb38937cc71 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.test.ts @@ -4,16 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('./', () => ({ - FlashMessagesLogic: { - actions: { - setFlashMessages: jest.fn(), - setQueuedMessages: jest.fn(), - }, - }, -})); -import { FlashMessagesLogic } from './'; +import '../../__mocks__/kibana_logic.mock'; +import { FlashMessagesLogic } from './flash_messages_logic'; import { flashAPIErrors } from './handle_api_errors'; describe('flashAPIErrors', () => { @@ -30,6 +23,9 @@ describe('flashAPIErrors', () => { beforeEach(() => { jest.clearAllMocks(); + FlashMessagesLogic.mount(); + jest.spyOn(FlashMessagesLogic.actions, 'setFlashMessages'); + jest.spyOn(FlashMessagesLogic.actions, 'setQueuedMessages'); }); it('converts API errors into flash messages', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts index c4b287ee08354..783a61296cac8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts @@ -6,7 +6,7 @@ import { HttpResponse } from 'src/core/public'; -import { FlashMessagesLogic, IFlashMessage } from './'; +import { FlashMessagesLogic, IFlashMessage } from './flash_messages_logic'; /** * The API errors we are handling can come from one of two ways: diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts index 4a5a4bb6be1f3..5de16bde98926 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts @@ -4,26 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mockHistory } from '../../__mocks__'; -jest.mock('../kibana', () => ({ - KibanaLogic: { values: { history: mockHistory } }, -})); +import '../../__mocks__/kibana_logic.mock'; +import { FlashMessagesLogic } from './flash_messages_logic'; import { - FlashMessagesLogic, - mountFlashMessagesLogic, setSuccessMessage, setErrorMessage, setQueuedSuccessMessage, setQueuedErrorMessage, clearFlashMessages, -} from './'; +} from './set_message_helpers'; describe('Flash Message Helpers', () => { const message = 'I am a message'; beforeEach(() => { - mountFlashMessagesLogic(); + FlashMessagesLogic.mount(); }); it('setSuccessMessage()', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.ts index e054ff6e2fd5a..15ee890aaf2e4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FlashMessagesLogic } from './'; +import { FlashMessagesLogic } from './flash_messages_logic'; export const setSuccessMessage = (message: string) => { FlashMessagesLogic.actions.setFlashMessages({ diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts index abddb96275c6f..558271a8fbdc6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts @@ -4,26 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { resetContext } from 'kea'; - -import { expectedAsyncError } from '../../__mocks__'; - -jest.mock('../http', () => ({ - HttpLogic: { - values: { http: { get: jest.fn() } }, - }, -})); -import { HttpLogic } from '../http'; - -jest.mock('../flash_messages', () => ({ - flashAPIErrors: jest.fn(), -})); -import { flashAPIErrors } from '../flash_messages'; +import { + LogicMounter, + mockFlashMessageHelpers, + mockHttpValues, + expectedAsyncError, +} from '../../__mocks__'; import { IndexingStatusLogic } from './indexing_status_logic'; describe('IndexingStatusLogic', () => { - let unmount: any; + const { mount, unmount } = new LogicMounter(IndexingStatusLogic); + const { http } = mockHttpValues; + const { flashAPIErrors } = mockFlashMessageHelpers; const mockStatusResponse = { percentageComplete: 50, @@ -33,8 +26,7 @@ describe('IndexingStatusLogic', () => { beforeEach(() => { jest.clearAllMocks(); - resetContext({}); - unmount = IndexingStatusLogic.mount(); + mount(); }); it('has expected default values', () => { @@ -66,12 +58,12 @@ describe('IndexingStatusLogic', () => { it('calls API and sets values', async () => { const setIndexingStatusSpy = jest.spyOn(IndexingStatusLogic.actions, 'setIndexingStatus'); const promise = Promise.resolve(mockStatusResponse); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete }); jest.advanceTimersByTime(TIMEOUT); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith(statusPath); + expect(http.get).toHaveBeenCalledWith(statusPath); await promise; expect(setIndexingStatusSpy).toHaveBeenCalledWith(mockStatusResponse); @@ -79,7 +71,7 @@ describe('IndexingStatusLogic', () => { it('handles error', async () => { const promise = Promise.reject('An error occured'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete }); jest.advanceTimersByTime(TIMEOUT); @@ -91,7 +83,7 @@ describe('IndexingStatusLogic', () => { it('handles indexing complete state', async () => { const promise = Promise.resolve({ ...mockStatusResponse, percentageComplete: 100 }); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete }); jest.advanceTimersByTime(TIMEOUT); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts index 3115e233a6058..0fed5820dbff9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts @@ -6,10 +6,7 @@ import { resetContext } from 'kea'; -import { mockKibanaValues, mockHttpValues } from '../../__mocks__'; -jest.mock('../http', () => ({ - HttpLogic: { values: { http: mockHttpValues.http } }, -})); +import { mockKibanaValues } from '../../__mocks__'; import { KibanaLogic, mountKibanaLogic } from './kibana_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts index 420d3a5dd4ded..6d4e4f4fe649c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts @@ -7,14 +7,13 @@ import { resetContext } from 'kea'; import { JSON_HEADER as headers } from '../../../../common/constants'; -import { mockHttpValues } from '../../__mocks__'; -jest.mock('../http', () => ({ - HttpLogic: { values: { http: mockHttpValues.http } }, -})); +import { mockHttpValues } from '../../__mocks__/http_logic.mock'; import { TelemetryLogic } from './'; describe('Telemetry logic', () => { + const { http } = mockHttpValues; + beforeEach(() => { jest.clearAllMocks(); resetContext({}); @@ -29,14 +28,14 @@ describe('Telemetry logic', () => { product: 'enterprise_search', }); - expect(mockHttpValues.http.put).toHaveBeenCalledWith('/api/enterprise_search/stats', { + expect(http.put).toHaveBeenCalledWith('/api/enterprise_search/stats', { headers, body: '{"product":"enterprise_search","action":"viewed","metric":"setup_guide"}', }); }); it('throws an error if the telemetry endpoint fails', async () => { - mockHttpValues.http.put.mockImplementationOnce(() => Promise.reject()); + http.put.mockImplementationOnce(() => Promise.reject()); // To capture thrown errors, we have to call the listener fn directly // instead of using `TelemetryLogic.actions.sendTelemetry` - this is 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 index a45094ac55ba0..9ab50000ba1a5 100644 --- 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 @@ -4,17 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../../__mocks__/kea.mock'; import '../../../../../__mocks__/shallow_useeffect.mock'; +import { mockKibanaValues, setMockActions, setMockValues } from '../../../../../__mocks__'; -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'; @@ -32,6 +26,7 @@ import { SaveConfig } from './save_config'; import { SaveCustom } from './save_custom'; describe('AddSourceList', () => { + const { navigateToUrl } = mockKibanaValues; const initializeAddSource = jest.fn(); const setAddSourceStep = jest.fn(); const saveSourceConfig = jest.fn((_, setConfigCompletedStep) => { @@ -83,9 +78,7 @@ describe('AddSourceList', () => { const wrapper = shallow(); wrapper.find(ConfigCompleted).prop('advanceStep')(); - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith( - '/sources/add/confluence_cloud/connect' - ); + expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/connect'); expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep); }); @@ -112,9 +105,7 @@ describe('AddSourceList', () => { const wrapper = shallow(); wrapper.find(ConnectInstance).prop('onFormCreated')('foo'); - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith( - '/sources/add/confluence_cloud/connect' - ); + expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/connect'); }); it('renders Configure Custom step', () => { @@ -137,9 +128,7 @@ describe('AddSourceList', () => { wrapper.find(ConfigureOauth).prop('onFormCreated')('foo'); - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith( - '/sources/add/confluence_cloud/connect' - ); + expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/connect'); }); it('renders Save Custom step', () => { 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 97f08e294be27..1cd7da56dbe73 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 @@ -6,22 +6,11 @@ import { resetContext } from 'kea'; -import { mockHttpValues, expectedAsyncError } 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 { + mockFlashMessageHelpers, + mockHttpValues, + expectedAsyncError, +} from '../../../../../__mocks__'; import { AppLogic } from '../../../../app_logic'; jest.mock('../../../../app_logic', () => ({ @@ -41,6 +30,9 @@ import { } from './add_source_logic'; describe('AddSourceLogic', () => { + const { http } = mockHttpValues; + const { clearFlashMessages, flashAPIErrors } = mockFlashMessageHelpers; + const defaultValues = { addSourceCurrentStep: AddSourceSteps.ConfigIntroStep, addSourceProps: {}, @@ -77,8 +69,6 @@ describe('AddSourceLogic', () => { const CUSTOM_SERVICE_TYPE_INDEX = 17; - const clearFlashMessagesSpy = jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages'); - beforeEach(() => { jest.clearAllMocks(); resetContext({}); @@ -283,10 +273,10 @@ describe('AddSourceLogic', () => { 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); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getSourceConfigData('github'); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/workplace_search/org/settings/connectors/github' ); await promise; @@ -295,7 +285,7 @@ describe('AddSourceLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getSourceConfigData('github'); await expectedAsyncError(promise); @@ -314,15 +304,13 @@ describe('AddSourceLogic', () => { 'setSourceConnectData' ); const promise = Promise.resolve(sourceConnectData); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getSourceConnectData('github', successCallback); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); expect(AddSourceLogic.values.buttonLoading).toEqual(true); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( - '/api/workplace_search/org/sources/github/prepare' - ); + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources/github/prepare'); await promise; expect(setSourceConnectDataSpy).toHaveBeenCalledWith(sourceConnectData); expect(successCallback).toHaveBeenCalledWith(sourceConnectData.oauthUrl); @@ -334,14 +322,14 @@ describe('AddSourceLogic', () => { AddSourceLogic.actions.setSourceIndexPermissionsValue(true); AddSourceLogic.actions.getSourceConnectData('github', successCallback); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(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); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getSourceConnectData('github', successCallback); await expectedAsyncError(promise); @@ -357,11 +345,11 @@ describe('AddSourceLogic', () => { 'setSourceConnectData' ); const promise = Promise.resolve(sourceConnectData); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getSourceReConnectData('github'); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/workplace_search/org/sources/github/reauth_prepare' ); await promise; @@ -370,7 +358,7 @@ describe('AddSourceLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getSourceReConnectData('github'); await expectedAsyncError(promise); @@ -386,20 +374,18 @@ describe('AddSourceLogic', () => { 'setPreContentSourceConfigData' ); const promise = Promise.resolve(config); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getPreContentSourceConfigData('123'); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( - '/api/workplace_search/org/pre_sources/123' - ); + expect(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); + http.get.mockReturnValue(promise); AddSourceLogic.actions.getPreContentSourceConfigData('123'); await expectedAsyncError(promise); @@ -430,14 +416,14 @@ describe('AddSourceLogic', () => { 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); + http.put.mockReturnValue(promise); AddSourceLogic.actions.saveSourceConfig(true, successCallback); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); expect(AddSourceLogic.values.buttonLoading).toEqual(true); expect( - HttpLogic.values.http.put + http.put ).toHaveBeenCalledWith( `/api/workplace_search/org/settings/connectors/${sourceConfigData.serviceType}`, { body: JSON.stringify({ params }) } @@ -462,17 +448,14 @@ describe('AddSourceLogic', () => { consumer_key: sourceConfigData.configuredFields?.consumerKey, }; - expect(HttpLogic.values.http.post).toHaveBeenCalledWith( - '/api/workplace_search/org/settings/connectors', - { - body: JSON.stringify({ params: createParams }), - } - ); + expect(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); + http.put.mockReturnValue(promise); AddSourceLogic.actions.saveSourceConfig(true); await expectedAsyncError(promise); @@ -514,18 +497,15 @@ describe('AddSourceLogic', () => { 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); + http.post.mockReturnValue(promise); AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); expect(AddSourceLogic.values.buttonLoading).toEqual(true); - expect(HttpLogic.values.http.post).toHaveBeenCalledWith( - '/api/workplace_search/org/create_source', - { - body: JSON.stringify({ ...params }), - } - ); + expect(http.post).toHaveBeenCalledWith('/api/workplace_search/org/create_source', { + body: JSON.stringify({ ...params }), + }); await promise; expect(setCustomSourceDataSpy).toHaveBeenCalledWith({ sourceConfigData }); expect(successCallback).toHaveBeenCalled(); @@ -534,7 +514,7 @@ describe('AddSourceLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback); await expectedAsyncError(promise); @@ -553,7 +533,7 @@ describe('AddSourceLogic', () => { it('getSourceConnectData', () => { AddSourceLogic.actions.getSourceConnectData('github', jest.fn()); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/workplace_search/account/sources/github/prepare' ); }); @@ -561,7 +541,7 @@ describe('AddSourceLogic', () => { it('getSourceReConnectData', () => { AddSourceLogic.actions.getSourceReConnectData('123'); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/api/workplace_search/account/sources/123/reauth_prepare' ); }); @@ -569,20 +549,15 @@ describe('AddSourceLogic', () => { it('getPreContentSourceConfigData', () => { AddSourceLogic.actions.getPreContentSourceConfigData('123'); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( - '/api/workplace_search/account/pre_sources/123' - ); + expect(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' }), - } - ); + expect(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 ec5cf541c2316..a328cbe222afb 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 @@ -15,7 +15,7 @@ import { HttpLogic } from '../../../../../shared/http'; import { flashAPIErrors, setSuccessMessage, - FlashMessagesLogic, + clearFlashMessages, } from '../../../../../shared/flash_messages'; import { staticSourceData } from '../../source_data'; @@ -348,7 +348,7 @@ export const AddSourceLogic = kea { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); const { isOrganization } = AppLogic.values; const { subdomainValue: subdomain, indexPermissionsValue: indexPermissions } = values; @@ -399,7 +399,7 @@ export const AddSourceLogic = kea { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); const { sourceConfigData: { serviceType }, baseUrlValue, @@ -447,7 +447,7 @@ export const AddSourceLogic = kea { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); const { isOrganization } = AppLogic.values; const route = isOrganization ? '/api/workplace_search/org/create_source' diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts index 4de524fd9ac8e..cfd4c279c6f8c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts @@ -6,32 +6,12 @@ import { resetContext } from 'kea'; -import { expectedAsyncError } from '../../../__mocks__'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { - values: { http: { get: jest.fn(), post: jest.fn(), put: jest.fn(), delete: jest.fn() } }, - }, -})); -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, - setSuccessMessage, - setQueuedSuccessMessage, -} from '../../../shared/flash_messages'; - -jest.mock('../../../shared/kibana', () => ({ - KibanaLogic: { values: { navigateToUrl: jest.fn() } }, -})); -import { KibanaLogic } from '../../../shared/kibana'; + mockKibanaValues, + mockFlashMessageHelpers, + mockHttpValues, + expectedAsyncError, +} from '../../../__mocks__'; import { groups } from '../../__mocks__/groups.mock'; import { mockGroupValues } from './__mocks__/group_logic.mock'; @@ -40,11 +20,20 @@ import { GroupLogic } from './group_logic'; import { GROUPS_PATH } from '../../routes'; describe('GroupLogic', () => { + const { http } = mockHttpValues; + const { navigateToUrl } = mockKibanaValues; + const { + clearFlashMessages, + flashAPIErrors, + setSuccessMessage, + setQueuedSuccessMessage, + setQueuedErrorMessage, + } = mockFlashMessageHelpers; + const group = groups[0]; const sourceIds = ['123', '124']; const userIds = ['1z1z']; const sourcePriorities = { [sourceIds[0]]: 1, [sourceIds[1]]: 0.5 }; - const clearFlashMessagesSpy = jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages'); beforeEach(() => { jest.clearAllMocks(); @@ -242,40 +231,34 @@ describe('GroupLogic', () => { it('calls API and sets values', async () => { const onInitializeGroupSpy = jest.spyOn(GroupLogic.actions, 'onInitializeGroup'); const promise = Promise.resolve(group); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); GroupLogic.actions.initializeGroup(sourceIds[0]); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/workplace_search/groups/123'); + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/groups/123'); await promise; expect(onInitializeGroupSpy).toHaveBeenCalledWith(group); }); it('handles 404 error', async () => { const promise = Promise.reject({ response: { status: 404 } }); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); GroupLogic.actions.initializeGroup(sourceIds[0]); await expectedAsyncError(promise); - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); - expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ - type: 'error', - message: 'Unable to find group with ID: "123".', - }); + expect(navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(setQueuedErrorMessage).toHaveBeenCalledWith('Unable to find group with ID: "123".'); }); it('handles non-404 error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); GroupLogic.actions.initializeGroup(sourceIds[0]); await expectedAsyncError(promise); - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); - expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ - type: 'error', - message: 'this is an error', - }); + expect(navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(setQueuedErrorMessage).toHaveBeenCalledWith('this is an error'); }); }); @@ -285,15 +268,13 @@ describe('GroupLogic', () => { }); it('deletes a group', async () => { const promise = Promise.resolve(true); - (HttpLogic.values.http.delete as jest.Mock).mockReturnValue(promise); + http.delete.mockReturnValue(promise); GroupLogic.actions.deleteGroup(); - expect(HttpLogic.values.http.delete).toHaveBeenCalledWith( - '/api/workplace_search/groups/123' - ); + expect(http.delete).toHaveBeenCalledWith('/api/workplace_search/groups/123'); await promise; - expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); expect(setQueuedSuccessMessage).toHaveBeenCalledWith( 'Group "group" was successfully deleted.' ); @@ -301,7 +282,7 @@ describe('GroupLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.delete as jest.Mock).mockReturnValue(promise); + http.delete.mockReturnValue(promise); GroupLogic.actions.deleteGroup(); await expectedAsyncError(promise); @@ -318,10 +299,10 @@ describe('GroupLogic', () => { it('updates name', async () => { const onGroupNameChangedSpy = jest.spyOn(GroupLogic.actions, 'onGroupNameChanged'); const promise = Promise.resolve(group); - (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + http.put.mockReturnValue(promise); GroupLogic.actions.updateGroupName(); - expect(HttpLogic.values.http.put).toHaveBeenCalledWith('/api/workplace_search/groups/123', { + expect(http.put).toHaveBeenCalledWith('/api/workplace_search/groups/123', { body: JSON.stringify({ group: { name: 'new name' } }), }); @@ -334,7 +315,7 @@ describe('GroupLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + http.put.mockReturnValue(promise); GroupLogic.actions.updateGroupName(); await expectedAsyncError(promise); @@ -351,15 +332,12 @@ describe('GroupLogic', () => { it('updates name', async () => { const onGroupSourcesSavedSpy = jest.spyOn(GroupLogic.actions, 'onGroupSourcesSaved'); const promise = Promise.resolve(group); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupLogic.actions.saveGroupSources(); - expect(HttpLogic.values.http.post).toHaveBeenCalledWith( - '/api/workplace_search/groups/123/share', - { - body: JSON.stringify({ content_source_ids: sourceIds }), - } - ); + expect(http.post).toHaveBeenCalledWith('/api/workplace_search/groups/123/share', { + body: JSON.stringify({ content_source_ids: sourceIds }), + }); await promise; expect(onGroupSourcesSavedSpy).toHaveBeenCalledWith(group); @@ -370,7 +348,7 @@ describe('GroupLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupLogic.actions.saveGroupSources(); await expectedAsyncError(promise); @@ -386,15 +364,12 @@ describe('GroupLogic', () => { it('updates name', async () => { const onGroupUsersSavedSpy = jest.spyOn(GroupLogic.actions, 'onGroupUsersSaved'); const promise = Promise.resolve(group); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupLogic.actions.saveGroupUsers(); - expect(HttpLogic.values.http.post).toHaveBeenCalledWith( - '/api/workplace_search/groups/123/assign', - { - body: JSON.stringify({ user_ids: userIds }), - } - ); + expect(http.post).toHaveBeenCalledWith('/api/workplace_search/groups/123/assign', { + body: JSON.stringify({ user_ids: userIds }), + }); await promise; expect(onGroupUsersSavedSpy).toHaveBeenCalledWith(group); @@ -405,7 +380,7 @@ describe('GroupLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupLogic.actions.saveGroupUsers(); await expectedAsyncError(promise); @@ -424,20 +399,17 @@ describe('GroupLogic', () => { 'onGroupPrioritiesChanged' ); const promise = Promise.resolve(group); - (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + http.put.mockReturnValue(promise); GroupLogic.actions.saveGroupSourcePrioritization(); - expect(HttpLogic.values.http.put).toHaveBeenCalledWith( - '/api/workplace_search/groups/123/boosts', - { - body: JSON.stringify({ - content_source_boosts: [ - [sourceIds[0], 1], - [sourceIds[1], 0.5], - ], - }), - } - ); + expect(http.put).toHaveBeenCalledWith('/api/workplace_search/groups/123/boosts', { + body: JSON.stringify({ + content_source_boosts: [ + [sourceIds[0], 1], + [sourceIds[1], 0.5], + ], + }), + }); await promise; expect(setSuccessMessage).toHaveBeenCalledWith( @@ -448,7 +420,7 @@ describe('GroupLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + http.put.mockReturnValue(promise); GroupLogic.actions.saveGroupSourcePrioritization(); await expectedAsyncError(promise); @@ -462,7 +434,7 @@ describe('GroupLogic', () => { GroupLogic.actions.showConfirmDeleteModal(); expect(GroupLogic.values.confirmDeleteModalVisible).toEqual(true); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); @@ -471,7 +443,7 @@ describe('GroupLogic', () => { GroupLogic.actions.showSharedSourcesModal(); expect(GroupLogic.values.sharedSourcesModalVisible).toEqual(true); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); @@ -480,7 +452,7 @@ describe('GroupLogic', () => { GroupLogic.actions.showManageUsersModal(); expect(GroupLogic.values.manageUsersModalVisible).toEqual(true); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); @@ -488,7 +460,7 @@ describe('GroupLogic', () => { it('clears flash messages', () => { GroupLogic.actions.resetFlashMessages(); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts index 357f0553c69e1..6d9c2d5d8233f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts @@ -10,13 +10,14 @@ import { i18n } from '@kbn/i18n'; import { HttpLogic } from '../../../shared/http'; import { KibanaLogic } from '../../../shared/kibana'; - import { - FlashMessagesLogic, + clearFlashMessages, flashAPIErrors, setSuccessMessage, setQueuedSuccessMessage, + setQueuedErrorMessage, } from '../../../shared/flash_messages'; + import { GROUPS_PATH } from '../../routes'; import { ContentSourceDetails, GroupDetails, User, SourcePriority } from '../../types'; @@ -223,10 +224,7 @@ export const GroupLogic = kea>({ ); const error = e.response?.status === 404 ? NOT_FOUND_MESSAGE : e; - FlashMessagesLogic.actions.setQueuedMessages({ - type: 'error', - message: error, - }); + setQueuedErrorMessage(error); KibanaLogic.values.navigateToUrl(GROUPS_PATH); } @@ -360,16 +358,16 @@ export const GroupLogic = kea>({ } }, showConfirmDeleteModal: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, showManageUsersModal: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, showSharedSourcesModal: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, resetFlashMessages: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts index d046863fd388c..bbeded9207d01 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -6,23 +6,7 @@ import { resetContext } from 'kea'; -import { expectedAsyncError } from '../../../__mocks__'; - -jest.mock('../../../shared/http', () => ({ - HttpLogic: { - values: { http: { get: jest.fn(), post: jest.fn() } }, - }, -})); -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 { mockFlashMessageHelpers, mockHttpValues, expectedAsyncError } from '../../../__mocks__'; import { DEFAULT_META } from '../../../shared/constants'; import { JSON_HEADER as headers } from '../../../../../common/constants'; @@ -37,7 +21,9 @@ const TIMEOUT = 400; const delay = () => new Promise((resolve) => setTimeout(resolve, TIMEOUT)); describe('GroupsLogic', () => { - const clearFlashMessagesSpy = jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages'); + const { http } = mockHttpValues; + const { clearFlashMessages, flashAPIErrors } = mockFlashMessageHelpers; + const groupsResponse = { results: groups, meta: DEFAULT_META, @@ -229,17 +215,17 @@ describe('GroupsLogic', () => { it('calls API and sets values', async () => { const onInitializeGroupsSpy = jest.spyOn(GroupsLogic.actions, 'onInitializeGroups'); const promise = Promise.resolve(groupsResponse); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); GroupsLogic.actions.initializeGroups(); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/workplace_search/groups'); + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/groups'); await promise; expect(onInitializeGroupsSpy).toHaveBeenCalledWith(groupsResponse); }); it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); GroupsLogic.actions.initializeGroups(); await expectedAsyncError(promise); @@ -269,14 +255,11 @@ describe('GroupsLogic', () => { it('calls API and sets values', async () => { const setSearchResultsSpy = jest.spyOn(GroupsLogic.actions, 'setSearchResults'); const promise = Promise.resolve(groups); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupsLogic.actions.getSearchResults(); await delay(); - expect(HttpLogic.values.http.post).toHaveBeenCalledWith( - '/api/workplace_search/groups/search', - payload - ); + expect(http.post).toHaveBeenCalledWith('/api/workplace_search/groups/search', payload); await promise; expect(setSearchResultsSpy).toHaveBeenCalledWith(groups); }); @@ -286,22 +269,19 @@ describe('GroupsLogic', () => { GroupsLogic.actions.setActivePage(2); const setSearchResultsSpy = jest.spyOn(GroupsLogic.actions, 'setSearchResults'); const promise = Promise.resolve(groups); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupsLogic.actions.getSearchResults(true); // Account for `breakpoint` that debounces filter value. await delay(); - expect(HttpLogic.values.http.post).toHaveBeenCalledWith( - '/api/workplace_search/groups/search', - payload - ); + expect(http.post).toHaveBeenCalledWith('/api/workplace_search/groups/search', payload); await promise; expect(setSearchResultsSpy).toHaveBeenCalledWith(groups); }); it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupsLogic.actions.getSearchResults(); await expectedAsyncError(promise); @@ -315,19 +295,17 @@ describe('GroupsLogic', () => { it('calls API and sets values', async () => { const setGroupUsersSpy = jest.spyOn(GroupsLogic.actions, 'setGroupUsers'); const promise = Promise.resolve(users); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); GroupsLogic.actions.fetchGroupUsers('123'); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith( - '/api/workplace_search/groups/123/group_users' - ); + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/groups/123/group_users'); await promise; expect(setGroupUsersSpy).toHaveBeenCalledWith(users); }); it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + http.get.mockReturnValue(promise); GroupsLogic.actions.fetchGroupUsers('123'); await expectedAsyncError(promise); @@ -342,10 +320,10 @@ describe('GroupsLogic', () => { GroupsLogic.actions.setNewGroupName(GROUP_NAME); const setNewGroupSpy = jest.spyOn(GroupsLogic.actions, 'setNewGroup'); const promise = Promise.resolve(groups[0]); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupsLogic.actions.saveNewGroup(); - expect(HttpLogic.values.http.post).toHaveBeenCalledWith('/api/workplace_search/groups', { + expect(http.post).toHaveBeenCalledWith('/api/workplace_search/groups', { body: JSON.stringify({ group_name: GROUP_NAME }), headers, }); @@ -355,7 +333,7 @@ describe('GroupsLogic', () => { it('handles error', async () => { const promise = Promise.reject('this is an error'); - (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + http.post.mockReturnValue(promise); GroupsLogic.actions.saveNewGroup(); await expectedAsyncError(promise); @@ -388,7 +366,7 @@ describe('GroupsLogic', () => { expect(GroupsLogic.values.newGroupModalOpen).toEqual(true); expect(GroupsLogic.values.newGroup).toEqual(null); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); @@ -400,7 +378,7 @@ describe('GroupsLogic', () => { expect(GroupsLogic.values.filteredUsers).toEqual([]); expect(GroupsLogic.values.filterValue).toEqual(''); expect(GroupsLogic.values.groupsMeta).toEqual(DEFAULT_META); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); @@ -409,7 +387,7 @@ describe('GroupsLogic', () => { GroupsLogic.actions.toggleFilterSourcesDropdown(); expect(GroupsLogic.values.filterSourcesDropdownOpen).toEqual(true); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); @@ -418,7 +396,7 @@ describe('GroupsLogic', () => { GroupsLogic.actions.toggleFilterUsersDropdown(); expect(GroupsLogic.values.filterUsersDropdownOpen).toEqual(true); - expect(clearFlashMessagesSpy).toHaveBeenCalled(); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts index 618ca84f825c9..3957f2ea7a124 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { HttpLogic } from '../../../shared/http'; import { - FlashMessagesLogic, + clearFlashMessages, flashAPIErrors, setSuccessMessage, } from '../../../shared/flash_messages'; @@ -339,16 +339,16 @@ export const GroupsLogic = kea>({ actions.getSearchResults(); }, openNewGroupModal: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, resetGroupsFilters: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, toggleFilterSourcesDropdown: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, toggleFilterUsersDropdown: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts index 1ec770e9defce..fb55d11be1f0f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts @@ -6,13 +6,14 @@ import { resetContext } from 'kea'; -jest.mock('../../../shared/http', () => ({ HttpLogic: { values: { http: { get: jest.fn() } } } })); -import { HttpLogic } from '../../../shared/http'; +import { mockHttpValues } from '../../../__mocks__'; import { mockOverviewValues } from './__mocks__'; import { OverviewLogic } from './overview_logic'; describe('OverviewLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { jest.clearAllMocks(); resetContext({}); @@ -65,7 +66,7 @@ describe('OverviewLogic', () => { await OverviewLogic.actions.initializeOverview(); - expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/workplace_search/overview'); + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/overview'); expect(setServerDataSpy).toHaveBeenCalled(); }); }); From bb70c6a82abbd1769af498de12142f685fb25f21 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Fri, 15 Jan 2021 12:41:16 -0800 Subject: [PATCH 28/38] [APM] Only display relevant sections for rum agent in service overview (#88410) * [APM] Only display relevent sections for rum agent in service overview (#85546) * call `isRumAgentName` once * User experience callout links to the selected service --- .../components/app/service_overview/index.tsx | 37 ++++++++++++------- .../app/transaction_overview/index.tsx | 2 +- .../user_experience_callout.tsx | 9 ++++- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index 2e04cce5ff670..f7720589359c8 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -16,6 +16,7 @@ import { LatencyChart } from '../../shared/charts/latency_chart'; import { TransactionBreakdownChart } from '../../shared/charts/transaction_breakdown_chart'; import { TransactionErrorRateChart } from '../../shared/charts/transaction_error_rate_chart'; import { SearchBar } from '../../shared/search_bar'; +import { UserExperienceCallout } from '../transaction_overview/user_experience_callout'; import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_table'; import { ServiceOverviewErrorsTable } from './service_overview_errors_table'; import { ServiceOverviewInstancesTable } from './service_overview_instances_table'; @@ -51,6 +52,7 @@ export function ServiceOverview({ 'xpack.apm.serviceOverview.searchBar.transactionTypeLabel', { defaultMessage: 'Type: {transactionType}', values: { transactionType } } ); + const isRumAgent = isRumAgentName(agentName); return ( @@ -58,6 +60,11 @@ export function ServiceOverview({ + {isRumAgent && ( + + + + )} @@ -87,7 +94,7 @@ export function ServiceOverview({ gutterSize="s" responsive={false} > - {!isRumAgentName(agentName) && ( + {!isRumAgent && ( - - - - - + {!isRumAgent && ( + + + + + + )} - - - - - + {!isRumAgent && ( + + + + + + )} diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx index 948facae222e7..30fbfe9cc8708 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx @@ -123,7 +123,7 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) { {transactionType === TRANSACTION_PAGE_LOAD && ( <> - + )} diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/user_experience_callout.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/user_experience_callout.tsx index 6e1154a458d6e..95f4fcaab63eb 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/user_experience_callout.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/user_experience_callout.tsx @@ -9,9 +9,14 @@ import { EuiButton, EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; -export function UserExperienceCallout() { +interface Props { + serviceName: string; +} +export function UserExperienceCallout({ serviceName }: Props) { const { core } = useApmPluginContext(); - const userExperienceHref = core.http.basePath.prepend(`/app/ux`); + const userExperienceHref = core.http.basePath.prepend( + `/app/ux?serviceName=${serviceName}` + ); return ( Date: Fri, 15 Jan 2021 21:17:25 +0000 Subject: [PATCH 29/38] chore(NA): remove mocha junit ci integrations (#88129) * chore(NA): remove mocha junit ci integrations * chore(NA): remove mocha script from xpack * chore(NA): single rule exclusion on eslint config for mocha * chore(NA): remove unused custom mocha integration code from kbn/test * chore(NA): rewording packages readme * docs(NA): remoe mocha mention from development-unit-tests --- .ci/Jenkinsfile_flaky | 29 ++---- .ci/teamcity/tests/mocha.sh | 8 -- .eslintrc.js | 12 +-- .teamcity/src/builds/test/QuickTests.kt | 1 - .../contributing/development-tests.asciidoc | 6 -- .../development-unit-tests.asciidoc | 34 +------ .../interpreting-ci-failures.asciidoc | 2 +- package.json | 1 - packages/README.md | 16 +-- packages/kbn-plugin-generator/README.md | 4 - packages/kbn-test/src/index.ts | 7 +- .../kbn-test/src/mocha/auto_junit_reporter.js | 37 ------- packages/kbn-test/src/mocha/index.ts | 4 - packages/kbn-test/src/mocha/run_mocha_cli.js | 99 ------------------- .../src/mocha/server_junit_reporter.js | 25 ----- scripts/mocha.js | 21 ---- src/dev/index.js | 2 +- test/scripts/jenkins_unit.sh | 1 - test/scripts/test/mocha.sh | 7 -- test/tsconfig.json | 2 +- vars/tasks.groovy | 1 - x-pack/package.json | 3 +- .../canvas/storybook/webpack.dll.config.js | 1 - x-pack/scripts/mocha.js | 7 -- x-pack/test/tsconfig.json | 2 +- 25 files changed, 26 insertions(+), 306 deletions(-) delete mode 100755 .ci/teamcity/tests/mocha.sh delete mode 100644 packages/kbn-test/src/mocha/auto_junit_reporter.js delete mode 100644 packages/kbn-test/src/mocha/run_mocha_cli.js delete mode 100644 packages/kbn-test/src/mocha/server_junit_reporter.js delete mode 100644 scripts/mocha.js delete mode 100755 test/scripts/test/mocha.sh delete mode 100644 x-pack/scripts/mocha.js diff --git a/.ci/Jenkinsfile_flaky b/.ci/Jenkinsfile_flaky index 2f496329dfd8e..33204d7396461 100644 --- a/.ci/Jenkinsfile_flaky +++ b/.ci/Jenkinsfile_flaky @@ -5,11 +5,10 @@ kibanaLibrary.load() def CI_GROUP_PARAM = params.CI_GROUP -// Looks like 'oss:ciGroup:1', 'oss:firefoxSmoke', or 'all:serverMocha' +// Looks like 'oss:ciGroup:1', 'oss:firefoxSmoke' def JOB_PARTS = CI_GROUP_PARAM.split(':') def IS_XPACK = JOB_PARTS[0] == 'xpack' def JOB = JOB_PARTS[1] -def NEED_BUILD = JOB != 'serverMocha' def CI_GROUP = JOB_PARTS.size() > 2 ? JOB_PARTS[2] : '' def EXECUTIONS = params.NUMBER_EXECUTIONS.toInteger() def AGENT_COUNT = getAgentCount(EXECUTIONS) @@ -31,15 +30,13 @@ kibanaPipeline(timeoutMinutes: 180) { print "Agent ${agentNumberInside} - ${agentExecutions} executions" workers.functional('flaky-test-runner', { - if (NEED_BUILD) { - if (!IS_XPACK) { - kibanaPipeline.buildOss() - if (CI_GROUP == '1') { - runbld("./test/scripts/jenkins_build_kbn_sample_panel_action.sh", "Build kbn tp sample panel action for ciGroup1") - } - } else { - kibanaPipeline.buildXpack() + if (!IS_XPACK) { + kibanaPipeline.buildOss() + if (CI_GROUP == '1') { + runbld("./test/scripts/jenkins_build_kbn_sample_panel_action.sh", "Build kbn tp sample panel action for ciGroup1") } + } else { + kibanaPipeline.buildXpack() } }, getWorkerMap(agentNumberInside, agentExecutions, worker, workerFailures))() } @@ -60,17 +57,7 @@ kibanaPipeline(timeoutMinutes: 180) { def getWorkerFromParams(isXpack, job, ciGroup) { if (!isXpack) { - if (job == 'serverMocha') { - return kibanaPipeline.functionalTestProcess('serverMocha', { - kibanaPipeline.bash( - """ - source src/dev/ci_setup/setup_env.sh - node scripts/mocha - """, - "run `node scripts/mocha`" - ) - }) - } else if (job == 'accessibility') { + if (job == 'accessibility') { return kibanaPipeline.functionalTestProcess('kibana-accessibility', './test/scripts/jenkins_accessibility.sh') } else if (job == 'firefoxSmoke') { return kibanaPipeline.functionalTestProcess('firefoxSmoke', './test/scripts/jenkins_firefox_smoke.sh') diff --git a/.ci/teamcity/tests/mocha.sh b/.ci/teamcity/tests/mocha.sh deleted file mode 100755 index acb088220fa78..0000000000000 --- a/.ci/teamcity/tests/mocha.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source "$(dirname "${0}")/../util.sh" - -checks-reporter-with-killswitch "Mocha Tests" \ - node scripts/mocha diff --git a/.eslintrc.js b/.eslintrc.js index 82275d9441cc2..db5d36d1168df 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -71,11 +71,6 @@ const SAFER_LODASH_SET_DEFINITELYTYPED_HEADER = ` */ `; -const allMochaRulesOff = {}; -Object.keys(require('eslint-plugin-mocha').rules).forEach((k) => { - allMochaRulesOff['mocha/' + k] = 'off'; -}); - module.exports = { root: true, @@ -542,7 +537,6 @@ module.exports = { 'packages/kbn-eslint-import-resolver-kibana/**/*.js', 'packages/kbn-eslint-plugin-eslint/**/*', 'x-pack/gulpfile.js', - 'x-pack/dev-tools/mocha/setup_mocha.js', 'x-pack/scripts/*.js', ], excludedFiles: ['**/integration_tests/**/*'], @@ -574,7 +568,9 @@ module.exports = { */ { files: ['test/harden/*.js', 'packages/elastic-safer-lodash-set/test/*.js'], - rules: allMochaRulesOff, + rules: { + 'mocha/handle-done-callback': 'off', + }, }, { files: ['**/*.{js,mjs,ts,tsx}'], @@ -794,7 +790,6 @@ module.exports = { files: ['x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}'], plugins: ['eslint-plugin-node', 'react'], env: { - mocha: true, jest: true, }, rules: { @@ -930,7 +925,6 @@ module.exports = { files: ['x-pack/plugins/lists/**/*.{js,mjs,ts,tsx}'], plugins: ['eslint-plugin-node'], env: { - mocha: true, jest: true, }, rules: { diff --git a/.teamcity/src/builds/test/QuickTests.kt b/.teamcity/src/builds/test/QuickTests.kt index a294fce9599c3..086f65e4ee26b 100644 --- a/.teamcity/src/builds/test/QuickTests.kt +++ b/.teamcity/src/builds/test/QuickTests.kt @@ -14,7 +14,6 @@ object QuickTests : BuildType({ val testScripts = mapOf( "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" ) steps { diff --git a/docs/developer/contributing/development-tests.asciidoc b/docs/developer/contributing/development-tests.asciidoc index 647dc8b3f3b26..7aabc480cdaa2 100644 --- a/docs/developer/contributing/development-tests.asciidoc +++ b/docs/developer/contributing/development-tests.asciidoc @@ -17,10 +17,6 @@ root) |Jest (integration) |`**/integration_tests/**/*.test.{js,mjs,ts,tsx}` |`yarn test:jest_integration [test path]` -|Mocha -|`**/__tests__/**/*.js` -|`node scripts/mocha --grep=regexp [test path]` - |Functional |`test/**/config.js` `x-pack/test/**/config.js` |`node scripts/functional_tests_server --config [directory]/config.js``node scripts/functional_test_runner_ --config [directory]/config.js --grep=regexp` @@ -60,8 +56,6 @@ kibana/src/plugins/dashboard/server$ yarn test:jest --coverage yarn jest --coverage --verbose --config /home/tyler/elastic/kibana/src/plugins/dashboard/jest.config.js server ---- -NOTE: There are still a handful of legacy tests that use the Mocha test runner. For those tests, use `node scripts/mocha --grep=regexp [test path]`. Tests using Mocha are located within `__tests__` directories. - [discrete] === Running browser automation tests diff --git a/docs/developer/contributing/development-unit-tests.asciidoc b/docs/developer/contributing/development-unit-tests.asciidoc index d5f5bc76b3302..72edaf1241bc6 100644 --- a/docs/developer/contributing/development-unit-tests.asciidoc +++ b/docs/developer/contributing/development-unit-tests.asciidoc @@ -1,20 +1,7 @@ [[development-unit-tests]] == Unit testing frameworks -{kib} is migrating unit testing from `Mocha` to `Jest`. Legacy unit tests -still exist in Mocha but all new unit tests should be written in Jest. - -[discrete] -=== Mocha (legacy) - -Mocha tests are contained in `__tests__` directories. - -*Running Mocha Unit Tests* - -["source","shell"] ------------ -yarn test:mocha ------------ +{kib} is doing unit testing doing `Jest`. [discrete] == Jest @@ -92,23 +79,10 @@ the `--debug-brk` flag. You’ll need to connect a remote debugger such as https://github.com/node-inspector/node-inspector[`node-inspector`] to proceed in this mode. -[source,bash] ----- -node scripts/mocha --debug ----- - [discrete] === Unit Testing Plugins -This should work super if you’re using the +Even when using https://github.com/elastic/kibana/tree/master/packages/kbn-plugin-generator[Kibana -plugin generator]. If you’re not using the generator, well, you’re on -your own. We suggest you look at how the generator works. - -To run the tests for just your particular plugin run the following -command from your plugin: - -[source,bash] ----- -yarn test:mocha ----- \ No newline at end of file +plugin generator] we do not enforce a way for unit testing your plugin. Please setup and you use +the tools of your choice. If the plugin will live inside the Kibana repo `Jest` must be used. \ No newline at end of file diff --git a/docs/developer/contributing/interpreting-ci-failures.asciidoc b/docs/developer/contributing/interpreting-ci-failures.asciidoc index bb623bc7a541c..38609b2be5c8c 100644 --- a/docs/developer/contributing/interpreting-ci-failures.asciidoc +++ b/docs/developer/contributing/interpreting-ci-failures.asciidoc @@ -27,7 +27,7 @@ image::images/job_view.png[] [discrete] === Viewing ciGroup/test Logs -To view the logs for a failed specific ciGroup, jest, mocha, type checkers, linters, etc., click on the *Pipeline Steps* link in from the Job page. +To view the logs for a failed specific ciGroup, jest, type checkers, linters, etc., click on the *Pipeline Steps* link in from the Job page. image::images/pipeline_steps_view.png[] diff --git a/package.json b/package.json index 7effbd0a5550a..053b2060dc4d3 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "es": "node scripts/es", "test:jest": "node scripts/jest", "test:jest_integration": "node scripts/jest_integration", - "test:mocha": "node scripts/mocha", "test:ftr": "node scripts/functional_tests", "test:ftr:server": "node scripts/functional_tests_server", "test:ftr:runner": "node scripts/functional_test_runner", diff --git a/packages/README.md b/packages/README.md index 9d9cd4ed7b6e5..a328fe70e201f 100644 --- a/packages/README.md +++ b/packages/README.md @@ -44,23 +44,17 @@ All new packages should use the `@kbn` namespace, and should be marked with ## Unit tests for a package -Currently there are two patterns used to test packages, one using Mocha and one using Jest. These patterns emerged out of convention and we'd like to make them more similar to each other in the near future. +Currently there is only one tool being used in order to test packages which is Jest. Below we will explain how it should be done. -### 1. Mocha -Today a package can follow the pattern of having a `__tests__` directory in each source code directory of a package which contains the tests for that module. These are usually run by Mocha. - -If a package's tests should be run with Mocha, you'll have to opt-in to run them by appending the package's test file pattern(s) to Kibana's `src/dev/mocha/run_mocha_cli.js` file. These will then be run by the unit test runner. - -* `yarn test` or `yarn grunt test` runs all unit tests. -* `node scripts/mocha` runs all Mocha tests. - -### 2. Jest -A package can also follow the pattern of having `.test.js` files as siblings of the source code files, and these run by Jest. +### Jest +A package should follow the pattern of having `.test.js` files as siblings of the source code files, and these run by Jest. A package using the `.test.js` naming convention will have those tests automatically picked up by Jest and run by the unit test runner, currently mapped to the Kibana `test` script in the root `package.json`. * `yarn test` or `yarn grunt test` runs all unit tests. * `yarn jest` runs all Jest tests in Kibana. +In order for the plugin or package to use Jest, a jest.config.js file must be present in it's root. However, there are safeguards for this in CI should a test file be added without a corresponding config file. + ---- Each package can also specify its own `test` script in the package's `package.json`, for cases where you'd prefer to run the tests from the local package directory. diff --git a/packages/kbn-plugin-generator/README.md b/packages/kbn-plugin-generator/README.md index bee8e6c2ca783..4603c9ed7b1ba 100644 --- a/packages/kbn-plugin-generator/README.md +++ b/packages/kbn-plugin-generator/README.md @@ -63,10 +63,6 @@ Generated plugins receive a handful of scripts that can be used during developme Build a distributable archive of your plugin. - - `yarn test:mocha` - - Run the server tests using mocha. - To start kibana run the following command from Kibana root. diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index a88820eb281cc..fa7124a5b042d 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -46,12 +46,7 @@ export { readConfigFile } from './functional_test_runner/lib/config/read_config_ export { runFtrCli } from './functional_test_runner/cli'; -export { - createAutoJUnitReporter, - runMochaCli, - setupJUnitReportGeneration, - escapeCdata, -} from './mocha'; +export { setupJUnitReportGeneration, escapeCdata } from './mocha'; export { runFailedTestsReporterCli } from './failed_tests_reporter'; diff --git a/packages/kbn-test/src/mocha/auto_junit_reporter.js b/packages/kbn-test/src/mocha/auto_junit_reporter.js deleted file mode 100644 index b6e79616e1cde..0000000000000 --- a/packages/kbn-test/src/mocha/auto_junit_reporter.js +++ /dev/null @@ -1,37 +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 mocha from 'mocha'; -import { setupJUnitReportGeneration } from './junit_report_generation'; - -const MochaSpecReporter = mocha.reporters.spec; - -export function createAutoJUnitReporter(junitReportOptions) { - return class AutoJUnitReporter { - constructor(runner, options) { - // setup a spec reporter for console output - new MochaSpecReporter(runner, options); - - // in CI we also setup the JUnit reporter - if (process.env.CI && !process.env.DISABLE_JUNIT_REPORTER) { - setupJUnitReportGeneration(runner, junitReportOptions); - } - } - }; -} diff --git a/packages/kbn-test/src/mocha/index.ts b/packages/kbn-test/src/mocha/index.ts index 99c0f6f4230b7..aadf1b7f16a19 100644 --- a/packages/kbn-test/src/mocha/index.ts +++ b/packages/kbn-test/src/mocha/index.ts @@ -17,13 +17,9 @@ * under the License. */ -// @ts-ignore not typed yet -export { createAutoJUnitReporter } from './auto_junit_reporter'; // @ts-ignore not typed yet export { setupJUnitReportGeneration } from './junit_report_generation'; // @ts-ignore not typed yet -export { runMochaCli } from './run_mocha_cli'; -// @ts-ignore not typed yet export { recordLog, snapshotLogsForRunnable } from './log_cache'; // @ts-ignore not typed yet export { escapeCdata } from './xml'; diff --git a/packages/kbn-test/src/mocha/run_mocha_cli.js b/packages/kbn-test/src/mocha/run_mocha_cli.js deleted file mode 100644 index f61b309610341..0000000000000 --- a/packages/kbn-test/src/mocha/run_mocha_cli.js +++ /dev/null @@ -1,99 +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 { REPO_ROOT } from '@kbn/utils'; -import getopts from 'getopts'; -import globby from 'globby'; - -export function runMochaCli() { - const opts = getopts(process.argv.slice(2), { - alias: { - t: 'timeout', - }, - boolean: ['no-timeouts'], - }); - - const runInBand = - process.execArgv.includes('--inspect') || process.execArgv.includes('--inspect-brk'); - - // ensure that mocha exits when test have completed - process.argv.push('--exit'); - - // check that we aren't leaking any globals - process.argv.push('--check-leaks'); - // prevent globals injected from canvas plugins from triggering leak check - process.argv.push('--globals', '__core-js_shared__,core,_, '); - - // set default test timeout - if (opts.timeout == null && !opts['no-timeouts']) { - if (runInBand) { - process.argv.push('--no-timeouts'); - } else { - process.argv.push('--timeout', '10000'); - } - } - - // set default slow timeout - if (opts.slow == null) { - process.argv.push('--slow', '5000'); - } - - // set default reporter - if (opts.reporter == null) { - process.argv.push('--reporter', require.resolve('./server_junit_reporter')); - } - - // set default test files - if (!opts._.length) { - globby - .sync( - [ - 'src/**/__tests__/**/*.{js,ts,tsx}', - 'packages/**/__tests__/**/*.{js,ts,tsx}', - 'tasks/**/__tests__/**/*.{js,ts,tsx}', - 'x-pack/common/**/__tests__/**/*.{js,ts,tsx}', - 'x-pack/server/**/__tests__/**/*.{js,ts,tsx}', - `x-pack/legacy/plugins/*/__tests__/**/*.{js,ts,tsx}`, - `x-pack/legacy/plugins/*/common/**/__tests__/**/*.{js,ts,tsx}`, - `x-pack/legacy/plugins/*/**/server/**/__tests__/**/*.{js,ts,tsx}`, - ], - { - cwd: REPO_ROOT, - onlyFiles: true, - absolute: true, - ignore: [ - '**/__tests__/fixtures/**', - 'src/**/public/**', - '**/_*.{js,ts,tsx}', - '**/*.test.*', - 'packages/**/target/**', - ], - } - ) - .forEach((file) => { - process.argv.push(file); - }); - } - - if (runInBand) { - require('mocha/bin/_mocha'); - } else { - require('mocha/bin/mocha'); - } -} diff --git a/packages/kbn-test/src/mocha/server_junit_reporter.js b/packages/kbn-test/src/mocha/server_junit_reporter.js deleted file mode 100644 index c629f401d0580..0000000000000 --- a/packages/kbn-test/src/mocha/server_junit_reporter.js +++ /dev/null @@ -1,25 +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. - */ - -// when the reporter is loaded by mocha in child process it might be before setup_node_env -require('../../../../src/setup_node_env'); - -module.exports = require('./auto_junit_reporter').createAutoJUnitReporter({ - reportName: 'Server Mocha Tests', -}); diff --git a/scripts/mocha.js b/scripts/mocha.js deleted file mode 100644 index 8d25d18a594cc..0000000000000 --- a/scripts/mocha.js +++ /dev/null @@ -1,21 +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. - */ - -require('../src/setup_node_env'); -require('@kbn/test').runMochaCli(); diff --git a/src/dev/index.js b/src/dev/index.js index f24eee040c867..d9d0cec0f1573 100644 --- a/src/dev/index.js +++ b/src/dev/index.js @@ -17,6 +17,6 @@ * under the License. */ -export { createAutoJUnitReporter, setupJUnitReportGeneration } from '@kbn/test'; +export { setupJUnitReportGeneration } from '@kbn/test'; export { generateNoticeFromSource } from './notice'; diff --git a/test/scripts/jenkins_unit.sh b/test/scripts/jenkins_unit.sh index 00fd378b348a0..6e28f9c3ef56a 100755 --- a/test/scripts/jenkins_unit.sh +++ b/test/scripts/jenkins_unit.sh @@ -15,7 +15,6 @@ if [[ -z "$CODE_COVERAGE" ]] ; then # Test ./test/scripts/test/jest_integration.sh - ./test/scripts/test/mocha.sh ./test/scripts/test/jest_unit.sh ./test/scripts/test/api_integration.sh diff --git a/test/scripts/test/mocha.sh b/test/scripts/test/mocha.sh deleted file mode 100755 index 5e005c89330ca..0000000000000 --- a/test/scripts/test/mocha.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -# TODO: will remove mocha in another PR -# checks-reporter-with-killswitch "Mocha Tests" \ -# node scripts/mocha diff --git a/test/tsconfig.json b/test/tsconfig.json index 684efc145e4d0..c8e6e69586ca0 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../tsconfig.base.json", "compilerOptions": { "incremental": false, - "types": ["node", "mocha", "flot"] + "types": ["node", "flot"] }, "include": ["**/*", "../typings/elastic__node_crypto.d.ts", "typings/**/*", "../packages/kbn-test/types/ftr_globals/**/*"], "exclude": ["plugin_functional/plugins/**/*", "interpreter_functional/plugins/**/*"], diff --git a/vars/tasks.groovy b/vars/tasks.groovy index ec2af87caba56..68a6d4b32618c 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -32,7 +32,6 @@ def test() { tasks([ // These 2 tasks require isolation because of hard-coded, conflicting ports and such, so let's use Docker here kibanaPipeline.scriptTaskDocker('Jest Integration Tests', 'test/scripts/test/jest_integration.sh'), - kibanaPipeline.scriptTaskDocker('Mocha Tests', 'test/scripts/test/mocha.sh'), kibanaPipeline.scriptTask('Jest Unit Tests', 'test/scripts/test/jest_unit.sh'), kibanaPipeline.scriptTask('API Integration Tests', 'test/scripts/test/api_integration.sh'), diff --git a/x-pack/package.json b/x-pack/package.json index 34ef8bb589b44..97fe5144f135a 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -12,8 +12,7 @@ "build": "../node_modules/.bin/gulp build", "testonly": "echo 'Deprecated, use `yarn test`'", "test": "../node_modules/.bin/gulp test", - "test:jest": "node ../scripts/jest", - "test:mocha": "node scripts/mocha" + "test:jest": "node ../scripts/jest" }, "kibana": { "build": { diff --git a/x-pack/plugins/canvas/storybook/webpack.dll.config.js b/x-pack/plugins/canvas/storybook/webpack.dll.config.js index b830f72692ede..2fa31235ac0bb 100644 --- a/x-pack/plugins/canvas/storybook/webpack.dll.config.js +++ b/x-pack/plugins/canvas/storybook/webpack.dll.config.js @@ -39,7 +39,6 @@ module.exports = { 'jquery', 'lodash', 'markdown-it', - 'mocha', 'monaco-editor', 'prop-types', 'react-ace', diff --git a/x-pack/scripts/mocha.js b/x-pack/scripts/mocha.js deleted file mode 100644 index cc0f94a2a760e..0000000000000 --- a/x-pack/scripts/mocha.js +++ /dev/null @@ -1,7 +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. - */ - -require('../../scripts/mocha'); diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 0fbb16c0e8c87..eac9d92884028 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { // overhead is too significant "incremental": false, - "types": ["mocha", "node", "flot"] + "types": ["node", "flot"] }, "include": ["**/*", "../typings/**/*", "../../packages/kbn-test/types/ftr_globals/**/*"], "exclude": ["../typings/jest.d.ts"], From f5ec1dc3525a2234d6e6fa34e0dd98647a45c51d Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Fri, 15 Jan 2021 22:34:54 +0100 Subject: [PATCH 30/38] [APM] Explicitly set environment for cross-service links (#87481) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apm/common/environment_filter_values.ts | 24 +++++++++++++++++++ .../MaybeViewTraceLink.tsx | 10 +++++++- .../Waterfall/FlyoutTopLevelProperties.tsx | 10 +++++++- .../SpanFlyout/StickySpanProperties.tsx | 11 ++++++++- .../index.tsx | 21 +++++++++++----- .../components/shared/Links/apm/APMLink.tsx | 20 +++++++++++----- .../shared/Links/apm/ErrorOverviewLink.tsx | 5 +++- .../shared/Links/apm/MetricOverviewLink.tsx | 5 +++- .../shared/Links/apm/ServiceMapLink.tsx | 4 ++-- .../Links/apm/ServiceNodeOverviewLink.tsx | 5 +++- .../shared/Links/apm/TraceOverviewLink.tsx | 2 +- .../Links/apm/service_inventory_link.tsx | 2 +- .../Links/apm/service_overview_link.tsx | 20 +++++++++++++--- .../service_transactions_overview_link.tsx | 16 ++++++++++--- .../Links/apm/transaction_detail_link.tsx | 5 +++- .../apm/typings/es_schemas/raw/span_raw.ts | 1 + 16 files changed, 132 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/apm/common/environment_filter_values.ts b/x-pack/plugins/apm/common/environment_filter_values.ts index e231f37a170ed..8f5b6456b8c92 100644 --- a/x-pack/plugins/apm/common/environment_filter_values.ts +++ b/x-pack/plugins/apm/common/environment_filter_values.ts @@ -33,3 +33,27 @@ export const ENVIRONMENT_NOT_DEFINED = { export function getEnvironmentLabel(environment: string) { return environmentLabels[environment] || environment; } + +// returns the environment url param that should be used +// based on the requested environment. If the requested +// environment is different from the URL parameter, we'll +// return ENVIRONMENT_ALL. If it's not, we'll just return +// the current environment URL param +export function getNextEnvironmentUrlParam({ + requestedEnvironment, + currentEnvironmentUrlParam, +}: { + requestedEnvironment?: string; + currentEnvironmentUrlParam?: string; +}) { + const normalizedRequestedEnvironment = + requestedEnvironment || ENVIRONMENT_NOT_DEFINED.value; + const normalizedQueryEnvironment = + currentEnvironmentUrlParam || ENVIRONMENT_ALL.value; + + if (normalizedRequestedEnvironment === normalizedQueryEnvironment) { + return currentEnvironmentUrlParam; + } + + return ENVIRONMENT_ALL.value; +} diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx index 49a016f338888..fec14ccf76c93 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx @@ -7,6 +7,7 @@ import { EuiButton, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { getNextEnvironmentUrlParam } from '../../../../../common/environment_filter_values'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { Transaction as ITransaction } from '../../../../../typings/es_schemas/ui/transaction'; import { TransactionDetailLink } from '../../../shared/Links/apm/transaction_detail_link'; @@ -20,8 +21,9 @@ export const MaybeViewTraceLink = ({ waterfall: IWaterfall; }) => { const { - urlParams: { latencyAggregationType }, + urlParams: { environment, latencyAggregationType }, } = useUrlParams(); + const viewFullTraceButtonLabel = i18n.translate( 'xpack.apm.transactionDetails.viewFullTraceButtonLabel', { @@ -73,6 +75,11 @@ export const MaybeViewTraceLink = ({ // the user is viewing a zoomed in version of the trace. Link to the full trace } else { + const nextEnvironment = getNextEnvironmentUrlParam({ + requestedEnvironment: rootTransaction?.service.environment, + currentEnvironmentUrlParam: environment, + }); + return ( {viewFullTraceButtonLabel} diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx index a67ec0a69ed87..6b6d54b6cbad6 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { useUrlParams } from '../../../../../../context/url_params_context/use_url_params'; +import { getNextEnvironmentUrlParam } from '../../../../../../../common/environment_filter_values'; import { SERVICE_NAME, TRANSACTION_NAME, @@ -22,13 +23,18 @@ interface Props { export function FlyoutTopLevelProperties({ transaction }: Props) { const { - urlParams: { latencyAggregationType }, + urlParams: { environment, latencyAggregationType }, } = useUrlParams(); if (!transaction) { return null; } + const nextEnvironment = getNextEnvironmentUrlParam({ + requestedEnvironment: transaction.service.environment, + currentEnvironmentUrlParam: environment, + }); + const stickyProperties = [ { label: i18n.translate('xpack.apm.transactionDetails.serviceLabel', { @@ -38,6 +44,7 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { val: ( {transaction.service.name} @@ -56,6 +63,7 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { traceId={transaction.trace.id} transactionName={transaction.transaction.name} transactionType={transaction.transaction.type} + environment={nextEnvironment} latencyAggregationType={latencyAggregationType} > {transaction.transaction.name} diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx index 5a1f6e3d2a24d..82b43b3dce99a 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { useUrlParams } from '../../../../../../../context/url_params_context/use_url_params'; +import { getNextEnvironmentUrlParam } from '../../../../../../../../common/environment_filter_values'; import { SERVICE_NAME, SPAN_NAME, @@ -26,8 +27,14 @@ interface Props { export function StickySpanProperties({ span, transaction }: Props) { const { - urlParams: { latencyAggregationType }, + urlParams: { environment, latencyAggregationType }, } = useUrlParams(); + + const nextEnvironment = getNextEnvironmentUrlParam({ + requestedEnvironment: transaction?.service.environment, + currentEnvironmentUrlParam: environment, + }); + const spanName = span.span.name; const transactionStickyProperties = transaction ? [ @@ -39,6 +46,7 @@ export function StickySpanProperties({ span, transaction }: Props) { val: ( {transaction.service.name} @@ -60,6 +68,7 @@ export function StickySpanProperties({ span, transaction }: Props) { traceId={transaction.trace.id} transactionName={transaction.transaction.name} transactionType={transaction.transaction.type} + environment={nextEnvironment} latencyAggregationType={latencyAggregationType} > {transaction.transaction.name} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx index 079c599f8f7ba..1bd7310e3251d 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx @@ -13,7 +13,10 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; +import { + ENVIRONMENT_ALL, + getNextEnvironmentUrlParam, +} from '../../../../../common/environment_filter_values'; import { asMillisecondDuration, asPercent, @@ -40,6 +43,10 @@ interface Props { } export function ServiceOverviewDependenciesTable({ serviceName }: Props) { + const { + urlParams: { start, end, environment }, + } = useUrlParams(); + const columns: Array> = [ { field: 'name', @@ -64,7 +71,13 @@ export function ServiceOverviewDependenciesTable({ serviceName }: Props) { {item.type === 'service' ? ( - + {item.name} ) : ( @@ -154,10 +167,6 @@ export function ServiceOverviewDependenciesTable({ serviceName }: Props) { }, ]; - const { - urlParams: { start, end, environment }, - } = useUrlParams(); - const { data = [], status } = useFetcher(() => { if (!start || !end) { return; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx index 7acc2542a65f3..fe5bfc6c4679b 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx @@ -36,16 +36,24 @@ export const PERSISTENT_APM_PARAMS: Array = [ /** * Hook to get a link for a path with persisted filters */ -export function useAPMHref( - path: string, - persistentFilters: Array = [] -) { +export function useAPMHref({ + path, + persistedFilters, + query, +}: { + path: string; + persistedFilters?: Array; + query?: APMQueryParams; +}) { const { urlParams } = useUrlParams(); const { basePath } = useApmPluginContext().core.http; const { search } = useLocation(); - const query = pickKeys(urlParams as APMQueryParams, ...persistentFilters); + const nextQuery = { + ...pickKeys(urlParams as APMQueryParams, ...(persistedFilters ?? [])), + ...query, + }; - return getAPMHref({ basePath, path, query, search }); + return getAPMHref({ basePath, path, query: nextQuery, search }); } /** diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx index dcf21de7dca8d..506787c0fe62f 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx @@ -17,7 +17,10 @@ const persistedFilters: Array = [ ]; export function useErrorOverviewHref(serviceName: string) { - return useAPMHref(`/services/${serviceName}/errors`, persistedFilters); + return useAPMHref({ + path: `/services/${serviceName}/errors`, + persistedFilters, + }); } interface Props extends APMLinkExtendProps { diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx index 8031b6088d420..e369e920a4b82 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx @@ -16,7 +16,10 @@ const persistedFilters: Array = [ ]; export function useMetricOverviewHref(serviceName: string) { - return useAPMHref(`/services/${serviceName}/metrics`, persistedFilters); + return useAPMHref({ + path: `/services/${serviceName}/metrics`, + persistedFilters, + }); } interface Props extends APMLinkExtendProps { diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx index 670b7137219e1..b5b74f06d65ba 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx @@ -8,10 +8,10 @@ import React from 'react'; import { APMLinkExtendProps, useAPMHref } from './APMLink'; export function useServiceMapHref(serviceName?: string) { - const pathFor = serviceName + const path = serviceName ? `/services/${serviceName}/service-map` : '/service-map'; - return useAPMHref(pathFor); + return useAPMHref({ path }); } interface ServiceMapLinkProps extends APMLinkExtendProps { diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx index 279c038d95a80..b2382ef99e982 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx @@ -14,5 +14,8 @@ const persistedFilters: Array = [ ]; export function useServiceNodeOverviewHref(serviceName: string) { - return useAPMHref(`/services/${serviceName}/nodes`, persistedFilters); + return useAPMHref({ + path: `/services/${serviceName}/nodes`, + persistedFilters, + }); } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx index 3cb0009a12c94..a5f7b3f2b28e4 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx @@ -20,5 +20,5 @@ const persistedFilters: Array = [ ]; export function useTraceOverviewHref() { - return useAPMHref('/traces', persistedFilters); + return useAPMHref({ path: '/traces', persistedFilters }); } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx index c3b80cbeb701b..1d879f86dff3d 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx @@ -15,5 +15,5 @@ import { useAPMHref } from './APMLink'; const persistedFilters: Array = ['host', 'agentName']; export function useServiceInventoryHref() { - return useAPMHref('/services', persistedFilters); + return useAPMHref({ path: '/services', persistedFilters }); } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx index ba53243a6bc75..31ce04cf23fbc 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx @@ -15,20 +15,34 @@ import { APMLinkExtendProps, useAPMHref } from './APMLink'; interface ServiceOverviewLinkProps extends APMLinkExtendProps { serviceName: string; + environment?: string; } const persistedFilters: Array = [ 'latencyAggregationType', ]; -export function useServiceOverviewHref(serviceName: string) { - return useAPMHref(`/services/${serviceName}/overview`, persistedFilters); +export function useServiceOverviewHref( + serviceName: string, + environment?: string +) { + const query = environment + ? { + environment, + } + : {}; + return useAPMHref({ + path: `/services/${serviceName}/overview`, + persistedFilters, + query, + }); } export function ServiceOverviewLink({ serviceName, + environment, ...rest }: ServiceOverviewLinkProps) { - const href = useServiceOverviewHref(serviceName); + const href = useServiceOverviewHref(serviceName, environment); return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx index 8b96ba8ab233a..c4ea99030d87c 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx @@ -17,18 +17,28 @@ const persistedFilters: Array = [ 'latencyAggregationType', ]; -export function useServiceOrTransactionsOverviewHref(serviceName: string) { - return useAPMHref(`/services/${serviceName}`, persistedFilters); +export function useServiceOrTransactionsOverviewHref( + serviceName: string, + environment?: string +) { + const query = environment ? { environment } : {}; + return useAPMHref({ + path: `/services/${serviceName}`, + persistedFilters, + query, + }); } interface Props extends APMLinkExtendProps { serviceName: string; + environment?: string; } export function ServiceOrTransactionsOverviewLink({ serviceName, + environment, ...rest }: Props) { - const href = useServiceOrTransactionsOverviewHref(serviceName); + const href = useServiceOrTransactionsOverviewHref(serviceName, environment); return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_detail_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_detail_link.tsx index 8108dcf41321f..a97d860d99798 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_detail_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_detail_link.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { useLocation } from 'react-router-dom'; import { EuiLink } from '@elastic/eui'; +import { pickBy, identity } from 'lodash'; import { getAPMHref, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { pickKeys } from '../../../../../common/utils/pick_keys'; @@ -20,6 +21,7 @@ interface Props extends APMLinkExtendProps { transactionName: string; transactionType: string; latencyAggregationType?: string; + environment?: string; } const persistedFilters: Array = [ @@ -34,6 +36,7 @@ export function TransactionDetailLink({ transactionName, transactionType, latencyAggregationType, + environment, ...rest }: Props) { const { urlParams } = useUrlParams(); @@ -47,8 +50,8 @@ export function TransactionDetailLink({ transactionId, transactionName, transactionType, - ...(latencyAggregationType ? { latencyAggregationType } : {}), ...pickKeys(urlParams as APMQueryParams, ...persistedFilters), + ...pickBy({ latencyAggregationType, environment }, identity), }, search: location.search, }); diff --git a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts index e152ed23af1b3..3938a9b54d913 100644 --- a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts +++ b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts @@ -18,6 +18,7 @@ export interface SpanRaw extends APMBaseDoc { trace: { id: string }; // trace is required service: { name: string; + environment?: string; }; span: { destination?: { From fbb8238f5747e967d14246b3f527fe334091f4de Mon Sep 17 00:00:00 2001 From: Vadim Yakhin Date: Fri, 15 Jan 2021 18:04:45 -0400 Subject: [PATCH 31/38] Porting fixes 1 (#88477) * Remove Manage button from Source overview page * Replace empty select option with "Leave unassigned" * Replace content source key with id * Update Google icons * Replace anchor with EuiLink * Add Folders as one of the synced items for Box * Add DLP feature UI to Jira and Confluence cloud pages * Fix the "Fix" link path Also updated TS types to match ent-search * Fix copy and button color on "Content source is disabled" callout * Remove incorrect copy from Private sources empty state * Move constants from logic file to a separate file, rename a constant * Fix i18n path to not include file name Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../shared/assets/source_icons/gmail.svg | 2 +- .../assets/source_icons/google_drive.svg | 2 +- .../assets/sources_full_bleed/gmail.svg | 2 +- .../sources_full_bleed/google_drive.svg | 2 +- .../shared/source_row/source_row.tsx | 14 +++++++-- .../workplace_search/constants.ts | 3 ++ .../applications/workplace_search/routes.ts | 2 +- .../applications/workplace_search/types.ts | 2 -- .../components/add_source/constants.ts | 6 ++-- .../components/add_source/save_custom.tsx | 12 +++----- .../components/display_settings/constants.ts | 21 +++++++++++++ .../display_settings_logic.ts | 9 +++--- .../display_settings/search_results.tsx | 11 ++++--- .../content_sources/components/overview.tsx | 30 ++++--------------- .../views/content_sources/private_sources.tsx | 11 +------ .../views/content_sources/source_data.tsx | 4 ++- .../views/content_sources/source_router.tsx | 11 +++---- .../views/setup_guide/setup_guide.tsx | 6 ++-- 18 files changed, 79 insertions(+), 71 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/constants.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg index ee824f730aca6..ae068feb7133d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg index 59469d814e35f..c684cecb71235 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg index 98d418244c22f..31fe60c6a63f9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg index 6541b3f9e753f..f3fe82cd3cd98 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx index 818d06c55dd12..2080f454596b5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx @@ -26,7 +26,12 @@ import { import { EuiLinkTo } from '../../../../shared/react_router_helpers'; import { SOURCE_STATUSES as statuses } from '../../../constants'; import { ContentSourceDetails } from '../../../types'; -import { ADD_SOURCE_PATH, SOURCE_DETAILS_PATH, getContentSourcePath } from '../../../routes'; +import { + ADD_SOURCE_PATH, + SOURCE_DETAILS_PATH, + getContentSourcePath, + getSourcesPath, +} from '../../../routes'; import { SourceIcon } from '../source_icon'; @@ -77,7 +82,12 @@ export const SourceRow: React.FC = ({ const imageClass = classNames('source-row__icon', { 'source-row__icon--loading': isIndexing }); const fixLink = ( - + Fix ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts index 74e0682db89b5..4ca256ac91a3f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts @@ -136,6 +136,9 @@ export const SOURCE_NAMES = { }; export const SOURCE_OBJ_TYPES = { + FOLDERS: i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.objTypes.folders', { + defaultMessage: 'Folders', + }), PAGES: i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.objTypes.pages', { defaultMessage: 'Pages', }), diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts index 868d76f7d09c5..1e4b51e157724 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts @@ -124,7 +124,7 @@ export const getContentSourcePath = ( export const getGroupPath = (groupId: string): string => generatePath(GROUP_PATH, { groupId }); export const getGroupSourcePrioritizationPath = (groupId: string): string => `${GROUPS_PATH}/${groupId}/source_prioritization`; -export const getSourcesPath = (path: string, isOrganization: boolean): string => +export const getSourcesPath = (path: string, isOrganization?: boolean): string => isOrganization ? path : `${PERSONAL_PATH}${path}`; export const getReindexJobRoute = ( sourceId: string, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts index 9bda686ebbf00..ed4946a019bb0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts @@ -130,7 +130,6 @@ export interface ContentSourceFullData extends ContentSourceDetails { groups: Group[]; custom: boolean; accessToken: string; - key: string; urlField: string; titleField: string; licenseSupportsPermissions: boolean; @@ -177,7 +176,6 @@ export enum FeatureIds { export interface CustomSource { accessToken: string; - key: string; name: string; id: string; } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts index 09a9d22461e14..7d891953e618b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts @@ -299,10 +299,10 @@ export const SAVE_CUSTOM_ACCESS_TOKEN_LABEL = i18n.translate( } ); -export const SAVE_CUSTOM_API_KEY_LABEL = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.apiKey.label', +export const SAVE_CUSTOM_ID_LABEL = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.id.label', { - defaultMessage: 'Key', + defaultMessage: 'ID', } ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx index 59d691023f413..28aeaec2b47df 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx @@ -43,7 +43,7 @@ import { SAVE_CUSTOM_API_KEYS_TITLE, SAVE_CUSTOM_API_KEYS_BODY, SAVE_CUSTOM_ACCESS_TOKEN_LABEL, - SAVE_CUSTOM_API_KEY_LABEL, + SAVE_CUSTOM_ID_LABEL, SAVE_CUSTOM_VISUAL_WALKTHROUGH_TITLE, SAVE_CUSTOM_STYLING_RESULTS_TITLE, SAVE_CUSTOM_DOC_PERMISSIONS_TITLE, @@ -59,7 +59,7 @@ interface SaveCustomProps { export const SaveCustom: React.FC = ({ documentationUrl, - newCustomSource: { key, id, accessToken, name }, + newCustomSource: { id, accessToken, name }, isOrganization, header, }) => ( @@ -109,17 +109,13 @@ export const SaveCustom: React.FC = ({

{SAVE_CUSTOM_API_KEYS_BODY}

+ + - - diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/constants.ts new file mode 100644 index 0000000000000..0e6cbb2560128 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/constants.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const LEAVE_UNASSIGNED_FIELD = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.leaveUnassignedField', + { + defaultMessage: 'Leave unassigned', + } +); + +export const SUCCESS_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.successMessage', + { + defaultMessage: 'Display Settings have been successfuly updated.', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts index c52665524f566..a8636b4a34da1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts @@ -20,10 +20,8 @@ import { import { AppLogic } from '../../../../app_logic'; import { SourceLogic } from '../../source_logic'; -const SUCCESS_MESSAGE = 'Display Settings have been successfuly updated.'; - import { DetailField, SearchResultConfig, OptionValue, Result } from '../../../../types'; - +import { LEAVE_UNASSIGNED_FIELD, SUCCESS_MESSAGE } from './constants'; export interface DisplaySettingsResponseProps { sourceName: string; searchResultConfig: SearchResultConfig; @@ -271,7 +269,10 @@ export const DisplaySettingsLogic = kea< () => [selectors.fieldOptions], (fieldOptions) => { const optionalFieldOptions = cloneDeep(fieldOptions); - optionalFieldOptions.unshift({ value: '', text: '' }); + optionalFieldOptions.unshift({ + value: LEAVE_UNASSIGNED_FIELD, + text: LEAVE_UNASSIGNED_FIELD, + }); return optionalFieldOptions; }, ], diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx index cfe0ddb1533ec..96b7a6fbe14b5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { DisplaySettingsLogic } from './display_settings_logic'; +import { LEAVE_UNASSIGNED_FIELD } from './constants'; import { ExampleSearchResultGroup } from './example_search_result_group'; import { ExampleStandoutResult } from './example_standout_result'; @@ -104,8 +105,10 @@ export const SearchResults: React.FC = () => { className="field-selector" hasNoInitialSelection={true} data-test-subj="SubtitleFieldSelect" - value={subtitleField || ''} - onChange={({ target: { value } }) => setSubtitleField(value === '' ? null : value)} + value={subtitleField || LEAVE_UNASSIGNED_FIELD} + onChange={({ target: { value } }) => + setSubtitleField(value === LEAVE_UNASSIGNED_FIELD ? null : value) + } /> { className="field-selector" hasNoInitialSelection={true} data-test-subj="DescriptionFieldSelect" - value={descriptionField || ''} + value={descriptionField || LEAVE_UNASSIGNED_FIELD} onChange={({ target: { value } }) => - setDescriptionField(value === '' ? null : value) + setDescriptionField(value === LEAVE_UNASSIGNED_FIELD ? null : value) } /> diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx index 8d6e421005df7..71d79b4b2a082 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx @@ -33,8 +33,6 @@ import { DOCUMENT_PERMISSIONS_DOCS_URL, ENT_SEARCH_LICENSE_MANAGEMENT, EXTERNAL_IDENTITIES_DOCS_URL, - SOURCE_CONTENT_PATH, - getContentSourcePath, getGroupPath, } from '../../../routes'; @@ -45,7 +43,7 @@ import { CredentialItem } from '../../../components/shared/credential_item'; import { ViewContentHeader } from '../../../components/shared/view_content_header'; import { LicenseBadge } from '../../../components/shared/license_badge'; import { Loading } from '../../../../shared/loading'; -import { EuiButtonEmptyTo, EuiPanelTo } from '../../../../shared/react_router_helpers'; +import { EuiPanelTo } from '../../../../shared/react_router_helpers'; import aclImage from '../../../assets/supports_acl.svg'; import { SourceLogic } from '../source_logic'; @@ -63,7 +61,6 @@ export const Overview: React.FC = () => { details, custom, accessToken, - key, licenseSupportsPermissions, serviceTypeSupportsPermissions, indexPermissions, @@ -105,24 +102,9 @@ export const Overview: React.FC = () => { return (
- - - -

Content summary

-
-
- {totalDocuments > 0 && ( - - - Manage - - - )} -
+ +

Content summary

+
{!summary && } @@ -388,9 +370,9 @@ export const Overview: React.FC = () => { - + - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx index 0f5f4ffb9e0da..a1a76c678866c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx @@ -107,16 +107,7 @@ export const PrivateSources: React.FC = () => { - You have no private sources} - body={ -

- Select from the content sources below to create a private source, available only to - you -

- } - /> + You have no private sources} />
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx index 882c3861922e7..8aebbf721f8fc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx @@ -106,7 +106,7 @@ export const staticSourceData = [ } ), connectStepDescription: connectStepDescription.files, - objTypes: [SOURCE_OBJ_TYPES.ALL_FILES], + objTypes: [SOURCE_OBJ_TYPES.FOLDERS, SOURCE_OBJ_TYPES.ALL_FILES], features: { basicOrgContext: [ FeatureIds.SyncFrequency, @@ -156,6 +156,7 @@ export const staticSourceData = [ FeatureIds.SyncedItems, FeatureIds.GlobalAccessPermissions, ], + basicOrgContextExcludedFeatures: [FeatureIds.DocumentLevelPermissions], platinumOrgContext: [FeatureIds.SyncFrequency, FeatureIds.SyncedItems], platinumPrivateContext: [ FeatureIds.Private, @@ -435,6 +436,7 @@ export const staticSourceData = [ FeatureIds.SyncedItems, FeatureIds.GlobalAccessPermissions, ], + basicOrgContextExcludedFeatures: [FeatureIds.DocumentLevelPermissions], platinumOrgContext: [FeatureIds.SyncFrequency, FeatureIds.SyncedItems], platinumPrivateContext: [ FeatureIds.Private, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx index 7244ff21e3b38..089ef0cd46a00 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx @@ -84,12 +84,13 @@ export const SourceRouter: React.FC = () => { <>

- Your organization's license level changed and no longer supports document-level - permissions.{' '} + Your organization’s license level has changed. Your data is safe, but document-level + permissions are no longer supported and searching of this source has been disabled. + Upgrade to a Platinum license to re-enable this source.

-

Don't worry: your data is safe. Search has been disabled.

-

Upgrade to a Platinum license to re-enable this source.

- Explore Platinum license + + Explore Platinum license +
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx index 3b91c4e84d02f..8fefe3bac2a12 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { EuiSpacer, EuiTitle, EuiText, EuiButton } from '@elastic/eui'; +import { EuiSpacer, EuiTitle, EuiText, EuiButton, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -30,7 +30,7 @@ export const SetupGuide: React.FC = () => { -
+ { width="1280" height-="720" /> - +

From c66c9424d9109e6de2406786a2ec2b9921a98244 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 15 Jan 2021 19:05:45 -0700 Subject: [PATCH 32/38] [Maps] fix zooming while drawing shape filter logs errors in console (#88413) * [Maps] fix zooming while drawing shape filter logs errors in console * add unit test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../connected_components/mb_map/mb.utils.test.js | 12 ++++++++++++ .../maps/public/connected_components/mb_map/utils.js | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb.utils.test.js b/x-pack/plugins/maps/public/connected_components/mb_map/mb.utils.test.js index a28cc75f6d89d..2d6cba84e17e5 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb.utils.test.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb.utils.test.js @@ -185,4 +185,16 @@ describe('removeOrphanedSourcesAndLayers', () => { removeOrphanedSourcesAndLayers(mockMbMap, [], spatialFilterLayer); expect(mockMbMap.getStyle()).toEqual(styleWithSpatialFilters); }); + + test('should not remove mapbox gl draw layers and sources', async () => { + const fooLayer = makeMultiSourceMockLayer('foo'); + const layerList = [fooLayer]; + + const currentStyle = getMockStyle(layerList); + currentStyle.layers.push({ id: 'gl-draw-points' }); + const mockMbMap = new MockMbMap(currentStyle); + + removeOrphanedSourcesAndLayers(mockMbMap, layerList, spatialFilterLayer); + expect(mockMbMap.getStyle()).toEqual(currentStyle); + }); }); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/utils.js b/x-pack/plugins/maps/public/connected_components/mb_map/utils.js index e5801afd5b601..f12f34061756f 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/utils.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/utils.js @@ -16,6 +16,11 @@ export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLa return; } + // ignore gl-draw layers + if (mbLayer.id.startsWith('gl-draw')) { + return; + } + const layer = layerList.find((layer) => { return layer.ownsMbLayerId(mbLayer.id); }); From 5c112b8b5a89c9ea5ae88f018cf985329c3afe31 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Fri, 15 Jan 2021 19:07:45 -0800 Subject: [PATCH 33/38] [Alerting] Migrate Event Log plugin to TS project references (#81557) * [Alerting] Migrate Event Log plugin to TS project references * fixed faling typechecks * fixed path to spaces plugin ts file * fixed missing include * added fix for mapping.json * replaced package.json get version with kibanaVersion from plugin initial context * fixed build * fixed typechecks * fixed tests --- .../event_log/server/es/context.test.ts | 5 +++++ x-pack/plugins/event_log/server/es/context.ts | 3 ++- .../event_log/server/es/documents.test.ts | 3 ++- .../plugins/event_log/server/es/names.test.ts | 16 ++++++++------ x-pack/plugins/event_log/server/es/names.ts | 6 ++--- x-pack/plugins/event_log/server/plugin.ts | 3 +++ x-pack/plugins/event_log/tsconfig.json | 22 +++++++++++++++++++ x-pack/test/tsconfig.json | 1 + x-pack/tsconfig.json | 2 ++ x-pack/tsconfig.refs.json | 1 + 10 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/event_log/tsconfig.json diff --git a/x-pack/plugins/event_log/server/es/context.test.ts b/x-pack/plugins/event_log/server/es/context.test.ts index f30b71c99a043..5f26399618e38 100644 --- a/x-pack/plugins/event_log/server/es/context.test.ts +++ b/x-pack/plugins/event_log/server/es/context.test.ts @@ -25,6 +25,7 @@ describe('createEsContext', () => { logger, clusterClientPromise: Promise.resolve(clusterClient), indexNameRoot: 'test0', + kibanaVersion: '1.2.3', }); expect(context.initialized).toBeFalsy(); @@ -38,6 +39,7 @@ describe('createEsContext', () => { logger, clusterClientPromise: Promise.resolve(clusterClient), indexNameRoot: 'test-index', + kibanaVersion: '1.2.3', }); const esNames = context.esNames; @@ -57,6 +59,7 @@ describe('createEsContext', () => { logger, clusterClientPromise: Promise.resolve(clusterClient), indexNameRoot: 'test1', + kibanaVersion: '1.2.3', }); clusterClient.callAsInternalUser.mockResolvedValue(false); @@ -74,6 +77,7 @@ describe('createEsContext', () => { logger, clusterClientPromise: Promise.resolve(clusterClient), indexNameRoot: 'test2', + kibanaVersion: '1.2.3', }); clusterClient.callAsInternalUser.mockResolvedValue(true); context.initialize(); @@ -98,6 +102,7 @@ describe('createEsContext', () => { logger, clusterClientPromise: Promise.resolve(clusterClient), indexNameRoot: 'test2', + kibanaVersion: '1.2.3', }); context.initialize(); const success = await context.waitTillReady(); diff --git a/x-pack/plugins/event_log/server/es/context.ts b/x-pack/plugins/event_log/server/es/context.ts index d7f67620e7968..c1777d6979c5c 100644 --- a/x-pack/plugins/event_log/server/es/context.ts +++ b/x-pack/plugins/event_log/server/es/context.ts @@ -36,6 +36,7 @@ export interface EsContextCtorParams { logger: Logger; clusterClientPromise: Promise; indexNameRoot: string; + kibanaVersion: string; } class EsContextImpl implements EsContext { @@ -47,7 +48,7 @@ class EsContextImpl implements EsContext { constructor(params: EsContextCtorParams) { this.logger = params.logger; - this.esNames = getEsNames(params.indexNameRoot); + this.esNames = getEsNames(params.indexNameRoot, params.kibanaVersion); this.readySignal = createReadySignal(); this.initialized = false; this.esAdapter = new ClusterClientAdapter({ diff --git a/x-pack/plugins/event_log/server/es/documents.test.ts b/x-pack/plugins/event_log/server/es/documents.test.ts index 2feb6e3b84f91..df0689ba0b63c 100644 --- a/x-pack/plugins/event_log/server/es/documents.test.ts +++ b/x-pack/plugins/event_log/server/es/documents.test.ts @@ -18,7 +18,8 @@ describe('getIlmPolicy()', () => { }); describe('getIndexTemplate()', () => { - const esNames = getEsNames('XYZ'); + const kibanaVersion = '1.2.3'; + const esNames = getEsNames('XYZ', kibanaVersion); test('returns the correct details of the index template', () => { const indexTemplate = getIndexTemplate(esNames); diff --git a/x-pack/plugins/event_log/server/es/names.test.ts b/x-pack/plugins/event_log/server/es/names.test.ts index bc6a4c9a52fac..b77ab30c754b7 100644 --- a/x-pack/plugins/event_log/server/es/names.test.ts +++ b/x-pack/plugins/event_log/server/es/names.test.ts @@ -13,20 +13,22 @@ jest.mock('../lib/../../../../package.json', () => ({ describe('getEsNames()', () => { test('works as expected', () => { const base = 'XYZ'; - const version = '1.2.3'; - const esNames = getEsNames(base); + const kibanaVersion = '1.2.3'; + const esNames = getEsNames(base, kibanaVersion); expect(esNames.base).toEqual(base); - expect(esNames.alias).toEqual(`${base}-event-log-${version}`); + expect(esNames.alias).toEqual(`${base}-event-log-${kibanaVersion}`); expect(esNames.ilmPolicy).toEqual(`${base}-event-log-policy`); expect(esNames.indexPattern).toEqual(`${base}-event-log-*`); - expect(esNames.indexPatternWithVersion).toEqual(`${base}-event-log-${version}-*`); - expect(esNames.initialIndex).toEqual(`${base}-event-log-${version}-000001`); - expect(esNames.indexTemplate).toEqual(`${base}-event-log-${version}-template`); + expect(esNames.indexPatternWithVersion).toEqual(`${base}-event-log-${kibanaVersion}-*`); + expect(esNames.initialIndex).toEqual(`${base}-event-log-${kibanaVersion}-000001`); + expect(esNames.indexTemplate).toEqual(`${base}-event-log-${kibanaVersion}-template`); }); test('ilm policy name does not contain dot prefix', () => { const base = '.XYZ'; - const esNames = getEsNames(base); + const kibanaVersion = '1.2.3'; + + const esNames = getEsNames(base, kibanaVersion); expect(esNames.ilmPolicy).toEqual('XYZ-event-log-policy'); }); }); diff --git a/x-pack/plugins/event_log/server/es/names.ts b/x-pack/plugins/event_log/server/es/names.ts index 8cd56a89d3fbe..363b67814efe7 100644 --- a/x-pack/plugins/event_log/server/es/names.ts +++ b/x-pack/plugins/event_log/server/es/names.ts @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import xPackage from '../../../../package.json'; - const EVENT_LOG_NAME_SUFFIX = `-event-log`; -const EVENT_LOG_VERSION_SUFFIX = `-${xPackage.version}`; export interface EsNames { base: string; @@ -19,7 +16,8 @@ export interface EsNames { indexTemplate: string; } -export function getEsNames(baseName: string): EsNames { +export function getEsNames(baseName: string, kibanaVersion: string): EsNames { + const EVENT_LOG_VERSION_SUFFIX = `-${kibanaVersion.toLocaleLowerCase()}`; const eventLogName = `${baseName}${EVENT_LOG_NAME_SUFFIX}`; const eventLogNameWithVersion = `${eventLogName}${EVENT_LOG_VERSION_SUFFIX}`; const eventLogPolicyName = `${ diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index 6471db7d5dd69..03125f3005c3d 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -55,12 +55,14 @@ export class Plugin implements CorePlugin; private eventLogClientService?: EventLogClientService; private savedObjectProviderRegistry: SavedObjectProviderRegistry; + private kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; constructor(private readonly context: PluginInitializerContext) { this.systemLogger = this.context.logger.get(); this.config$ = this.context.config.create(); this.globalConfig$ = this.context.config.legacy.globalConfig$; this.savedObjectProviderRegistry = new SavedObjectProviderRegistry(); + this.kibanaVersion = this.context.env.packageInfo.version; } async setup(core: CoreSetup): Promise { @@ -78,6 +80,7 @@ export class Plugin implements CorePlugin elasticsearch.legacy.client), + kibanaVersion: this.kibanaVersion, }); this.eventLogService = new EventLogService({ diff --git a/x-pack/plugins/event_log/tsconfig.json b/x-pack/plugins/event_log/tsconfig.json new file mode 100644 index 0000000000000..9b7cde10da3d6 --- /dev/null +++ b/x-pack/plugins/event_log/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "server/**/*", + "scripts/**/*", + "generated/*", + // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 + "generated/*.json", + "common/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" } + ] +} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index eac9d92884028..d977730181e89 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -42,6 +42,7 @@ { "path": "../plugins/global_search_providers/tsconfig.json" }, { "path": "../plugins/features/tsconfig.json" }, { "path": "../plugins/embeddable_enhanced/tsconfig.json" }, + { "path": "../plugins/event_log/tsconfig.json"}, { "path": "../plugins/licensing/tsconfig.json" }, { "path": "../plugins/task_manager/tsconfig.json" }, { "path": "../plugins/telemetry_collection_xpack/tsconfig.json" }, diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index 01d9bcf9537ec..56444ed80bc90 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -13,6 +13,7 @@ "plugins/graph/**/*", "plugins/features/**/*", "plugins/embeddable_enhanced/**/*", + "plugins/event_log/**/*", "plugins/licensing/**/*", "plugins/searchprofiler/**/*", "plugins/security_solution/cypress/**/*", @@ -73,6 +74,7 @@ { "path": "./plugins/features/tsconfig.json" }, { "path": "./plugins/graph/tsconfig.json" }, { "path": "./plugins/embeddable_enhanced/tsconfig.json" }, + { "path": "./plugins/event_log/tsconfig.json"}, { "path": "./plugins/licensing/tsconfig.json" }, { "path": "./plugins/searchprofiler/tsconfig.json" }, { "path": "./plugins/task_manager/tsconfig.json" }, diff --git a/x-pack/tsconfig.refs.json b/x-pack/tsconfig.refs.json index 4352d2993002a..a0a9eda0aaf23 100644 --- a/x-pack/tsconfig.refs.json +++ b/x-pack/tsconfig.refs.json @@ -8,6 +8,7 @@ { "path": "./plugins/data_enhanced/tsconfig.json" }, { "path": "./plugins/global_search/tsconfig.json" }, { "path": "./plugins/global_search_providers/tsconfig.json" }, + { "path": "./plugins/event_log/tsconfig.json"}, { "path": "./plugins/features/tsconfig.json" }, { "path": "./plugins/graph/tsconfig.json" }, { "path": "./plugins/embeddable_enhanced/tsconfig.json" }, From 273ad4e50517bac13a36bab0768b57d11e3b822a Mon Sep 17 00:00:00 2001 From: Michail Yasonik Date: Fri, 15 Jan 2021 22:31:44 -0500 Subject: [PATCH 34/38] removing kibana-core-ui from codeowners (#88111) --- .github/CODEOWNERS | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5c768dccaf274..b47d78ea6d691 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -98,18 +98,6 @@ #CC# /x-pack/legacy/plugins/canvas/ @elastic/kibana-presentation #CC# /x-pack/plugins/dashboard_mode @elastic/kibana-presentation -# Core UI -# Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon -/src/plugins/home/public @elastic/kibana-core-ui -/src/plugins/home/server/*.ts @elastic/kibana-core-ui -/src/plugins/home/server/services/ @elastic/kibana-core-ui -/src/plugins/kibana_overview/ @elastic/kibana-core-ui -/x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui -#CC# /src/plugins/newsfeed @elastic/kibana-core-ui -#CC# /src/plugins/home/public @elastic/kibana-core-ui -#CC# /src/plugins/home/server/services/ @elastic/kibana-core-ui -#CC# /src/plugins/home/ @elastic/kibana-core-ui -#CC# /x-pack/plugins/global_search_providers/ @elastic/kibana-core-ui # Observability UIs /x-pack/plugins/infra/ @elastic/logs-metrics-ui @@ -138,10 +126,6 @@ /x-pack/test/functional/apps/maps/ @elastic/kibana-gis /x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis /x-pack/test/visual_regression/tests/maps/index.js @elastic/kibana-gis -/x-pack/plugins/stack_alerts/server/alert_types/geo_containment @elastic/kibana-gis -/x-pack/plugins/stack_alerts/public/alert_types/geo_containment @elastic/kibana-gis -/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold @elastic/kibana-gis -/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold @elastic/kibana-gis #CC# /src/plugins/maps_legacy/ @elastic/kibana-gis #CC# /src/plugins/maps_oss/ @elastic/kibana-gis #CC# /x-pack/plugins/file_upload @elastic/kibana-gis @@ -200,6 +184,11 @@ /src/plugins/status_page/ @elastic/kibana-core /src/plugins/saved_objects_management/ @elastic/kibana-core /src/dev/run_check_published_api_changes.ts @elastic/kibana-core +/src/plugins/home/public @elastic/kibana-core +/src/plugins/home/server/*.ts @elastic/kibana-core +/src/plugins/home/server/services/ @elastic/kibana-core +/src/plugins/kibana_overview/ @elastic/kibana-core +/x-pack/plugins/global_search_bar/ @elastic/kibana-core #CC# /src/core/server/csp/ @elastic/kibana-core #CC# /src/legacy/server/config/ @elastic/kibana-core #CC# /src/legacy/server/http/ @elastic/kibana-core @@ -210,6 +199,11 @@ #CC# /x-pack/plugins/cloud/ @elastic/kibana-core #CC# /x-pack/plugins/features/ @elastic/kibana-core #CC# /x-pack/plugins/global_search/ @elastic/kibana-core +#CC# /src/plugins/newsfeed @elastic/kibana-core +#CC# /src/plugins/home/public @elastic/kibana-core +#CC# /src/plugins/home/server/services/ @elastic/kibana-core +#CC# /src/plugins/home/ @elastic/kibana-core +#CC# /x-pack/plugins/global_search_providers/ @elastic/kibana-core # Kibana Telemetry /packages/kbn-analytics/ @elastic/kibana-core @@ -236,7 +230,6 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib # Security /src/core/server/csp/ @elastic/kibana-security @elastic/kibana-core /src/plugins/security_oss/ @elastic/kibana-security -/src/plugins/spaces_oss/ @elastic/kibana-security /test/security_functional/ @elastic/kibana-security /x-pack/plugins/spaces/ @elastic/kibana-security /x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security @@ -263,7 +256,7 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib /x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services /docs/user/alerting/ @elastic/kibana-alerting-services /docs/management/alerting/ @elastic/kibana-alerting-services -#CC# /x-pack/plugins/stack_alerts/ @elastic/kibana-alerting-services +#CC# /x-pack/plugins/stack_alerts @elastic/kibana-alerting-services # Enterprise Search # Shared From abffb198899a61d6b05dcca3137bafce9a633e57 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Fri, 15 Jan 2021 22:05:14 -0700 Subject: [PATCH 35/38] [Test] Add tag cloud visualization to dashboard in functional test for reporting (#87600) * add tag cloud to dashboard in kibana archive * add pdf snapshot testing to downloaded pdfs * update png baseline image * remove pdf snapshot testing * rename mislabeled variable * fix test comparison * remove unnecessary pdf utf8 checks * Update screenshots.ts Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../baseline/dashboard_preserve_layout.png | Bin 631719 -> 754042 bytes .../apps/dashboard/reporting/screenshots.ts | 28 +- .../functional/apps/visualize/reporting.ts | 2 +- .../reporting/ecommerce_kibana/data.json.gz | Bin 4219 -> 4526 bytes .../reporting/ecommerce_kibana/mappings.json | 3024 ++++++++--------- 5 files changed, 1371 insertions(+), 1683 deletions(-) diff --git a/x-pack/test/functional/apps/dashboard/reporting/reports/baseline/dashboard_preserve_layout.png b/x-pack/test/functional/apps/dashboard/reporting/reports/baseline/dashboard_preserve_layout.png index 1eb5f29d212c230c984be34e4aec189dbd2aa751..1832f3dc03d5291ec40cd10e827a1ebf31102584 100644 GIT binary patch literal 754042 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 z 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 -7SmLc--NveKN(5nSC@Kgo&T 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
M;81<5FG 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