From ba6f9eb6a123f6ed653221aff98f187379891b6f Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Thu, 22 Dec 2022 11:12:35 -0800 Subject: [PATCH 01/37] Add a new `vis_augmenter` plugin (#3107) This PR only sets up the boilerplate code for the new plugin. The additions of the new saved object type and expression function types will be added in later PRs. Breaking it up for readability purposes. Signed-off-by: Tyler Ohlsen --- src/plugins/vis_augmenter/README.md | 1 + .../vis_augmenter/opensearch_dashboards.json | 7 ++++ src/plugins/vis_augmenter/public/index.ts | 12 ++++++ src/plugins/vis_augmenter/public/plugin.ts | 40 +++++++++++++++++++ src/plugins/vis_augmenter/server/index.ts | 12 ++++++ src/plugins/vis_augmenter/server/plugin.ts | 38 ++++++++++++++++++ 6 files changed, 110 insertions(+) create mode 100644 src/plugins/vis_augmenter/README.md create mode 100644 src/plugins/vis_augmenter/opensearch_dashboards.json create mode 100644 src/plugins/vis_augmenter/public/index.ts create mode 100644 src/plugins/vis_augmenter/public/plugin.ts create mode 100644 src/plugins/vis_augmenter/server/index.ts create mode 100644 src/plugins/vis_augmenter/server/plugin.ts diff --git a/src/plugins/vis_augmenter/README.md b/src/plugins/vis_augmenter/README.md new file mode 100644 index 000000000000..fb9d4fbdcaae --- /dev/null +++ b/src/plugins/vis_augmenter/README.md @@ -0,0 +1 @@ +Contains interfaces and type definitions used for allowing external plugins to augment Visualizations. Registers the relevant saved object types and expression functions. diff --git a/src/plugins/vis_augmenter/opensearch_dashboards.json b/src/plugins/vis_augmenter/opensearch_dashboards.json new file mode 100644 index 000000000000..e9cd25618d01 --- /dev/null +++ b/src/plugins/vis_augmenter/opensearch_dashboards.json @@ -0,0 +1,7 @@ +{ + "id": "visAugmenter", + "version": "opensearchDashboards", + "server": true, + "ui": true, + "requiredPlugins": ["data", "savedObjects", "opensearchDashboardsUtils"] +} diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts new file mode 100644 index 000000000000..e931e2ac2e03 --- /dev/null +++ b/src/plugins/vis_augmenter/public/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PluginInitializerContext } from 'src/core/public'; +import { VisAugmenterPlugin, VisAugmenterSetup, VisAugmenterStart } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new VisAugmenterPlugin(initializerContext); +} +export { VisAugmenterSetup, VisAugmenterStart }; diff --git a/src/plugins/vis_augmenter/public/plugin.ts b/src/plugins/vis_augmenter/public/plugin.ts new file mode 100644 index 000000000000..cbc7a24800de --- /dev/null +++ b/src/plugins/vis_augmenter/public/plugin.ts @@ -0,0 +1,40 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisAugmenterSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisAugmenterStart {} + +export interface VisAugmenterSetupDeps { + data: DataPublicPluginSetup; +} + +export interface VisAugmenterStartDeps { + data: DataPublicPluginStart; +} + +export class VisAugmenterPlugin + implements + Plugin { + constructor(initializerContext: PluginInitializerContext) {} + + public setup( + core: CoreSetup, + { data }: VisAugmenterSetupDeps + ): VisAugmenterSetup { + return {}; + } + + public start(core: CoreStart, { data }: VisAugmenterStartDeps): VisAugmenterStart { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/vis_augmenter/server/index.ts b/src/plugins/vis_augmenter/server/index.ts new file mode 100644 index 000000000000..cc6530649a8e --- /dev/null +++ b/src/plugins/vis_augmenter/server/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PluginInitializerContext } from '../../../core/server'; +import { VisAugmenterPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new VisAugmenterPlugin(initializerContext); +} +export { VisAugmenterPluginSetup, VisAugmenterPluginStart } from './plugin'; diff --git a/src/plugins/vis_augmenter/server/plugin.ts b/src/plugins/vis_augmenter/server/plugin.ts new file mode 100644 index 000000000000..d921126a1f66 --- /dev/null +++ b/src/plugins/vis_augmenter/server/plugin.ts @@ -0,0 +1,38 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + Logger, +} from '../../../core/server'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisAugmenterPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisAugmenterPluginStart {} + +export class VisAugmenterPlugin + implements Plugin { + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('VisAugmenter: Setup'); + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('VisAugmenter: Started'); + return {}; + } + + public stop() {} +} From e78ccbfe5d75b58eea1a1651d0033bb65d6f737f Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Thu, 29 Dec 2022 16:13:12 -0800 Subject: [PATCH 02/37] Add interfaces for layering data on Visualizations (#3108) * Add vis layer interfaces Signed-off-by: Tyler Ohlsen --- src/plugins/vis_augmenter/common/index.ts | 6 ++ .../vis_augmenter/common/types.test.ts | 60 +++++++++++++++++++ src/plugins/vis_augmenter/common/types.ts | 54 +++++++++++++++++ .../vis_augmenter/opensearch_dashboards.json | 2 +- .../vis_augmenter/public/expressions/index.ts | 6 ++ .../public/expressions/vis_layers.ts | 33 ++++++++++ src/plugins/vis_augmenter/public/plugin.ts | 6 +- 7 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/plugins/vis_augmenter/common/index.ts create mode 100644 src/plugins/vis_augmenter/common/types.test.ts create mode 100644 src/plugins/vis_augmenter/common/types.ts create mode 100644 src/plugins/vis_augmenter/public/expressions/index.ts create mode 100644 src/plugins/vis_augmenter/public/expressions/vis_layers.ts diff --git a/src/plugins/vis_augmenter/common/index.ts b/src/plugins/vis_augmenter/common/index.ts new file mode 100644 index 000000000000..9f269633f307 --- /dev/null +++ b/src/plugins/vis_augmenter/common/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './types'; diff --git a/src/plugins/vis_augmenter/common/types.test.ts b/src/plugins/vis_augmenter/common/types.test.ts new file mode 100644 index 000000000000..9e1282f40062 --- /dev/null +++ b/src/plugins/vis_augmenter/common/types.test.ts @@ -0,0 +1,60 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisLayerTypes, VisLayer, isPointInTimeEventsVisLayer, isValidVisLayer } from './types'; + +describe('isPointInTimeEventsVisLayer()', function () { + it('should return false if type does not match', function () { + const visLayer = ({ + type: 'incorrect-type', + name: 'visLayerName', + field1: 'value1', + field2: 'value2', + } as unknown) as VisLayer; + expect(isPointInTimeEventsVisLayer(visLayer)).toBe(false); + }); + + it('should return true if type matches', function () { + const visLayer = { + type: VisLayerTypes.PointInTimeEvents, + name: 'testName', + events: [ + { + timestamp: 123, + resourceId: 'testId', + resourceName: 'testName', + }, + ], + } as VisLayer; + expect(isPointInTimeEventsVisLayer(visLayer)).toBe(true); + }); +}); + +describe('isValidVisLayer()', function () { + it('should return false if no valid type', function () { + const visLayer = ({ + type: 'incorrect-type', + name: 'visLayerName', + field1: 'value1', + field2: 'value2', + } as unknown) as VisLayer; + expect(isValidVisLayer(visLayer)).toBe(false); + }); + + it('should return true if type matches', function () { + const visLayer = { + type: VisLayerTypes.PointInTimeEvents, + name: 'testName', + events: [ + { + timestamp: 123, + resourceId: 'testId', + resourceName: 'testName', + }, + ], + } as VisLayer; + expect(isValidVisLayer(visLayer)).toBe(true); + }); +}); diff --git a/src/plugins/vis_augmenter/common/types.ts b/src/plugins/vis_augmenter/common/types.ts new file mode 100644 index 000000000000..ea4561991488 --- /dev/null +++ b/src/plugins/vis_augmenter/common/types.ts @@ -0,0 +1,54 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ExpressionFunctionDefinition } from '../../expressions'; + +export enum VisLayerTypes { + PointInTimeEvents = 'PointInTimeEvents', +} + +export interface VisLayer { + type: keyof typeof VisLayerTypes; + name: string; +} + +export type VisLayers = VisLayer[]; + +// resourceId & resourceName are required so that the +// events flyout can partition data based on these attributes +// (e.g., partitioning anomalies based on the detector they came from) +export interface EventMetadata { + resourceId: string; + resourceName: string; + tooltip?: string; +} + +export interface PointInTimeEvent { + timestamp: number; + metadata: EventMetadata; +} + +export interface PointInTimeEventsVisLayer extends VisLayer { + events: PointInTimeEvent[]; +} + +export const isPointInTimeEventsVisLayer = (obj: any) => { + return obj?.type === VisLayerTypes.PointInTimeEvents; +}; + +export const isValidVisLayer = (obj: any) => { + return obj?.type in VisLayerTypes; +}; + +export interface VisLayerResponseValue { + visLayers: object; +} + +export type VisLayerFunctionDefinition = ExpressionFunctionDefinition< + string, + VisLayerResponseValue, + any, + Promise +>; diff --git a/src/plugins/vis_augmenter/opensearch_dashboards.json b/src/plugins/vis_augmenter/opensearch_dashboards.json index e9cd25618d01..ac78ec9c0281 100644 --- a/src/plugins/vis_augmenter/opensearch_dashboards.json +++ b/src/plugins/vis_augmenter/opensearch_dashboards.json @@ -3,5 +3,5 @@ "version": "opensearchDashboards", "server": true, "ui": true, - "requiredPlugins": ["data", "savedObjects", "opensearchDashboardsUtils"] + "requiredPlugins": ["data", "savedObjects", "opensearchDashboardsUtils", "expressions"] } diff --git a/src/plugins/vis_augmenter/public/expressions/index.ts b/src/plugins/vis_augmenter/public/expressions/index.ts new file mode 100644 index 000000000000..f7bcfbd083fe --- /dev/null +++ b/src/plugins/vis_augmenter/public/expressions/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './vis_layers'; diff --git a/src/plugins/vis_augmenter/public/expressions/vis_layers.ts b/src/plugins/vis_augmenter/public/expressions/vis_layers.ts new file mode 100644 index 000000000000..f99ead0407eb --- /dev/null +++ b/src/plugins/vis_augmenter/public/expressions/vis_layers.ts @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ExpressionTypeDefinition } from '../../../expressions'; +import { VisLayers } from '../../common'; + +const name = 'vis_layers'; + +export interface ExprVisLayers { + type: typeof name; + layers: VisLayers; +} + +// Setting default empty arrays for null & undefined edge cases +export const visLayers: ExpressionTypeDefinition = { + name, + from: { + null: () => { + return { + type: name, + layers: [] as VisLayers, + } as ExprVisLayers; + }, + undefined: () => { + return { + type: name, + layers: [] as VisLayers, + } as ExprVisLayers; + }, + }, +}; diff --git a/src/plugins/vis_augmenter/public/plugin.ts b/src/plugins/vis_augmenter/public/plugin.ts index cbc7a24800de..d53116bdd12d 100644 --- a/src/plugins/vis_augmenter/public/plugin.ts +++ b/src/plugins/vis_augmenter/public/plugin.ts @@ -3,8 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { ExpressionsSetup } from '../../expressions/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; +import { visLayers } from './expressions'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface VisAugmenterSetup {} @@ -14,6 +16,7 @@ export interface VisAugmenterStart {} export interface VisAugmenterSetupDeps { data: DataPublicPluginSetup; + expressions: ExpressionsSetup; } export interface VisAugmenterStartDeps { @@ -27,8 +30,9 @@ export class VisAugmenterPlugin public setup( core: CoreSetup, - { data }: VisAugmenterSetupDeps + { data, expressions }: VisAugmenterSetupDeps ): VisAugmenterSetup { + expressions.registerType(visLayers); return {}; } From 2b82eddf69cf2f06b99bc274459f754ab1f0533e Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Wed, 1 Feb 2023 13:35:41 -0800 Subject: [PATCH 03/37] Rebase feature/feature-anywhere with main (#3364) Rebase feature branch with main --- .github/workflows/add_untriaged_label.yml | 19 + .github/workflows/build_and_test_workflow.yml | 311 +- .github/workflows/changelog_verifier.yml | 1 + .gitignore | 1 + .node-version | 2 +- .nvmrc | 2 +- CHANGELOG.md | 47 +- DEVELOPER_GUIDE.md | 5 + Dockerfile | 4 +- MAINTAINERS.md | 44 +- NOTICE.txt | 124 +- TESTING.md | 2 +- config/opensearch_dashboards.yml | 5 + dev-tools/get-version.sh | 15 - docs/plugins/data_persistence.md | 235 ++ docs/plugins/img/URL_example.png | Bin 0 -> 125722 bytes docs/plugins/img/app_filter.png | Bin 0 -> 23776 bytes docs/plugins/img/app_persistence.png | Bin 0 -> 257130 bytes docs/plugins/img/app_query.png | Bin 0 -> 6135 bytes docs/plugins/img/global_filter.png | Bin 0 -> 103758 bytes docs/plugins/img/global_persistence.png | Bin 0 -> 405417 bytes docs/plugins/img/initialization.png | Bin 0 -> 439631 bytes docs/plugins/img/navigate.png | Bin 0 -> 894434 bytes docs/plugins/img/refresh.png | Bin 0 -> 232464 bytes docs/plugins/img/refresh_interval.png | Bin 0 -> 24813 bytes docs/plugins/img/storage_instance.png | Bin 0 -> 57035 bytes docs/plugins/img/time_range.png | Bin 0 -> 137963 bytes docs/plugins/img/visualization.png | Bin 0 -> 1159643 bytes package.json | 31 +- packages/osd-babel-preset/common_preset.js | 16 +- packages/osd-babel-preset/package.json | 1 + .../rules/no_restricted_paths.js | 2 +- .../rules/no_restricted_paths.test.js | 16 + packages/osd-opensearch/package.json | 2 +- .../src/integration_tests/cluster.test.js | 9 +- packages/osd-optimizer/package.json | 2 +- .../osd-optimizer/src/worker/run_worker.ts | 6 +- .../src/worker/webpack.config.ts | 4 +- packages/osd-ui-shared-deps/package.json | 2 +- ...ensearch-dashboards.release-notes-1.3.7.md | 49 + ...ensearch-dashboards.release-notes-2.4.1.md | 9 + ...ensearch-dashboards.release-notes-2.5.0.md | 70 + scripts/upgrade_chromedriver.js | 122 + .../public/doc_links/doc_links_service.ts | 6 +- .../opensearch/client/client_config.test.ts | 18 + .../server/opensearch/client/client_config.ts | 5 + .../opensearch/opensearch_config.test.ts | 1 + .../server/opensearch/opensearch_config.ts | 8 + .../ui_settings/ui_settings_client.test.ts | 23 + .../server/ui_settings/ui_settings_config.ts | 7 +- src/dev/build/lib/scan_copy.ts | 8 +- src/dev/build/tasks/notice_file_task.ts | 2 +- .../bin/opensearch-dashboards-docker | 1 + .../tasks/patch_native_modules_task.test.ts | 4 +- .../build/tasks/patch_native_modules_task.ts | 29 +- src/dev/ci_setup/setup_env.sh | 23 +- src/dev/jest/config.js | 1 + src/dev/jest/junit_reporter.js | 5 +- src/dev/notice/cli.js | 2 +- src/dev/notice/generate_notice_from_source.ts | 34 +- .../application/contexts/services_context.tsx | 3 +- .../send_request_to_opensearch.test.ts | 182 + .../send_request_to_opensearch.ts | 157 +- .../use_send_current_request_to_opensearch.ts | 6 +- .../console/public/application/index.tsx | 1 + .../public/lib/opensearch/opensearch.ts | 45 +- .../data/common/field_formats/field_format.ts | 6 + .../common/search/aggs/agg_configs.test.ts | 52 +- .../data/common/search/aggs/agg_configs.ts | 10 +- src/plugins/data/public/index.ts | 1 + src/plugins/data/public/mocks.ts | 6 + src/plugins/data/public/public.api.md | 14 + .../data/public/query/state_sync/README.md | 23 + .../state_sync/connect_to_query_state.test.ts | 123 +- .../state_sync/connect_to_query_state.ts | 103 +- .../data/public/query/state_sync/index.ts | 2 +- .../server/client/client_config.test.ts | 2 +- .../data_source/server/client/client_pool.ts | 2 +- .../server/client/configure_client.ts | 54 +- .../data_source/server/client/index.ts | 8 +- .../data_source/server/data_source_service.ts | 33 +- .../server/legacy/client_config.test.ts | 2 +- .../server/legacy/configure_legacy_client.ts | 8 +- src/plugins/data_source/server/lib/error.ts | 13 +- src/plugins/data_source/server/plugin.ts | 5 + .../data_source_connection_validator.ts | 19 + .../server/routes/test_connection.ts | 79 + src/plugins/data_source/server/types.ts | 3 +- .../create_data_source_wizard.test.tsx.snap | 1941 --------- .../create_data_source_form.test.tsx.snap | 3611 ----------------- .../create_data_source_form.test.tsx | 13 +- .../create_form/create_data_source_form.tsx | 73 +- .../create_data_source_wizard.test.tsx | 28 +- .../create_data_source_wizard.tsx | 42 +- .../edit_data_source.test.tsx.snap | 1228 ------ .../edit_data_source_form.test.tsx.snap | 2182 ---------- .../edit_form/edit_data_source_form.test.tsx | 13 +- .../edit_form/edit_data_source_form.tsx | 49 +- .../header/__snapshots__/header.test.tsx.snap | 175 - .../components/header/header.test.tsx | 6 +- .../components/header/header.tsx | 35 +- .../edit_data_source.test.tsx | 10 +- .../edit_data_source/edit_data_source.tsx | 8 + .../public/components/utils.test.ts | 47 + .../public/components/utils.ts | 25 +- src/plugins/embeddable/public/lib/errors.ts | 2 +- .../opensearch_dashboards_utils/README.md | 1 + .../docs/global_data_persistence.md | 99 + .../public/region_map_visualization.js | 1 - .../vis_builder/public/application/app.tsx | 24 +- .../components/data_tab/secondary_panel.tsx | 162 +- .../utils/get_available_fields.test.ts | 80 + .../data_tab/utils/get_field_details.test.ts | 52 + .../public/application/components/top_nav.tsx | 14 +- .../application/components/workspace.tsx | 21 +- .../application/utils/get_top_nav_config.tsx | 16 +- .../public/application/utils/mocks.ts | 19 +- .../state_management/handlers/editor_state.ts | 24 + .../state_management/handlers/parent_aggs.ts | 67 + .../utils/state_management/metadata_slice.ts | 27 +- .../redux_persistence.test.tsx | 53 + .../state_management/redux_persistence.ts | 38 + .../utils/state_management/store.ts | 48 +- .../state_management/visualization_slice.ts | 18 + .../utils/use/use_saved_vis_builder_vis.ts | 17 +- .../application/utils/validations/index.ts | 8 + .../application/utils/validations/types.ts | 9 + .../validations/validate_aggregations.test.ts | 99 + .../validations/validate_aggregations.ts | 54 + .../validations/validate_schema_state.test.ts | 59 + .../validate_schema_state.ts | 24 +- .../vis_builder_state_validation.test.ts | 10 +- .../vis_builder_state_validation.ts | 23 + .../utils/vis_builder_state_validation.ts | 19 - .../embeddable/vis_builder_embeddable.tsx | 4 +- src/plugins/vis_builder/public/plugin.test.ts | 1 + src/plugins/vis_builder/public/plugin.ts | 90 +- .../vis_builder/public/plugin_services.ts | 4 + src/plugins/vis_builder/public/types.ts | 6 + .../table/components/table_viz_options.tsx | 4 +- .../visualizations/table/to_expression.ts | 4 +- .../components/controls/date_ranges.tsx | 2 +- src/plugins/vis_type_table/README.md | 36 +- .../vis_type_table/opensearch_dashboards.json | 4 +- .../__snapshots__/table_vis_fn.test.ts.snap | 5 +- .../public/__snapshots__/to_ast.test.ts.snap | 115 + .../vis_type_table/public/_table_vis.scss | 23 - .../public/agg_table/_agg_table.scss | 42 - .../public/agg_table/_index.scss | 1 - .../public/agg_table/agg_table.html | 34 - .../public/agg_table/agg_table.js | 295 -- .../public/agg_table/agg_table.test.js | 512 --- .../public/agg_table/agg_table_group.html | 77 - .../public/agg_table/agg_table_group.js | 67 - .../public/agg_table/agg_table_group.test.js | 152 - .../public/agg_table/tabified_data.js | 806 ---- .../public/components/table_vis_app.scss | 0 .../public/components/table_vis_app.tsx | 4 +- .../public/components/table_vis_component.tsx | 28 +- .../components/table_vis_component_group.tsx | 0 .../public/components/table_vis_control.tsx | 2 +- .../components/table_vis_grid_columns.tsx | 148 + .../public/components/table_vis_options.tsx | 4 +- .../public/get_inner_angular.ts | 117 - src/plugins/vis_type_table/public/index.scss | 10 - src/plugins/vis_type_table/public/index.ts | 31 +- .../public/paginated_table/_index.scss | 1 - .../paginated_table/_table_cell_filter.scss | 30 - .../paginated_table/paginated_table.html | 55 - .../public/paginated_table/paginated_table.js | 120 - .../paginated_table/paginated_table.test.ts | 485 --- .../public/paginated_table/rows.js | 149 - .../paginated_table/table_cell_filter.html | 23 - src/plugins/vis_type_table/public/plugin.ts | 76 +- src/plugins/vis_type_table/public/services.ts | 32 +- .../vis_type_table/public/table_vis.html | 29 - .../public/table_vis_controller.js | 67 - .../public/table_vis_controller.test.ts | 272 -- .../public/table_vis_fn.test.ts | 2 +- .../vis_type_table/public/table_vis_fn.ts | 47 +- .../public/table_vis_legacy_module.ts | 52 - .../public/table_vis_renderer.tsx | 0 .../public/table_vis_response_handler.ts | 69 +- .../vis_type_table/public/table_vis_type.ts | 143 +- .../vis_type_table/public/to_ast.test.ts | 58 + src/plugins/vis_type_table/public/to_ast.ts | 65 + src/plugins/vis_type_table/public/types.ts | 43 +- .../public/utils/convert_to_csv_data.ts | 0 .../public/utils/convert_to_formatted_data.ts | 179 + .../public/utils/index.ts | 0 .../public/utils/use_pagination.ts | 0 .../vis_type_table/public/vis_controller.ts | 135 - src/plugins/vis_type_table_new/README.md | 1 - .../opensearch_dashboards.json | 16 - .../components/table_vis_grid_columns.tsx | 148 - .../vis_type_table_new/public/index.ts | 13 - .../vis_type_table_new/public/plugin.ts | 36 - .../vis_type_table_new/public/services.ts | 11 - .../vis_type_table_new/public/table_vis_fn.ts | 65 - .../public/table_vis_response_handler.ts | 112 - .../vis_type_table_new/public/types.ts | 70 - .../public/utils/convert_to_formatted_data.ts | 68 - .../public/data_model/vega_parser.test.js | 8 +- .../vis_type_vega/public/default.spec.hjson | 64 +- src/plugins/vis_type_vega/public/lib/vega.js | 5 +- .../public/test_utils/default.spec.json | 2 +- .../public/test_utils/vegalite_graph.json | 2 +- .../components/inspector_data_grid.tsx | 12 +- .../public/vega_view/vega_map_view.js | 3 +- .../public/vega_visualization.test.js | 2 +- .../get_usage_collector.test.ts | 2 +- .../__snapshots__/build_pipeline.test.ts.snap | 10 - .../public/legacy/build_pipeline.test.ts | 78 - .../public/legacy/build_pipeline.ts | 27 - tasks/config/run.js | 132 +- test/functional/apps/console/_console.ts | 2 +- .../apps/dashboard/dashboard_filtering.js | 12 - .../apps/dashboard/dashboard_grid.js | 2 +- .../apps/dashboard/embeddable_rendering.js | 6 +- .../apps/dashboard/url_field_formatter.ts | 8 - test/functional/apps/visualize/_data_table.js | 485 --- .../visualize/_data_table_nontimeindex.js | 170 - .../_data_table_notimeindex_filters.ts | 101 - .../apps/visualize/_embedding_chart.js | 187 - .../visualize/_histogram_request_start.js | 95 - .../apps/visualize/_linked_saved_searches.ts | 138 - test/functional/apps/visualize/index.ts | 6 - .../opensearch_dashboards/data.json.gz | Bin 21378 -> 20707 bytes .../functional/page_objects/dashboard_page.ts | 1 - .../page_objects/visualize_chart_page.ts | 72 - .../functional/page_objects/visualize_page.ts | 4 - .../services/dashboard/expectations.ts | 11 - test/functional/services/index.ts | 2 - test/functional/services/table.ts | 73 - yarn.lock | 1316 ++---- 235 files changed, 4578 insertions(+), 16631 deletions(-) create mode 100644 .github/workflows/add_untriaged_label.yml delete mode 100755 dev-tools/get-version.sh create mode 100644 docs/plugins/data_persistence.md create mode 100644 docs/plugins/img/URL_example.png create mode 100644 docs/plugins/img/app_filter.png create mode 100644 docs/plugins/img/app_persistence.png create mode 100644 docs/plugins/img/app_query.png create mode 100644 docs/plugins/img/global_filter.png create mode 100644 docs/plugins/img/global_persistence.png create mode 100644 docs/plugins/img/initialization.png create mode 100644 docs/plugins/img/navigate.png create mode 100644 docs/plugins/img/refresh.png create mode 100644 docs/plugins/img/refresh_interval.png create mode 100644 docs/plugins/img/storage_instance.png create mode 100644 docs/plugins/img/time_range.png create mode 100644 docs/plugins/img/visualization.png create mode 100644 release-notes/opensearch-dashboards.release-notes-1.3.7.md create mode 100644 release-notes/opensearch-dashboards.release-notes-2.4.1.md create mode 100644 release-notes/opensearch-dashboards.release-notes-2.5.0.md create mode 100644 scripts/upgrade_chromedriver.js create mode 100644 src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts create mode 100644 src/plugins/data_source/server/routes/data_source_connection_validator.ts create mode 100644 src/plugins/data_source/server/routes/test_connection.ts delete mode 100644 src/plugins/data_source_management/public/components/create_data_source_wizard/__snapshots__/create_data_source_wizard.test.tsx.snap delete mode 100644 src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/__snapshots__/create_data_source_form.test.tsx.snap delete mode 100644 src/plugins/data_source_management/public/components/edit_data_source/__snapshots__/edit_data_source.test.tsx.snap delete mode 100644 src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/__snapshots__/edit_data_source_form.test.tsx.snap delete mode 100644 src/plugins/data_source_management/public/components/edit_data_source/components/header/__snapshots__/header.test.tsx.snap create mode 100644 src/plugins/opensearch_dashboards_utils/docs/global_data_persistence.md create mode 100644 src/plugins/vis_builder/public/application/components/data_tab/utils/get_available_fields.test.ts create mode 100644 src/plugins/vis_builder/public/application/components/data_tab/utils/get_field_details.test.ts create mode 100644 src/plugins/vis_builder/public/application/utils/state_management/handlers/editor_state.ts create mode 100644 src/plugins/vis_builder/public/application/utils/state_management/handlers/parent_aggs.ts create mode 100644 src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx create mode 100644 src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts create mode 100644 src/plugins/vis_builder/public/application/utils/validations/index.ts create mode 100644 src/plugins/vis_builder/public/application/utils/validations/types.ts create mode 100644 src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.test.ts create mode 100644 src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.ts create mode 100644 src/plugins/vis_builder/public/application/utils/validations/validate_schema_state.test.ts rename src/plugins/vis_builder/public/application/utils/{ => validations}/validate_schema_state.ts (65%) rename src/plugins/vis_builder/public/application/utils/{ => validations}/vis_builder_state_validation.test.ts (83%) create mode 100644 src/plugins/vis_builder/public/application/utils/validations/vis_builder_state_validation.ts delete mode 100644 src/plugins/vis_builder/public/application/utils/vis_builder_state_validation.ts create mode 100644 src/plugins/vis_type_table/public/__snapshots__/to_ast.test.ts.snap delete mode 100644 src/plugins/vis_type_table/public/_table_vis.scss delete mode 100644 src/plugins/vis_type_table/public/agg_table/_agg_table.scss delete mode 100644 src/plugins/vis_type_table/public/agg_table/_index.scss delete mode 100644 src/plugins/vis_type_table/public/agg_table/agg_table.html delete mode 100644 src/plugins/vis_type_table/public/agg_table/agg_table.js delete mode 100644 src/plugins/vis_type_table/public/agg_table/agg_table.test.js delete mode 100644 src/plugins/vis_type_table/public/agg_table/agg_table_group.html delete mode 100644 src/plugins/vis_type_table/public/agg_table/agg_table_group.js delete mode 100644 src/plugins/vis_type_table/public/agg_table/agg_table_group.test.js delete mode 100644 src/plugins/vis_type_table/public/agg_table/tabified_data.js rename src/plugins/{vis_type_table_new => vis_type_table}/public/components/table_vis_app.scss (100%) rename src/plugins/{vis_type_table_new => vis_type_table}/public/components/table_vis_app.tsx (94%) rename src/plugins/{vis_type_table_new => vis_type_table}/public/components/table_vis_component.tsx (79%) rename src/plugins/{vis_type_table_new => vis_type_table}/public/components/table_vis_component_group.tsx (100%) rename src/plugins/{vis_type_table_new => vis_type_table}/public/components/table_vis_control.tsx (93%) create mode 100644 src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx delete mode 100644 src/plugins/vis_type_table/public/get_inner_angular.ts delete mode 100644 src/plugins/vis_type_table/public/index.scss delete mode 100644 src/plugins/vis_type_table/public/paginated_table/_index.scss delete mode 100644 src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss delete mode 100644 src/plugins/vis_type_table/public/paginated_table/paginated_table.html delete mode 100644 src/plugins/vis_type_table/public/paginated_table/paginated_table.js delete mode 100644 src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts delete mode 100644 src/plugins/vis_type_table/public/paginated_table/rows.js delete mode 100644 src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html delete mode 100644 src/plugins/vis_type_table/public/table_vis.html delete mode 100644 src/plugins/vis_type_table/public/table_vis_controller.js delete mode 100644 src/plugins/vis_type_table/public/table_vis_controller.test.ts delete mode 100644 src/plugins/vis_type_table/public/table_vis_legacy_module.ts rename src/plugins/{vis_type_table_new => vis_type_table}/public/table_vis_renderer.tsx (100%) create mode 100644 src/plugins/vis_type_table/public/to_ast.test.ts create mode 100644 src/plugins/vis_type_table/public/to_ast.ts rename src/plugins/{vis_type_table_new => vis_type_table}/public/utils/convert_to_csv_data.ts (100%) create mode 100644 src/plugins/vis_type_table/public/utils/convert_to_formatted_data.ts rename src/plugins/{vis_type_table_new => vis_type_table}/public/utils/index.ts (100%) rename src/plugins/{vis_type_table_new => vis_type_table}/public/utils/use_pagination.ts (100%) delete mode 100644 src/plugins/vis_type_table/public/vis_controller.ts delete mode 100644 src/plugins/vis_type_table_new/README.md delete mode 100644 src/plugins/vis_type_table_new/opensearch_dashboards.json delete mode 100644 src/plugins/vis_type_table_new/public/components/table_vis_grid_columns.tsx delete mode 100644 src/plugins/vis_type_table_new/public/index.ts delete mode 100644 src/plugins/vis_type_table_new/public/plugin.ts delete mode 100644 src/plugins/vis_type_table_new/public/services.ts delete mode 100644 src/plugins/vis_type_table_new/public/table_vis_fn.ts delete mode 100644 src/plugins/vis_type_table_new/public/table_vis_response_handler.ts delete mode 100644 src/plugins/vis_type_table_new/public/types.ts delete mode 100644 src/plugins/vis_type_table_new/public/utils/convert_to_formatted_data.ts delete mode 100644 test/functional/apps/visualize/_data_table.js delete mode 100644 test/functional/apps/visualize/_data_table_nontimeindex.js delete mode 100644 test/functional/apps/visualize/_data_table_notimeindex_filters.ts delete mode 100644 test/functional/apps/visualize/_embedding_chart.js delete mode 100644 test/functional/apps/visualize/_histogram_request_start.js delete mode 100644 test/functional/apps/visualize/_linked_saved_searches.ts delete mode 100644 test/functional/services/table.ts diff --git a/.github/workflows/add_untriaged_label.yml b/.github/workflows/add_untriaged_label.yml new file mode 100644 index 000000000000..15b9a5565125 --- /dev/null +++ b/.github/workflows/add_untriaged_label.yml @@ -0,0 +1,19 @@ +name: Apply 'untriaged' label during issue lifecycle + +on: + issues: + types: [opened, reopened, transferred] + +jobs: + apply-label: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v6 + with: + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['untriaged'] + }) diff --git a/.github/workflows/build_and_test_workflow.yml b/.github/workflows/build_and_test_workflow.yml index 8951c5459562..f2e1f5759d52 100644 --- a/.github/workflows/build_and_test_workflow.yml +++ b/.github/workflows/build_and_test_workflow.yml @@ -9,10 +9,12 @@ on: branches: [ '**', '!backport/**' ] paths-ignore: - '**/*.md' + - 'docs/**' pull_request: branches: [ '**' ] paths-ignore: - '**/*.md' + - 'docs/**' env: TEST_BROWSER_HEADLESS: 1 @@ -25,18 +27,18 @@ env: OSD_SNAPSHOT_SKIP_VERIFY_CHECKSUM: true jobs: - build-lint-test: + build-lint-test-linux: runs-on: ubuntu-latest container: image: docker://opensearchstaging/ci-runner:ci-runner-rockylinux8-opensearch-dashboards-integtest-v2 options: --user 1001 - name: Build and Verify + name: Build and Verify on Linux steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version-file: '.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -45,6 +47,18 @@ jobs: run: | npm uninstall -g yarn npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache + run: echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $GITHUB_ENV + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- - name: Run bootstrap run: yarn osd bootstrap @@ -53,6 +67,82 @@ jobs: id: linter run: yarn lint + - name: Validate NOTICE file + id: notice-validate + run: yarn notice:validate + + - name: Run unit tests with coverage + id: unit-tests + run: yarn test:jest:ci:coverage + + - name: Run mocha tests with coverage + id: mocha-tests + run: yarn test:mocha:coverage + + - name: Upload Code Coverage + id: upload-code-coverage + uses: codecov/codecov-action@v3 + with: + directory: ./target/opensearch-dashboards-coverage + flags: Linux + + - name: Run integration tests + id: integration-tests + run: yarn test:jest_integration:ci + + build-lint-test-windows: + runs-on: windows-latest + name: Build and Verify on Windows + steps: + - name: Configure git's autocrlf + run: | + git config --global core.autocrlf false + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup JDK + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + registry-url: 'https://registry.npmjs.org' + + - name: Setup Yarn + run: | + npm uninstall -g yarn + npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache + run: | + echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $env:GITHUB_ENV + echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%" + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- + + - name: Run bootstrap + run: yarn osd bootstrap || yarn osd bootstrap + + - name: Run linter + id: linter + run: yarn lint + + - name: Validate NOTICE file + id: notice-validate + run: yarn notice:validate + - name: Run unit tests with coverage id: unit-tests run: yarn test:jest:ci:coverage @@ -66,17 +156,18 @@ jobs: uses: codecov/codecov-action@v3 with: directory: ./target/opensearch-dashboards-coverage + flags: Windows - name: Run integration tests id: integration-tests run: yarn test:jest_integration:ci - functional-tests: + functional-tests-linux: runs-on: ubuntu-latest container: image: docker://opensearchstaging/ci-runner:ci-runner-rockylinux8-opensearch-dashboards-integtest-v2 options: --user 1001 - name: Run functional tests + name: Run functional tests on Linux strategy: matrix: group: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] @@ -84,10 +175,10 @@ jobs: - run: echo Running functional tests for ciGroup${{ matrix.group }} - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version-file: '.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -96,10 +187,21 @@ jobs: run: | npm uninstall -g yarn npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache + run: echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $GITHUB_ENV + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- - # image has the latest chrome v99 - name: Setup chromedriver - run: yarn add --dev chromedriver@99.0.0 + run: node scripts/upgrade_chromedriver.js - name: Run bootstrap run: yarn osd bootstrap @@ -116,12 +218,93 @@ jobs: JOB: ci${{ matrix.group }} CACHE_DIR: ciGroup${{ matrix.group }} - build-min-artifact-tests: + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: failure-artifacts + path: | + test/*/failure_debug/ + test/*/screenshots/ + + functional-tests-windows: + runs-on: windows-latest + name: Run functional tests on Windows + strategy: + matrix: + group: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] + steps: + - run: echo Running functional tests for ciGroup${{ matrix.group }} + + - name: Configure git's autocrlf + run: | + git config --global core.autocrlf false + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup JDK + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + registry-url: 'https://registry.npmjs.org' + + - name: Setup Yarn + run: | + npm uninstall -g yarn + npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache + run: | + echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $env:GITHUB_ENV + echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%" + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- + + - name: Setup chromedriver + run: node scripts/upgrade_chromedriver.js + + - name: Run bootstrap + run: yarn osd bootstrap || yarn osd bootstrap + + - name: Build plugins + run: node scripts/build_opensearch_dashboards_platform_plugins --no-examples --workers 10 + + - name: Run CI test group ${{ matrix.group }} + id: ftr-tests + run: node scripts/functional_tests.js --config test/functional/config.js --include ciGroup${{ matrix.group }} + env: + CI_GROUP: ciGroup${{ matrix.group }} + CI_PARALLEL_PROCESS_NUMBER: ciGroup${{ matrix.group }} + JOB: ci${{ matrix.group }} + CACHE_DIR: ciGroup${{ matrix.group }} + + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: failure-artifacts-ci${{ matrix.group }} + path: | + test/*/failure_debug/ + test/*/screenshots/ + + build-min-artifact-tests-linux: runs-on: ubuntu-latest container: image: docker://opensearchstaging/ci-runner:ci-runner-rockylinux8-opensearch-dashboards-integtest-v2 options: --user 1001 - name: Build min release artifacts + name: Build min release artifacts on Linux defaults: run: working-directory: ./artifacts @@ -138,12 +321,12 @@ jobs: script: build-platform --linux-arm --skip-os-packages steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: ./artifacts - name: Setup Node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version-file: './artifacts/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -152,6 +335,18 @@ jobs: run: | npm uninstall -g yarn npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache + run: echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $GITHUB_ENV + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- - name: Get package version run: | @@ -174,8 +369,78 @@ jobs: path: ./artifacts/target/${{ env.ARTIFACT_BUILD_NAME }} retention-days: 1 + build-min-artifact-tests-windows: + runs-on: windows-latest + name: Build min release artifacts on Windows + defaults: + run: + working-directory: artifacts + strategy: + matrix: + include: + - name: Windows x64 + ext: zip + suffix: windows-x64 + script: build-platform --windows --skip-os-packages + steps: + - name: Configure git's autocrlf + run: | + git config --global core.autocrlf false + working-directory: . + + - name: Checkout code + uses: actions/checkout@v3 + with: + path: artifacts + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version-file: 'artifacts/.nvmrc' + registry-url: 'https://registry.npmjs.org' + + - name: Setup Yarn + run: | + npm uninstall -g yarn + npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache + run: | + echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $env:GITHUB_ENV + echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%" + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- + + - name: Get package version + run: | + echo "VERSION=$(yarn --silent pkg-version)" >> $env:GITHUB_ENV + + - name: Get artifact build name + run: | + echo "ARTIFACT_BUILD_NAME=opensearch-dashboards-${{ env.VERSION }}-${{ matrix.suffix }}.${{ matrix.ext }}" >> $env:GITHUB_ENV + + - name: Run bootstrap + run: yarn osd bootstrap + + - name: Build `${{ matrix.name }}` + run: yarn ${{ matrix.script }} --release + + - uses: actions/upload-artifact@v3 + if: success() + with: + name: ${{ matrix.suffix }}-${{ env.VERSION }} + path: artifacts/target/${{ env.ARTIFACT_BUILD_NAME }} + retention-days: 1 + bwc-tests: - needs: [build-min-artifact-tests] + needs: [build-min-artifact-tests-linux] runs-on: ubuntu-latest container: image: docker://opensearchstaging/ci-runner:ci-runner-rockylinux8-opensearch-dashboards-integtest-v2 @@ -189,7 +454,7 @@ jobs: version: [ osd-2.0.0, osd-2.1.0, osd-2.2.0, osd-2.3.0, osd-2.4.0 ] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: ./artifacts @@ -197,7 +462,7 @@ jobs: - run: echo [NOTE] These tests will be ran using Linux x64 release builds without security - name: Setup Node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version-file: './artifacts/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -206,6 +471,18 @@ jobs: run: | npm uninstall -g yarn npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache + run: echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $GITHUB_ENV + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- - name: Get package version run: | diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml index d8201382ba80..0890ea8b8fbb 100644 --- a/.github/workflows/changelog_verifier.yml +++ b/.github/workflows/changelog_verifier.yml @@ -1,6 +1,7 @@ name: "Changelog Verifier" on: pull_request: + branches: [ '**', '!feature/**' ] types: [opened, edited, review_requested, synchronize, reopened, ready_for_review, labeled, unlabeled] jobs: diff --git a/.gitignore b/.gitignore index 01c2aaeaf9ef..3f1759e6665a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .ackrc /.opensearch /.chromium +/package.json.bak .DS_Store .node_binaries .native_modules diff --git a/.node-version b/.node-version index a3eb5a03fa6a..285152276014 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -14.20.0 +14.20.1 diff --git a/.nvmrc b/.nvmrc index a3eb5a03fa6a..285152276014 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14.20.0 +14.20.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a208b4f1f40..c06ea5f0030c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Legacy Maps Plugin] Prevent reverse-tabnabbing ([#2540](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2540)) - Eliminate dependency on `got` versions older than 11.8.5 ([#2801](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2801)) - [Multi DataSource] Add explicit no spellcheck on password fields ([#2818](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2818)) +- [CVE-2022-25912] Bumps simple-git from 3.4.0 to 3.15.0 ([#3036](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3036)) +- [CVE-2022-35256] Bumps node version from 14.20.0 to 14.20.1 [#3166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3166)) +- [CVE-2022-46175] Bumps json5 version from 1.0.1 and 2.2.1 to 1.0.2 and 2.2.3 ([#3201](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3201)) +- [CVE-2022-25860] Bumps simple-git from 3.15.1 to 3.16.0 ([#3345](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3345)) ### 📈 Features/Enhancements @@ -46,6 +50,14 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Vis Builder] Add field summary popovers ([#2682](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2682)) - [I18n] Register ru, ru-RU locale ([#2817](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2817)) - Add yarn opensearch arg to setup plugin dependencies ([#2544](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2544)) +- [Multi DataSource] Test the connection to an external data source when creating or updating ([#2973](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2973)) +- [Table Visualization] Refactor table visualization using React and DataGrid component ([#2863](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2863)) +- [Vis Builder] Add redux store persistence ([#3088](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3088)) +- [Multi DataSource] Improve test connection ([#3110](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3110)) +- [Vis Builder] Add app filter and query persistence without using state container ([#3100](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3100)) +- [Optimizer] Increase timeout waiting for the exiting of an optimizer worker ([#3193](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3193)) +- [Data] Update `createAggConfig` so that newly created configs can be added to beginning of `aggConfig` array ([#3160](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3160)) +- Add disablePrototypePoisoningProtection configuration to prevent JS client from erroring when cluster utilizes JS reserved words ([#2992](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2992)) ### 🐛 Bug Fixes @@ -61,6 +73,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multi DataSource] Address UX comments on index pattern management stack ([#2611](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2611)) - [Multi DataSource] Apply get indices error handling in step index pattern ([#2652](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2652)) - [Vis Builder] Last Updated Timestamp for visbuilder savedobject is getting Generated ([#2628](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2628)) +- [Vis Builder] fixes filters for table visualisation ([#3210](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3210)) - Removed Leftover X Pack references ([#2638](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2638)) - Removes Add Integration button ([#2723](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2723)) - Change geckodriver version to make consistency ([#2772](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2772)) @@ -69,12 +82,28 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Temporary workaround for task-kill exceptions on Windows when it is passed a pid for a process that is already dead ([#2842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2842)) - [Vis Builder] Fix empty workspace animation does not work in firefox ([#2853](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2853)) - Bumped `del` version to fix MacOS race condition ([#2847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2873)) +- [Build] Fixed "Last Access Time" not being set by `scanCopy` on Windows ([#2964](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2964)) +- [Vis Builder] Add global data persistence for vis builder #2896 ([#2896](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2896)) +- Update `leaflet-vega` and fix its usage ([#3005](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3005)) +- [Table Visualization][bug] Fix Url content display issue in table ([#2918](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2918)) +- Fixes misleading embaddable plugin error message ([#3043](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3043)) +- [MD] Update dummy url in tests to follow lychee url allowlist ([#3099](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3099)) +- Adds config override to fix obsolete theme:version config value of v8 (beta) rendering issue ([#3045](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3045)) +- [CI] Update test workflow to increase network-timeout for yarn for installing dependencies ([#3118](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3118)) +- [VisBuilder] Fixes pipeline aggs ([#3137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3137)) +- [Region Maps] Fixes bug that prevents selected join field to be used ([#3213](Fix bug that prevents selected join field to be used)) +- [Multi DataSource]Update test connection button text([#3247](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3247)) ### 🚞 Infrastructure - Add CHANGELOG.md and related workflows ([#2414](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2414)) - Update backport custom branch name to utilize head template ([#2766](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2766)) - Re-enable CI workflows for feature branckes ([#2908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2908)) +- Add Windows CI workflows ([#2966](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2966)) +- Add automatic selection of the appropriate version of chrome driver to run functional tests ([#2990](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2990)) +- Add recording of functional test artifacts if they fail ([#3190](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3190)) +- Improve yarn's performance in workflows by caching yarn's cache folder ([#3194](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3194)) +- Fix detection of Chrome's version on Darwin during CI ([#3296](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3296)) ### 📝 Documentation @@ -83,21 +112,35 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [MD] Tweak multiple data source design doc [#2724](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2724) - Corrected README and help command of osd-plugin-helpers ([#2810](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2810)) - Add `current-usage.md` and more details to `README.md` of `charts` plugin ([#2695](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2695)) +- [Doc] Add readme for global query persistence ([#3001](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3001)) +- Updates NOTICE file, adds validation to GitHub CI ([#3051](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3051)) +- [Doc] Add current plugin persistence implementation readme ([#3081](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3081)) ### 🛠 Maintenance - Adding @zhongnansu as maintainer. ([#2590](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2590)) +- Removes `minimatch` manual resolution ([#3019](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3019)) +- Remove `github-checks-reporter`, an unused dependency ([#3126](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3126)) +- Upgrade `vega-lite` dependency to ^5.6.0 ([#3076](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3076)) +- Bumps `re2` and `supertest` ([3018](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3018)) ### 🪛 Refactoring - [MD] Refactor data source error handling ([#2661](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2661)) - Refactor and improve Discover field summaries ([#2391](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2391)) - [Vis Builder] Removed Hard Coded Strings and Used i18n to transalte([#2867](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2867)) +- [Console] Replace jQuery.ajax with core.http when calling OSD APIs in console ([#3080]https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3080)) ### 🔩 Tests - [Multi DataSource] Add unit test coverage for Update Data source management stack ([#2567](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2567)) - [BWC Tests] Add BWC tests for 2.5.0 ([#2890](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2890)) +- Add retrial of flaky tests ([#2967](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2967)) +- Fix incorrect validation of time values in JUnit Reporter ([#2965](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2965)) +- Make tests covering plugin installation on cluster snapshots work across platforms ([#2994](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2994)) +- Correct the linting logic for `no-restricted-path` to ignore trailing slashes ([#3020](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3020)) +- [Tests] Bumps `chromedriver` to v107 ([#3017](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3017)) +- [Vis Builder] Adds field unit tests ([#3211](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3211)) ## [2.x] @@ -114,7 +157,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Resolve sub-dependent d3-color version and potential security issue ([#2454](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2454)) - [CVE-2022-3517] Bumps minimatch from 3.0.4 to 3.0.5 and [IBM X-Force ID: 220063] unset-value from 1.0.1 to 2.0.1 ([#2640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2640)) - [CVE-2022-37601] Bump loader-utils to 2.0.3 ([#2689](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2689)) -- [WS-2021-0638][Security] bump mocha to 10.1.0 ([#2711](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2711)) +- [CVE-2022-37599] Bump loader-utils to 2.0.4 ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)) +- [CVE-2022-37603] Bump loader-utils to 2.0.4 ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)) +- [WS-2021-0638][security] bump mocha to 10.1.0 ([#2711](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2711)) ### 📈 Features/Enhancements diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 4fe8fc4e368c..af3cce9f61a0 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -36,6 +36,11 @@ $ npm i -g yarn $ yarn osd bootstrap # This command will also install npm dependencies ``` +If you experience a network timeout while bootstrapping, you can update the timeout by configuring it in the `.yarnrc`. For example: +``` +network-timeout 1000000 +``` + ### Configure OpenSearch Dashboards *This step is only mandatory if you have https/authentication enabled, or if you use the OpenSearch Docker image in its default configuration.* diff --git a/Dockerfile b/Dockerfile index d5fddcd2c6cf..8f2742a6c1f2 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG NODE_VERSION=14.20.0 +ARG NODE_VERSION=14.20.1 FROM node:${NODE_VERSION} AS base ENV HOME '.' @@ -13,7 +13,7 @@ RUN apt-get update && \ # Specify the version of Chrome that matches the version of chromedriver in the package.json. # A list of Chrome versions can be found here: # https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable -ARG CHROME_VERSION=100.0.4896.127-1 +ARG CHROME_VERSION=107.0.5304.121-1 RUN curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \ && wget -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb \ && apt-get update \ diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 3c01b79dd913..717903ef2915 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,25 +1,27 @@ -# OpenSearch-Dashboards Maintainers +## Overview -## Maintainers -| Maintainer | GitHub ID | Affiliation | -| --------------- | --------- | ----------- | -| Anan | [ananzh](https://github.com/ananzh) | Amazon | -| Bishoy Boktor | [boktorbb-amzn](https://github.com/boktorbb-amzn) | Amazon | -| Mihir Soni | [mihirsoni](https://github.com/mihirsoni) | Amazon | -| Rocky | [kavilla](https://github.com/kavilla) | Amazon | -| Sean Neumann | [seanneumann](https://github.com/seanneumann) | Amazon | -| Miki Barahmand | [AMoo-Miki](https://github.com/AMoo-Miki) | Amazon | -| Ashwin P Chandran | [ashwin-pc](https://github.com/ashwin-pc) | Amazon | -| Josh Romero | [joshuarrrr](https://github.com/joshuarrrr) | Amazon | -| Abby Hu | [abbyhu2000](https://github.com/abbyhu2000) | Amazon | -| Yan Zeng | [zengyan-amazon](https://github.com/zengyan-amazon) | Amazon | -| Kristen Tian | [kristenTian](https://github.com/kristenTian) | Amazon | -| Zhongnan Su | [zhongnansu](https://github.com/zhongnansu) | Amazon | +This document contains a list of maintainers in this repo. See [opensearch-project/.github/RESPONSIBILITIES.md](https://github.com/opensearch-project/.github/blob/main/RESPONSIBILITIES.md#maintainer-responsibilities) that explains what the role of maintainer means, what maintainers do in this and other repos, and how they should be doing it. If you're interested in contributing, and becoming a maintainer, see [CONTRIBUTING](CONTRIBUTING.md). -## Emeritus +## Current Maintainers + +| Maintainer | GitHub ID | Affiliation | +| ------------------------- | --------------------------------------------------- | ----------- | +| Anan Zhuang | [ananzh](https://github.com/ananzh) | Amazon | +| Bishoy Boktor | [boktorbb](https://github.com/boktorbb) | Amazon | +| Mihir Soni | [mihirsoni](https://github.com/mihirsoni) | Amazon | +| Kawika (Rocky) Avilla | [kavilla](https://github.com/kavilla) | Amazon | +| Sean Neumann | [seanneumann](https://github.com/seanneumann) | Amazon | +| Miki Barahmand | [AMoo-Miki](https://github.com/AMoo-Miki) | Amazon | +| Ashwin P Chandran | [ashwin-pc](https://github.com/ashwin-pc) | Amazon | +| Josh Romero | [joshuarrrr](https://github.com/joshuarrrr) | Amazon | +| Abby Hu | [abbyhu2000](https://github.com/abbyhu2000) | Amazon | +| Yan Zeng | [zengyan-amazon](https://github.com/zengyan-amazon) | Amazon | +| Kristen Tian | [kristenTian](https://github.com/kristenTian) | Amazon | +| Zhongnan Su | [zhongnansu](https://github.com/zhongnansu) | Amazon | +| Manasvini B Suryanarayana | [manasvinibs](https://github.com/manasvinibs) | Amazon | -| Maintainer | GitHub ID | Affiliation | -| --------------- | --------- | ----------- | -| Tommy Markley | [tmarkley](https://github.com/tmarkley) | Amazon | +## Emeritus -[This document](https://github.com/opensearch-project/.github/blob/main/MAINTAINERS.md) explains what maintainers do, and how they should be doing it. If you're interested in contributing, see [CONTRIBUTING](CONTRIBUTING.md). +| Maintainer | GitHub ID | Affiliation | +| ------------- | --------------------------------------- | ----------- | +| Tommy Markley | [tmarkley](https://github.com/tmarkley) | Amazon | diff --git a/NOTICE.txt b/NOTICE.txt index edd87ef8de7f..5962cc847b61 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,7 +1,15 @@ -OpenSearch -Copyright 2021 OpenSearch Contributors -This product includes software, including Kibana source code, developed by Elasticsearch (http://www.elastic.co). -Copyright 2012-2021 Elasticsearch B.V. +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software, including Kibana source code, +developed by Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch B.V. + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/) + +This product includes software developed by +Joda.org (http://www.joda.org/). --- Pretty handling of logarithmic axes. Copyright (c) 2007-2014 IOLA and Ole Laursen. @@ -115,10 +123,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- -This product uses Noto fonts that are licensed under the SIL Open -Font License, Version 1.1. - --- Based on the scroll-into-view-if-necessary module from npm https://github.com/stipsan/compute-scroll-into-view/blob/master/src/index.ts#L269-L340 @@ -145,70 +149,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- -Detection Rules -Copyright 2020 Elasticsearch B.V. - ---- -This product bundles rules based on https://github.com/BlueTeamLabs/sentinel-attack -which is available under a "MIT" license. The files based on this license are: - -- defense_evasion_via_filter_manager -- discovery_process_discovery_via_tasklist_command -- persistence_priv_escalation_via_accessibility_features -- persistence_via_application_shimming -- defense_evasion_execution_via_trusted_developer_utilities - -MIT License - -Copyright (c) 2019 Edoardo Gerosa, Olaf Hartong - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- -This product bundles rules based on https://github.com/FSecureLABS/leonidas -which is available under a "MIT" license. The files based on this license are: - -- credential_access_secretsmanager_getsecretvalue.toml - -MIT License - -Copyright (c) 2020 F-Secure LABS - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --- MIT License @@ -306,43 +246,3 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ---- -This product includes code in the function applyCubicBezierStyles that was -inspired by a public Codepen, which was available under a "MIT" license. - -Copyright (c) 2020 by Guillaume (https://codepen.io/guillaumethomas/pen/xxbbBKO) -MIT License http://www.opensource.org/licenses/mit-license - ---- -This product includes code that is adapted from mapbox-gl-js, which is -available under a "BSD-3-Clause" license. -https://github.com/mapbox/mapbox-gl-js/blob/master/src/util/image.js - -Copyright (c) 2016, Mapbox - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of Mapbox GL JS nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/TESTING.md b/TESTING.md index 68077547ef88..9cca21b7eee6 100644 --- a/TESTING.md +++ b/TESTING.md @@ -24,7 +24,7 @@ In general, we recommend four tiers of tests: # Requirements * Install the latest NodeJS, [NPM](https://www.npmjs.com/get-npm) and [Yarn](https://classic.yarnpkg.com/en/docs/install/#mac-stable) - * `nvm install v14.20.0` + * `nvm install v14.20.1` * `npm install -g yarn` # Running tests diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 4d81b0b3be69..6bb4f63bbbde 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -107,6 +107,11 @@ # Logs queries sent to OpenSearch. Requires logging.verbose set to true. #opensearch.logQueries: false +# Disables errors from the OpenSearch JS client and enables you to utilize protected words such as: 'boolean', 'proto', 'constructor'. +# within cluster. By default, OpenSearch Dashboards and the client will protect you against prototype poisoning attacks. +# WARNING: Index patterns are user-supplied data. Disabling this will place the expectation that you are handling the data safely. +#opensearch.disablePrototypePoisoningProtection: false + # Specifies the path where OpenSearch Dashboards creates the process ID file. #pid.file: /var/run/opensearchDashboards.pid diff --git a/dev-tools/get-version.sh b/dev-tools/get-version.sh deleted file mode 100755 index 555dd89e9e9c..000000000000 --- a/dev-tools/get-version.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Copyright OpenSearch Contributors -# SPDX-License-Identifier: Apache-2.0 - -set -e - -PACKAGE_VERSION=$(cat package.json \ -| grep version \ -| head -1 \ -| awk -F: '{ print $2 }' \ -| sed 's/[",]//g' \ -| tr -d [:space:]) - -echo "$PACKAGE_VERSION" \ No newline at end of file diff --git a/docs/plugins/data_persistence.md b/docs/plugins/data_persistence.md new file mode 100644 index 000000000000..b4141e8a57a6 --- /dev/null +++ b/docs/plugins/data_persistence.md @@ -0,0 +1,235 @@ +# Data persistence +There are currently five plugins that have the ability to persist user data and configurations: `dashboard`, `discover`, `timeline`, `visualize`, and `vis-builder`. Data will be persisted globally when users navigate between them in OpenSearch Dashboards; data will also be persisted locally across the action of refreshes. To achieve this, they use services and mechanisms from `opensearch_dashboard_utils` plugin. + +State syncing utils are a set of helpers to sync application state with URL or browser storage (when setting state: `storeInSessionStore` to `true` in advanced setting, or in the case of an overflowed URL): +1. `syncState()`: subscribe to state changes and push them to state storage; subscribe to state storage and push them to state container +2. storages that are compatible with `syncState()` + 1. `OsdUrlStateStorage`: serialize state and persist it to URL's query param in [Rison](https://github.com/w33ble/rison-node) format; listen for state change in URL and update them back to state + 2. `SessionStorageStateStorage`: serialize state and persist it to URL's query param in session storage +3. state containers: redux-store like objects to help manage states and provide a central place to store state + +# Two types of persistence +There are two types for data persistence: +1. App state (example from visualization plugin) + 1. App state storage key: '_a' + 2. App state is persistent only within the specific app, values will persist when we refresh the page, values will not be persist when we navigate away from the app + 3. For visualize app, the params are: + 1. Query + + ![img](./img/app_query.png) + + 2. App filters + + ![img](./img/app_filter.png) + + 3. Vis & UI state + + ![img](./img/visualization.png) +2. Global query state + 1. Global state storage key: '_g' + 2. Global query state is persistent across the entire OpenSearch Dashboards application, values will persist when we refresh the page, or when we navigate across visualize, discover, timeline or dashboard page. For example, if we set time range to last 24 hours, and refresh intervals to every 30 min, the same time range and refresh intervals will be applied if we navigate to any of the other pages. + 3. Params: + 1. global filters (Select `pin filter` to make the filters global) + + ![img](./img/global_filter.png) + + 2. refresh intervals + + ![img](./img/refresh_interval.png) + + 3. time range + + ![img](./img/time_range.png) + +# URL breakdown & example + +![img](./img/URL_example.png) + +# Global state persistence + +1. In plugin.ts, during plugin setup, call `createOsdUrlTracker()`, listen to history changes and global state changes, then update the nav link URL. This also returns function such as `onMountApp()`, `onUnmountedApp()` + ```ts + const { + appMounted, + appUnMounted, + ... + } = createOsdUrlTracker({ + baseUrl: core.http.basePath.prepend('/app/visualize'), + defaultSubUrl: '#/', + storageKey: `lastUrl:${core.http.basePath.get()}:visualize`, + navLinkUpdater$: this.appStateUpdater, + stateParams: [ + { + osdUrlKey: '_g', + stateUpdate$: data.query.state$.pipe( + filter( + ({ changes }) => !!(changes.globalFilters || changes.time || changes.refreshInterval) + ), + map(({ state }) => ({ + ...state, + filters: state.filters?.filter(opensearchFilters.isFilterPinned), + })) + ), + }, + ], + .... + ``` + + * When we enter the app and app is mounted, it initializes nav link by getting previously stored URL from storage instance: `const storedUrl = storageInstance.getItem(storageKey)`. (Storage instance is a browser wide session storage instance.) Then it unsubscribes to global `state$` and subscribes to `URL$`. The current app actively listens to history location changes. If there are changes, set the updated URL as the active URL + + ```ts + function onMountApp() { + unsubscribe(); + ... + // track current hash when within app + unsubscribeURLHistory = historyInstance.listen((location) => { + ... + setActiveUrl(location.hash.substr(1)); + } + }); + } + ``` + + * When we are leaving the app and app is unmounted, unsubscribe `URL$` and subscribe to global `state$`. If the global states are changed in another app, the global state listener will still get triggered in this app even though it is unmounted, it will set the updated URL in storage instance, so next time when we enter the app, it gets the URL from the storage instance thus the global state will persist. + + ```ts + function onUnmountApp() { + unsubscribe(); + // propagate state updates when in other apps + unsubscribeGlobalState = stateParams.map(({ stateUpdate$, osdUrlKey }) => + stateUpdate$.subscribe((state) => { + ... + const updatedUrl = setStateToOsdUrl( ... ); + ... + storageInstance.setItem(storageKey, activeUrl); + }) + ); + } + ``` + +2. In `app.tsx`, call `syncQueryStateWithUrl(query, osdUrlStateStorage)` to sync `_g` portion of url with global state params + * When we first enter the app, there is no initial state in the URL, then we initialize and put the _g key into url + + ```ts + if (!initialStateFromUrl) { + osdUrlStateStorage.set(GLOBAL_STATE_STORAGE_KEY, initialState, { + replace: true, + }); + } + ``` + + * When we enter the app, if there is some initial state in the URL(the previous saved URL in `storageInstance`), so we retrieve global state from `_g` URL + + ```ts + // retrieve current state from `_g` url + const initialStateFromUrl = osdUrlStateStorage.get(GLOBAL_STATE_STORAGE_KEY); + // remember whether there was info in the URL + const hasInheritedQueryFromUrl = Boolean( + initialStateFromUrl && Object.keys(initialStateFromUrl).length + ); + // prepare initial state, whatever was in URL takes precedences over current state in services + const initialState: QueryState = { + ...defaultState, + ...initialStateFromUrl, + }; + ``` + + * If we make some changes to the global state: 1. `stateUpdate$` get triggered for all other unmounted app(if we made the change in visualize plugin, then the `stateUpdate$` will get triggered for dashboard, discover, timeline), then it will call `setStateToOsdUrl()` to set `updatedURL` in `storageInstance` so global state get updated for all unmounted app. 2. `updateStorage()` get triggered for `currentApp` to update current URL state storage, then global query state container will also be in sync with URL state storage + + ```ts + const { start, stop: stopSyncingWithUrl } = syncState({ + stateStorage: osdUrlStateStorage, + stateContainer: { + ...globalQueryStateContainer, + set: (state) => { + if (state) { + // syncState utils requires to handle incoming "null" value + globalQueryStateContainer.set(state); + } + }, + }, + storageKey: GLOBAL_STATE_STORAGE_KEY, + }); + start(); + ``` + +# App state persistence + +1. We use `useVisualizeAppState()` hook to instantiate the visualize app state container, which is in sync with '_a' URL + +```ts + const { stateContainer, stopStateSync } = createVisualizeAppState({ + stateDefaults, + osdUrlStateStorage: services.osdUrlStateStorage, + byValue, + }); +``` +2. When we first enter the app, there is no app state in the URL, so we set the default states into URL in `createDefaultVisualizeAppState()`: `osdUrlStateStorage.set(STATE_STORAGE_KEY, initialState, { replace: true });` + +3. When we make changes to the app state, the `dirtyStateChange` event emitter will get triggered, then osd state container will call `updateStorage()` to update the URL state storage, then state container(appState) will also be in sync with URL state storage + +```ts + const onDirtyStateChange = ({ isDirty }: { isDirty: boolean }) => { + if (!isDirty) { + // it is important to update vis state with fresh data + stateContainer.transitions.updateVisState(visStateToEditorState(instance, services).vis); + } + setHasUnappliedChanges(isDirty); + }; + eventEmitter.on('dirtyStateChange', onDirtyStateChange); + ... + const { start, stop: stopSyncingWithUrl } = syncState({ + stateStorage: osdUrlStateStorage, + stateContainer: { + ...globalQueryStateContainer, + set: (state) => { + if (state) { + globalQueryStateContainer.set(state); + } + }, + }, + storageKey: GLOBAL_STATE_STORAGE_KEY, + }); + // start syncing the appState with the ('_a') url + startStateSync(); +``` + +4. In `useEditorUpdates()`, we use the saved appState to load the visualize editor + +5. We can also choose to do state sync without using state container by directing hooking up the state managers with the URL data storage. For example, we implemented state management in Vis Builder with redux store, while the global state persistence implementation is the same, we implemented app state persistence without using any state containers or state syncing utils. For the actual visual data, we directly hook up redux store with `OsdUrlStateStorage` to sync the values by using `saveReduxState()` and `loadReduxState()`. For app filter and query, since they are part of the app states but not part of the redux store, we directly hook up their state managers from data plugin with `OsdUrlStateStorage` and `connectStorageToQueryState()`. + +# Refresh +When we refresh the page, both app state and global state should persist: + +1. `appMounted()` gets triggered for the current app, so current app subscribe to URL$ +2. `syncQueryStateWithUrl()` gets called within app.tsx for the current app, and we are getting the global states from URL '_g', and then `connectToQueryState()` gets called to sync global states and state container for the current app so the current app load the saved global states in top nav +3. `stateUpdate$` will get triggered for every other unmounted app, so the global states are updated for their URL in storage instance as well by calling `setStateOsdUrl()` +4. When we load the visualize editor, `createDefaultVisualizeAppState()` gets called, and it gets app state from URL '_a', and it updates appState based on URL +5. In `useEditorUpdates()`, it uses the updated appState to load the visualization with previous saved states +# Navigate to another app + +When we navigate to another app from the current app, global state should persist: + +1. `appUnmounted()` triggered for the current app, unsubscribe to `URLHistory$`, and subscribe to stateUpdate$ +2. `appMounted()` triggered for the app that we navigated to, so it unsubscribe its `stateUpdate$`, and subscribe to `URLHistory$` +3. `syncQueryStateWithUrl` is triggered, it then gets the saved global state from the `osdurlstatestorage` and set the top nav global states by using `globalQueryStateContainer` + +# Diagrams + +1. When first navigate to the visualize app, initialize and sync state storage and state containers +![img](./img/initialization.png) + +2. When we navigate to another app, the browser wide storage instance stores the last active URL for each app and also updates the URL if there are any new global state values. This ensure global data persistence across different apps. For example, if we navigate from visualize app to discover app: +![img](./img/navigate.png) + +Here is an example of the storage instance: +![img](./img/storage_instance.png) + +3. When we made some changes to the global params, the global query state container will receive updates and push the updates to osd url state storage. Other unmounted app will update their last active URL in instance storage as well. +![img](./img/global_persistence.png) + +4. When we made some changes to the app params, the app state container will receive updates and also push the updates to osd url state storage. +![img](./img/app_persistence.png) + +5. When we refresh the page, we parse the information from the URL(_g for global state, _a for app state). We use the saved information to create new state containers and set up synchronization between state containers and state storage. +![img](./img/refresh.png) diff --git a/docs/plugins/img/URL_example.png b/docs/plugins/img/URL_example.png new file mode 100644 index 0000000000000000000000000000000000000000..d7a42937f8514fd75cef1c8aa3a5c2f43d2298f8 GIT binary patch literal 125722 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 z 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�VYn|Bv7hxnRheu4m6<_ZeV96zMNX0cW!+t5 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 c`E@Sh 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 literal 0 HcmV?d00001 diff --git a/docs/plugins/img/app_persistence.png b/docs/plugins/img/app_persistence.png new file mode 100644 index 0000000000000000000000000000000000000000..e6ba4608fc49d41954a6b20a90607d6b1a62a9ab GIT binary patch literal 257130 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 Ay8r+H literal 0 HcmV?d00001 diff --git a/docs/plugins/img/app_query.png b/docs/plugins/img/app_query.png new file mode 100644 index 0000000000000000000000000000000000000000..287b735a4095c232938a36d93f271538f29d2d8a GIT binary patch literal 6135 zcmZWt2Rzho{J%SA92uF}M<;tEmh@;9_isHPh2YIC!{8*g1OG zVxRMFK?B@e-F*-NO8loD2x6b4mgI+?x?pcB@tf%xK-E0FF;F=P zDG4clWojrCiu87LLKv!F`2#18l=z*oSWkqcq`$wvg#RT85AU0j7Znr~B&DP!rKQD* z9^yWM?pV73ad)5dzk~c8M;+ti;O*jxb@6bAlH%Igd-!6N`1wgh|6ITG#00qf%jE9! zXIjJwN|G#+7bT=5|A|e6B1u|=flC0!)m+`hjaV}xhqA0R^3?tR8U7{w576vCz>Amu zi~Nt}{~}F%Fy3k&ZbU|`^1rL~2mD{-A0SeaH1Yow;&+u#wZt+jQzIq+Su$nnwJ!Oa z#8u>SQAZmSC2_xWnPN z;{BN37S<*kuL%T`v0Z1g!RIAROwGLEDtZYYHn{O{L3ZvvtX`E*?csEA zE6Tfd%<|1q=p24;JZiM*#P;d_Mq?K1*7}x(%6E%jVv7q!8Y(CE?)guFsTiP%Hx$6l zp#)ZPAc!<%(8bXKM@MBCna~jE*80q!ur+K#UF9TuL^F`MW8#7?xIK;>W z6e$EbO+Vj+k%vDQ;KoyplQ%43bih7i1F6VCViu@y6f(kv>O;lcmdYG}{xmZbE;xdQ z4|nM2t96*x^Vh~cdU1jJs zw-PCsG3QjF6m$t(n01YLaB2iIQgMS?czFs(Me9Md^`;OKJzf`zr5gUsXKO;0p-0I= z3Zl+P#S21&Lhj3qZ|RUSppEF|gPw3#y{s9VF!)Uj2j!`uP9}tW2M@U!ZZ<8YkQg#S zZEAREYQT`1Pxt_-GN=h@ARi8I!7It>dT>gIGD1-?fHDb}^+7O_szq`H>G@ZX2>&mv5(!3rTP)lOQkmrsZfwlS@%Zs%X?O`<95%t)w&c0> zgcPFc9Oya^$Q>D!u5Y81<9G7w;PCQ~`kg7eJ1++G(DJK?^-w5SH>PAazqAw^9GpF} z;+#%MZTk2A`J*xvP-$Ca0 zT_KL!vW!l~k7_W*LfYSAYiMgU{rFePv|gH~q{zMSZVl2WAb-gJ>L2aMXbU;C{zVuI zT5X)$Q`s3hQkfg`v57OSP^_AOh6$}`1!j=NaSn>dnelawg{07g zSdrP;$6T=e(%LvStM>#}R2pUzR38QtK;(wFXKQ$^~5 z^XtCtXeTM98OFn*SY7RM9fEopT)Z_CpU0YM? zP$&ZfRWlt}=2)9CTs~YK*VscYFtvto*X8Cuxxkp1o=&M0v=Biko#|}};q>j6G?`r- zaF&wJ0d^z=sy;#Z>ZF>OEKF2!td66jQ$^iOOj><0@*Y2;X_V7#H(+YTX4O;U?qSCL zvhaWpH2T?a+Emf&6uC4%ymN04y5Cg_+K#OLqF!uKFX8G%2)|Kx>+Wb;9WE~q7#O%` zYbynjx$XRB*UN!VPJ(XiyMM&qGP{)hTL$w4_*p6{Fc|dw0e)N`v}qYR(Y=BdPz;iP zPHEkz_yLo26D1+!mH%o=`rMKn*%2LYYVoy}F%~TS_D{{r^)*o&sR$Sp4m;E0ysqHr z=+st`<&U~r1`OW$F2C8qQ70f+gnz2ZzP8?>2f!>19WdOe+mA;yg($MFj>X(A?Z_gAld zO&B*fH)r7Cd8fp}!PS>0!zVIWhL98!yIWO7<$h3~n2`d8tZVnaSm@X|vg3S9cToA! zi=LJ?#;GUsYsmZe@6}%URbc9d!_?K8ji-{aAD_3v0vnTZVFKSHmoCcV^jB_d>Xw;S zl$srV-6PC>b`itvV3uBTd#;T57Mr}1%U1HSz5q<%Rf*

%jMyFxf6=Tq_`hbVbEp zm{{(H*uIkUoUdXNw5jx5!%Gx*bTIMEv#d1+ux|&gPQ#uZ=5QPGA2XdUuK>gDs5UD0 z*w{Kv1Js;v*ycFIVy1k!)CvP>0mSI7DL;3*}31P&ACk1*M`ZLWDo;I*L4w+Tf`Z zZ9qmEg@s&2#B7gB@=?R51oU&XI?AD7o8mL)T5ji`wed^lW`9HOv3Z7OqShc$I@E^BK?E6 za>zI5zvxL{p4lM_MYn6tt0k;jwGBxEWXf)P+b2WFC^yMP%j`q$b0;MP?IbO*$4$9! zo*Wikgu@X8$hWH{VVl&!YnM%G{M+s}_C8U$jYKNZx9KRa^FKzNSqn-{R(j0NXs}D) z(b?#uj2x@tZt@)fI{ei3UV$jfxi%^#3F1FXGIWcV;}3R$#XX((u#edbzt!)J0sM|36F1VO-vaMVO91KtyLd9v0}C3feMx!spbb;>M7 zK_vo3#pow;k0F2Qew>2&>|71jR!Ott?h;xQUurHt5D^lyU#SbKinyav6IBpYpa_*p z@m(IvY7AO_w&cq>rZqdfZsa|#8J~lb&V3kaoR*f;79A&L`^l*yZ&lXEBNK&uAC+1!eP|N5e-o_*6zxnN0OMjES(+y5bJH9`n6F?YN ztkYOVENt3cZnLYANLd&R0a!VlgeaIkWF0+we7{3AG=4Wvi7)SLf5+tc4sDeY8J8~X zcI|?zIqyj1xg6!C_V@GX{EOAY?{fr>74%_22|SAL5dI6I^`q_Ui(ib;EoswYa}u_M z@^k}*?V0%(SF|r&63udLT4pOSmZ)PJUpxw7ghR65o-^PfTf3lzZ(DQBEU*xRe_U&x zRx^kJ+j5$yPn-wQw4X#5z%_GIh~L6oUT0?sFkdlh?m$Gg7N29m7m8bOHg0wlegWBe#FP-yvi|}9hnAnZ8da$iEC!mdY zs$X$k>%A!1DPigQ(dk$^y-PFUFQH^5ZF*%&E)_xaldzHs*!h;!lLiP9%wvBhMTz2v z791yFvs-^FVEw_4_E<&O(98Oyo&0jo@O;nB*IZpfRjcEmTP9`deBnlYb$K(Iix^3v zMMHTmTL%Z)*FnDNS?(jvwEAB1zgj{@f$hMS^h3MRms{Si+Zu#>>#hn)T@>~UaUg$R zvbAR~Nxu{4d0+C}D<)3Lr9nien`df3>v4NO9i=UAQxn2g#y7uQZC!of4eC*>`_?C$P9vu{y=1U86RRt!O zedQptP}IDUTRjp;UQAsW5R{wN4jew2Z0`^g0DevASO#(bx40=C>3hZWwRU0tORG}^Lv3UjTm&7NN(V4xfsPcBQOG*?A zaZ?UXEH=4oZeDr2How1>L_i*In8GXf9mdGQc{eFi9B(e(0TuYopG7I{3VTqYzDA4_ z6@DA_=I{J=pTR%%-tX#VvXyc#H9VRwaXMqw{=n^!Om$vQVqrC8cDd%bu)b|rWjsm% z9o~~Pvyqioa1)Qaw!z82y?&5e>0e^KZjma8Dn(Dr1@OS%P|))9JUd~}Ph2eU*aSa1 z{sk@!nph8MiuoWqG~7r?pXX{P-&rl{<=ot0`7}+Nh@J2^^`j#Y=)L)b@flNmNcP~_$#q5jEb{9#T@Ro+fq_acw(>Ctw=sTZJw3+(J**> zbrikiA0aU{GjwGOH&`HM^*`da<_kMK-*m{GQ+(g)_qKSdD&p4+YCke`Hk*Fn zX*YEx{rA_55Kph~9}d{BczKko_~!*MCJutNLdMXcD1LTQt6 zMo7j*pZKwztqhCl()2;e6b;Z6W73DTC$E%dj{T1&J6Gf#@ApNhS*tUyzJ333D`=bB zQIyMS-dtpa#digfrEptfq1~5zDBt6Qn?WTEf?~Xg%}~DQ?P-bY&UsB)9D=N;c}}J< z3ui5m6>w%sQa+#jmrfJQ3d5Y_aWO;;%xZt8SmpG2A?BeG_y&ks5JA6gnha0a?b_%v z5h2LDkc8fUUZp87HH&rFPb>8VJlocjzZ8>_V%)n>uIIjGQNQXb-^f&WIC;z4hvqgF zgf>!tgd5rl&9(d%8|yzqyt#ux=Z+n9g(KoP<@3wIx|ca+s(3M!%xf$MeLbNwjfcX$ z*Ip^v+zVskK?YDsNYJgZpee;BvdC#?N}L<=TWwmcndLGLMpdaGkRmBSH1FNJs*Mjr zLFYK)$;Y^ykM_lFZ`Nm+HaNhcP^oj?0nw^Ax!rUK?JP$PfxT6EcV5JVPol4^c_m-K zq9@N@cTNo@%;p-w@f{s(p;`4*HwgV7uEcSu8rTY9V6HpETDvPMp69r@tpeok(HWN* zl7m2Zg_7<1uB00hb_+cLmD?Szw1@9s(k>3gh#&qnp>tC61_-}`+#w8{AZh6j!nT*$ zht78UY;=U5{rtUSuGV(aaBYV^Kk=%mlN`qV+V zKOd$a;zvd(l#j~U3wbFhV z_2{80@iPboT|0q7wNS!}d{ev*x@Rq4V)+URi_~8&5XO$D4p*wGw6t_} z@7lF*rmThSZwjvs%p@ize&`!6jm-H0$Qcnis#v)fKh5)@RV;Lqf9U;<<#E~ znBV~rUo$hpAsoS~Ly;2`2F`B?E2A&>A{KLzfvO?4C$yY0;mRR*dPB*LJ5Sm>+dnRW z`=>d-%qJW@{<;>=t*Ct)RC?QVPGO-U&q&aXv7>XVI9>kH-Y=~Y8i3I^Zyz7wBZi*~ zfHT;@8$7esS2bNNk+_zQY=em(R9j17ANuI}VvBLz#8YA5;+V#V&!3-K9`UJBsMg&Y zD43O5x1Mzg)j;m>_EA&PE>bqo1DAr#*Ekp-2$S*Gft1vN=ief>t@l z=7SL>k2AR}78-hXV)3C~N!O85GoNG78|d8GukjHG1UE|=5HfDwGDKFnvYVJtm!srs z@=Q=fmXYoBTNRg0)$GTJyXAwcaTRF{G%(+IUS@5D4uAcG7P~UC=j$uFnQ|Avm{7MQ zSC%qAP^;+nAPR;L8KvN-6Cu~gBQ7%O5MF!_e_IfEYinQfEF)~fW&_$~7tKt^0K;+N zAlH6x1vV-wMgtu<;G$y4KC;kpGNIxA&st}zP;PptwlV;2t&~L;x zA93@r!zNaO3f|bCs!pk5!lA&s;9_~hsFXkK%|67~P%XToeDOP42=Qc1n%Nzy<~bOY zljHX5T><$mD3jsobV=z3$TI{y*TzT2e8UakwqLEo-dT~3nb+oRa%r2tYe9j^wZQum3BezaFT@+dI#%hwY%bRg~Uj zEap$fRN(&0C^tr2yfoi=V)_2i<3}UIZ@>Uydh}tLtYMQY-)iR$i%iFJIGNL^|4tAE z#c=@Zv;cA;$Jt*#XYupl!vGwmvJVXZDG(L-2Q~PZQYFJcv<6|(O#CAhpsk^=Ua4w( G_kRFk+``lV literal 0 HcmV?d00001 diff --git a/docs/plugins/img/global_filter.png b/docs/plugins/img/global_filter.png new file mode 100644 index 0000000000000000000000000000000000000000..f741d708f3397ef0b794da254c615c2f6fd0784e GIT binary patch literal 103758 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

- } - > -
- -
- -
- -

- - - Authentication Method - - -

-
-
-
- -
- -
-
- - - -
-
- -
- -
- -
- -
- - -
- -
- -
- -
- -
-
- - -
- - -
-
- - - -
-
- - -
-
- - - - -
-
-
-
-
-
-
- -
-
- - - -
-
- -
- -
- - , - ] - } - compressed={false} - fullWidth={false} - icon="lock" - isLoading={false} - > -
-
- - - - -
- - - - - -
-
-
- - - -
-
-
-
-
- -
- - - - - -
-
-
-
-
-
-
-
- -
- -
- -
- -
- - -
- - -
- - - -`; - -exports[`Datasource Management: Edit Datasource Form Case 2: With No Authentication should render normally 1`] = ` - - -
- -
- -
-
- -

- create-test-ds123 -

-
- -
- -
-
- - -
- - - - - - - -
-
-
- -
- -
- -
- -
-

- - - Connection Details - - -

-
-
- -
-
- - -

- } - title={ -

- -

- } - > -
- -
- -
- -

- - - Object Details - - -

-
- -
- -
-

- - - This connection information is used for reference in tables and when adding to a data source connection - - -

-
-
-
-
-
-
- -
- -
-
- - - -
-
- - -
-
- - - - -
-
-
-
-
-
-
- - - - - - - - - - - } - labelType="label" - > -
-
- - - -
-
- - -
-
- - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
- -
-

- - - Endpoint - - -

-
-
- -
-
- -
-
- - - -
-
- - -
-
- - - - -
-
-
-
-
-
-
-
-
- -
- - -
- -
-

- - - Authentication - - -

-
-
- -
-
- - - - } - > -
- -
- -
- -

- - - Authentication Method - - -

-
-
-
- -
- -
-
- - - -
-
- -
- -
- -
- -
- - -
- -
- -
- -
- -
-
- - -
- -
- -
- -
- -
- -
- - -
- - -
- - - -`; diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx index bf63f9ff8125..492e34e4e198 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx @@ -58,6 +58,7 @@ describe('Datasource Management: Edit Datasource Form', () => { existingDatasourceNamesList={existingDatasourceNamesList} onDeleteDataSource={mockFn} handleSubmit={mockFn} + handleTestConnection={mockFn} displayToastMessage={mockFn} /> ), @@ -72,7 +73,6 @@ describe('Datasource Management: Edit Datasource Form', () => { }); test('should render normally', () => { - expect(component).toMatchSnapshot(); // @ts-ignore expect(component.find({ name: titleFieldIdentifier }).first().props().value).toBe( mockDataSourceAttributesWithAuth.title @@ -230,6 +230,7 @@ describe('Datasource Management: Edit Datasource Form', () => { existingDatasourceNamesList={existingDatasourceNamesList} onDeleteDataSource={mockFn} handleSubmit={mockFn} + handleTestConnection={mockFn} displayToastMessage={mockFn} /> ), @@ -244,7 +245,6 @@ describe('Datasource Management: Edit Datasource Form', () => { }); test('should render normally', () => { - expect(component).toMatchSnapshot(); // @ts-ignore expect(component.find({ name: titleFieldIdentifier }).first().props().value).toBe( mockDataSourceAttributesWithNoAuth.title @@ -326,5 +326,14 @@ describe('Datasource Management: Edit Datasource Form', () => { }, 100) ); }); + + /* Test Connection */ + test('should test connection on click test connection button', async () => { + expect(component.find('Header').exists()).toBe(true); + // @ts-ignore + component.find('Header').first().prop('onClickTestConnection')(); + component.update(); + expect(mockFn).toHaveBeenCalled(); + }); }); }); diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx index 46c91ad540c8..561a651edee2 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx @@ -45,6 +45,7 @@ export interface EditDataSourceProps { existingDataSource: DataSourceAttributes; existingDatasourceNamesList: string[]; handleSubmit: (formValues: DataSourceAttributes) => void; + handleTestConnection: (formValues: DataSourceAttributes) => void; onDeleteDataSource?: () => void; displayToastMessage: (info: ToastMessageItem) => void; } @@ -231,7 +232,7 @@ export class EditDataSourceForm extends React.Component { + this.setState({ isLoading: true }); + const existingAuthType = this.props.existingDataSource.auth.type; + + try { + const isNewCredential = !!( + existingAuthType === AuthType.NoAuth && this.state.auth.type !== existingAuthType + ); + const formValues: DataSourceAttributes = { + title: this.state.title, + description: this.state.description, + endpoint: this.props.existingDataSource.endpoint, + auth: { + ...this.state.auth, + credentials: { + ...this.state.auth.credentials, + password: isNewCredential ? this.state.auth.credentials.password : '', + }, + }, + }; + + await this.props.handleTestConnection(formValues); + + this.props.displayToastMessage({ + id: 'dataSourcesManagement.editDataSource.testConnectionSuccessMsg', + defaultMessage: + 'Connecting to the endpoint using the provided authentication method was successful.', + success: true, + }); + } catch (e) { + this.props.displayToastMessage({ + id: 'dataSourcesManagement.editDataSource.testConnectionFailMsg', + defaultMessage: + 'Failed Connecting to the endpoint using the provided authentication method.', + }); + } finally { + this.setState({ isLoading: false }); + } + }; + onChangeFormValues = () => { setTimeout(() => { this.didFormValuesChange(); @@ -280,7 +321,7 @@ export class EditDataSourceForm extends React.Component ); }; diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/header/__snapshots__/header.test.tsx.snap b/src/plugins/data_source_management/public/components/edit_data_source/components/header/__snapshots__/header.test.tsx.snap deleted file mode 100644 index d9877a2cdc1d..000000000000 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/header/__snapshots__/header.test.tsx.snap +++ /dev/null @@ -1,175 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Datasource Management: Edit Datasource Header do not show delete icon should render normally 1`] = ` - -
- -
- -
-
- -

- testTest20 -

-
- -
- -
-
- - -
- -
- -
-
-`; - -exports[`Datasource Management: Edit Datasource Header show delete icon should render normally 1`] = ` - -
- -
- -
-
- -

- testTest20 -

-
- -
- -
-
- - -
- - - - - - - -
-
-
- -
-
-`; diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx index 36a3551d9ada..f679a7db6e67 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx @@ -26,8 +26,10 @@ describe('Datasource Management: Edit Datasource Header', () => { component = mount( wrapWithIntl(
), @@ -41,7 +43,6 @@ describe('Datasource Management: Edit Datasource Header', () => { }); test('should render normally', () => { - expect(component).toMatchSnapshot(); expect(component.find(headerTitleIdentifier).last().text()).toBe(dataSourceName); }); test('should show confirm delete modal pop up on trash icon click and cancel button work normally', () => { @@ -76,8 +77,10 @@ describe('Datasource Management: Edit Datasource Header', () => { component = mount( wrapWithIntl(
), @@ -90,7 +93,6 @@ describe('Datasource Management: Edit Datasource Header', () => { ); }); test('should render normally', () => { - expect(component).toMatchSnapshot(); expect(component.find(headerTitleIdentifier).last().text()).toBe(dataSourceName); expect(component.find(deleteIconIdentifier).exists()).toBe(false); }); diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx index 8a73bcccc275..49c100b7ec9a 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx @@ -13,6 +13,7 @@ import { EuiToolTip, EuiButtonIcon, EuiConfirmModal, + EuiButton, } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; @@ -21,11 +22,15 @@ import { DataSourceManagementContext } from '../../../../types'; export const Header = ({ showDeleteIcon, + isFormValid, onClickDeleteIcon, + onClickTestConnection, dataSourceName, }: { showDeleteIcon: boolean; + isFormValid: boolean; onClickDeleteIcon: () => void; + onClickTestConnection: () => void; dataSourceName: string; }) => { /* State Variables */ @@ -105,9 +110,28 @@ export const Header = ({ ); }; + const renderTestConnectionButton = () => { + return ( + { + onClickTestConnection(); + }} + data-test-subj="datasource-edit-testConnectionButton" + > + + + ); + }; return ( + {/* Title */}
@@ -116,7 +140,16 @@ export const Header = ({
- {showDeleteIcon ? renderDeleteButton() : null} + + {/* Right side buttons */} + + + {/* Test connection button */} + {renderTestConnectionButton()} + {/* Delete icon button */} + {showDeleteIcon ? renderDeleteButton() : null} + +
); }; diff --git a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx index d11f8c8bc9da..5f6e823e0f86 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx @@ -85,7 +85,6 @@ describe('Datasource Management: Edit Datasource Wizard', () => { }); test('should render normally', () => { - expect(component).toMatchSnapshot(); expect(component.find(notFoundIdentifier).exists()).toBe(false); expect(utils.getDataSources).toHaveBeenCalled(); expect(utils.getDataSourceById).toHaveBeenCalled(); @@ -136,5 +135,14 @@ describe('Datasource Management: Edit Datasource Wizard', () => { component.update(); expect(utils.deleteDataSourceById).toHaveBeenCalled(); }); + test('should test connection', () => { + spyOn(utils, 'testConnection'); + // @ts-ignore + component.find('EditDataSourceForm').first().prop('handleTestConnection')( + mockDataSourceAttributesWithAuth + ); + component.update(); + expect(utils.testConnection).toHaveBeenCalled(); + }); }); }); diff --git a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx index 9bbaecfccce4..bc2bac5b66b8 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx @@ -15,6 +15,7 @@ import { deleteDataSourceById, getDataSourceById, getDataSources, + testConnection, updateDataSourceById, } from '../utils'; import { getEditBreadcrumbs } from '../breadcrumbs'; @@ -39,6 +40,7 @@ export const EditDataSource: React.FunctionComponent().services; const dataSourceID: string = props.match.params.id; @@ -110,6 +112,11 @@ export const EditDataSource: React.FunctionComponent { + await testConnection(http, attributes, dataSourceID); + }; + /* Render the edit wizard */ const renderContent = () => { if (!isLoading && (!dataSource || !dataSource.id)) { @@ -124,6 +131,7 @@ export const EditDataSource: React.FunctionComponent ) : null} {isLoading || !dataSource?.endpoint ? : null} diff --git a/src/plugins/data_source_management/public/components/utils.test.ts b/src/plugins/data_source_management/public/components/utils.test.ts index 7aeb00e14f7e..ec30c7966971 100644 --- a/src/plugins/data_source_management/public/components/utils.test.ts +++ b/src/plugins/data_source_management/public/components/utils.test.ts @@ -10,6 +10,7 @@ import { getDataSourceById, getDataSources, isValidUrl, + testConnection, updateDataSourceById, } from './utils'; import { coreMock } from '../../../../core/public/mocks'; @@ -23,6 +24,7 @@ import { mockResponseForSavedObjectsCalls, } from '../mocks'; import { AuthType } from '../types'; +import { HttpStart } from 'opensearch-dashboards/public'; const { savedObjects } = coreMock.createStart(); @@ -139,6 +141,51 @@ describe('DataSourceManagement: Utils.ts', () => { }); }); + describe('Test connection to the endpoint of the data source - success', () => { + let http: jest.Mocked; + const mockSuccess = jest.fn().mockResolvedValue({ body: { success: true } }); + const mockError = jest.fn().mockRejectedValue(null); + beforeEach(() => { + http = coreMock.createStart().http; + http.post.mockResolvedValue(mockSuccess); + }); + test('Success: Test Connection to the endpoint while creating a new data source', async () => { + await testConnection(http, getDataSourceByIdWithoutCredential.attributes); + expect(http.post.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/internal/data-source-management/validate", + Object { + "body": "{\\"dataSourceAttr\\":{\\"endpoint\\":\\"https://test.com\\",\\"auth\\":{\\"type\\":\\"no_auth\\",\\"credentials\\":null}}}", + }, + ], + ] + `); + }); + + test('Success: Test Connection to the endpoint while existing data source is updated', async () => { + await testConnection(http, getDataSourceByIdWithoutCredential.attributes, 'test1234'); + expect(http.post.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/internal/data-source-management/validate", + Object { + "body": "{\\"id\\":\\"test1234\\",\\"dataSourceAttr\\":{\\"endpoint\\":\\"https://test.com\\",\\"auth\\":{\\"type\\":\\"no_auth\\",\\"credentials\\":null}}}", + }, + ], + ] + `); + }); + test('failure: Test Connection to the endpoint while creating/updating a data source', async () => { + try { + http.post.mockRejectedValue(mockError); + await testConnection(http, getDataSourceByIdWithoutCredential.attributes, 'test1234'); + } catch (e) { + expect(e).toBeTruthy(); + } + }); + }); + describe('Delete multiple data sources by id', () => { test('Success: deleting multiple data source', async () => { try { diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index 51f190be1ba0..3d9d6f51b413 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { SavedObjectsClientContract } from 'src/core/public'; -import { DataSourceTableItem, DataSourceAttributes } from '../types'; +import { HttpStart, SavedObjectsClientContract } from 'src/core/public'; +import { AuthType, DataSourceAttributes, DataSourceTableItem } from '../types'; export async function getDataSources(savedObjectsClient: SavedObjectsClientContract) { return savedObjectsClient @@ -79,6 +79,27 @@ export async function deleteMultipleDataSources( ); } +export async function testConnection( + http: HttpStart, + { endpoint, auth: { type, credentials } }: DataSourceAttributes, + dataSourceID?: string +) { + const query: any = { + id: dataSourceID, + dataSourceAttr: { + endpoint, + auth: { + type, + credentials: type === AuthType.NoAuth ? null : { ...credentials }, + }, + }, + }; + + await http.post(`/internal/data-source-management/validate`, { + body: JSON.stringify(query), + }); +} + export const isValidUrl = (endpoint: string) => { try { const url = new URL(endpoint); diff --git a/src/plugins/embeddable/public/lib/errors.ts b/src/plugins/embeddable/public/lib/errors.ts index 6561c463890f..2a261d45c90a 100644 --- a/src/plugins/embeddable/public/lib/errors.ts +++ b/src/plugins/embeddable/public/lib/errors.ts @@ -49,7 +49,7 @@ export class EmbeddableFactoryNotFoundError extends Error { constructor(type: string) { super( i18n.translate('embeddableApi.errors.embeddableFactoryNotFound', { - defaultMessage: `{type} can't be loaded. Please upgrade to the default distribution of OpenSearch and OpenSearch Dashboards with the appropriate license.`, + defaultMessage: `OpenSearch Dashboards can't load "{type}" visualizations. Check for a missing plugin or an incompatible visualization type.`, values: { type, }, diff --git a/src/plugins/opensearch_dashboards_utils/README.md b/src/plugins/opensearch_dashboards_utils/README.md index 46e436ba89df..d5650849ff13 100644 --- a/src/plugins/opensearch_dashboards_utils/README.md +++ b/src/plugins/opensearch_dashboards_utils/README.md @@ -4,3 +4,4 @@ Utilities for building OpenSearch Dashboards plugins. - [State containers](./docs/state_containers). - [State syncing utilities](./docs/state_sync). +- [Global data persistence](./docs/global_data_persistence.md) diff --git a/src/plugins/opensearch_dashboards_utils/docs/global_data_persistence.md b/src/plugins/opensearch_dashboards_utils/docs/global_data_persistence.md new file mode 100644 index 000000000000..d756b8c35ac5 --- /dev/null +++ b/src/plugins/opensearch_dashboards_utils/docs/global_data_persistence.md @@ -0,0 +1,99 @@ +# Global data persistence + +As of 12/1/2022, there are five plugins that have implemented global data persistence ability in OpenSearch Dashboards, and they are visualize, discover, Timeline, dashboards, and vis-builder. Global data persistence means that the data are not only persisted over refreshes, but also able to be persisted across multiple plugins. We utilize [state containers](https://github.com/opensearch-project/OpenSearch-Dashboards/tree/main/src/plugins/opensearch_dashboards_utils/docs/state_containers), [state storage](https://github.com/opensearch-project/OpenSearch-Dashboards/tree/main/src/plugins/opensearch_dashboards_utils/docs/state_sync/storages) and [state syncing utilities](https://github.com/opensearch-project/OpenSearch-Dashboards/tree/main/src/plugins/opensearch_dashboards_utils/docs/state_sync) from [OpenSearch Dashboards Utils](https://github.com/opensearch-project/OpenSearch-Dashboards/tree/main/src/plugins/opensearch_dashboards_utils) to achieve global data persistence. User can choose to persist data either in URL or session storage by changing the setting `Store URLs in session storage` under advanced setting page. + +One of the global data persistence example that currently exists is global query parameters. Global query parameters include globally pinned filters, time range and time refresh intervals. For example, we set a specific time range and time refresh interval when trying to a new visualization. When we navigate to the dashboard page, we can see the previous time range and time refresh interval that are set within the visualization app are still there. However, when we create a filter, it will only be persisted within that specific plugin since it is not a global filter. We can make a filter become a global filter by selecting `Pin across all apps`. Only global filters are persisted across all other globally persistent plugins within the application. + +The following five steps demonstrate how to add global query parameter persistence for a plugin. Step 3 is specific to global query parameter persistence. For implementing global data persistence in general, step 1 and 2 are required. A function that is similar to step 3 to sync up the state manager of the data with osdUrlStateStorage is also required. + +# Steps to add global data persistence ability to a plugin + +1. Call [`createOsdUrlTracker()`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/plugins/opensearch_dashboards_utils/public/state_management/url/osd_url_tracker.ts) in the set up function within public/plugin.ts. This creates a tracker that syncs the storage with the state manager by listening to history changes and global state changes, and updating the nav link URL of a given app to point to the last visited page. The two functions that get returned, `appMounted()` and `appUnMounted()`, help with global data persistence across the app. When user enters one app, `appMounted()` will be called to make sure that the current app is actively listening to history changes. It will also initialize the URL to be previously stored URL from storage. When user leaves one app, `appUnmounted()` will be called so the app will stop listening actively on history changes, but start subscribing to the global states. Therefore, if the global states are changed in another app, the global state listener will still be triggered in this app even though it is not currently active. It will also update the corresponding URL in the browser storage. By using `appMounted()` and `appUnMounted()`, it makes sure that global data are always persisted no matter which app we are currently on. + * declare two private variables: `appStateUpdater` observable and `stopUrlTracking()` + ```ts + private appStateUpdater = new BehaviorSubject(() => ({})); + private stopUrlTracking?: () => void; + ``` + * within the `setup()` function in the plugin class, call `createOsdUrlTracker` by passing in the corresponding baseUrl, defaultSubUrl, storageKey, navLinkUpdater observable and stateParams. StorageKey should follow format: `lastUrl:${core.http.basePath.get()}:pluginID`. + - `this.appStateUpdater` is passed into the function as `navLinkUpdater`. + - return three functions `appMounted()`, `appUnMounted()` and `stopUrlTracker()`. Then class variable `stopUrlTracking()` is set to be `stopUrlTracker()` + * call `appMounted()` in the `mount()` function + * call `appUnMounted()` in return of `mount()` + * call `stopUrlTracking()` in `stop()` function for the plugin + + ```ts + const { appMounted, appUnMounted, stop: stopUrlTracker } = createOsdUrlTracker({ + baseUrl: core.http.basePath.prepend('/app/vis-builder'), + defaultSubUrl: '#/', + storageKey: `lastUrl:${core.http.basePath.get()}:vis-builder`, + navLinkUpdater$: this.appStateUpdater, + toastNotifications: core.notifications.toasts, + stateParams: [ + { + osdUrlKey: '_g', + stateUpdate$: data.query.state$.pipe( + filter( + ({ changes }) => + !!(changes.globalFilters || changes.time || changes.refreshInterval) + ), + map(({ state }) => ({ + ...state, + filters: state.filters?.filter(opensearchFilters.isFilterPinned), + })) + ), + }, + ], + getHistory: () => { + return this.currentHistory!; + }, + }); + this.stopUrlTracking = () => { + stopUrlTracker(); + }; + ``` + +2. Set [`osdUrlStateStorage()`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/plugins/opensearch_dashboards_utils/public/state_sync/state_sync_state_storage/create_osd_url_state_storage.ts#L83) service. This step initializes the store, and indicates global storage by using '_g' flag. + * when setting the plugin services, set osdUrlStateStorage service by calling `createOsdUrlStateStorage()` with the current history, useHash and withNotifyErrors + + ```ts + const services: VisBuilderServices = { + ...coreStart, + history: params.history, + osdUrlStateStorage: createOsdUrlStateStorage({ + history: params.history, + useHash: coreStart.uiSettings.get('state:storeInSessionStorage'), + ...withNotifyOnErrors(coreStart.notifications.toasts), + }), + ... + + ``` +3. Sync states with storage. There are many ways to do this and use whatever makes sense for your specific use cases. One such implementation is for syncing the query data in `syncQueryStateWithUrl` from the data plugin. + * import [`syncQueryStateWithUrl`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/plugins/data/public/query/state_sync/sync_state_with_url.ts#L48) from data plugin and call it with query service and osdUrlStateStorage service that we set in step 2. This function completes two jobs: 1. When we first enter the app and there is no data stored in the URL, it initializes the URL by putting the `_g` key followed by default data values. 2. When we refresh the page, this function is responsible to retrive the stored states in the URL, and apply them to the app. + + ```ts + export const VisBuilderApp = () => { + const { + services: { + data: { query }, + osdUrlStateStorage, + }, + } = useOpenSearchDashboards(); + const { pathname } = useLocation(); + + useEffect(() => { + // syncs `_g` portion of url with query services + const { stop } = syncQueryStateWithUrl(query, osdUrlStateStorage); + + return () => stop(); + + // this effect should re-run when pathname is changed to preserve querystring part, + // so the global state is always preserved + }, [query, osdUrlStateStorage, pathname]); + ``` + + * If not already, add query services from data plugin in public/plugin_services.ts + + ```ts + export const [getQueryService, setQueryService] = createGetterSetter('Query'); + ``` + diff --git a/src/plugins/region_map/public/region_map_visualization.js b/src/plugins/region_map/public/region_map_visualization.js index 101e4233ca72..81afee4671ca 100644 --- a/src/plugins/region_map/public/region_map_visualization.js +++ b/src/plugins/region_map/public/region_map_visualization.js @@ -158,7 +158,6 @@ export function createRegionMapVisualization({ let selectedLayer; if (DEFAULT_MAP_CHOICE === this._params.layerChosenByUser && this._params.selectedLayer) { selectedLayer = await this._loadConfig(this._params.selectedLayer); - this._params.selectedJoinField = selectedLayer?.fields[0]; } else if ( CUSTOM_MAP_CHOICE === this._params.layerChosenByUser && this._params.selectedCustomLayer diff --git a/src/plugins/vis_builder/public/application/app.tsx b/src/plugins/vis_builder/public/application/app.tsx index bd5f2be1feda..2bdc2b1c631b 100644 --- a/src/plugins/vis_builder/public/application/app.tsx +++ b/src/plugins/vis_builder/public/application/app.tsx @@ -3,17 +3,39 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; +import React, { useEffect } from 'react'; import { I18nProvider } from '@osd/i18n/react'; import { EuiPage, EuiResizableContainer } from '@elastic/eui'; +import { useLocation } from 'react-router-dom'; import { DragDropProvider } from './utils/drag_drop/drag_drop_context'; import { LeftNav } from './components/left_nav'; import { TopNav } from './components/top_nav'; import { Workspace } from './components/workspace'; import './app.scss'; import { RightNav } from './components/right_nav'; +import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public'; +import { VisBuilderServices } from '../types'; +import { syncQueryStateWithUrl } from '../../../data/public'; export const VisBuilderApp = () => { + const { + services: { + data: { query }, + osdUrlStateStorage, + }, + } = useOpenSearchDashboards(); + const { pathname } = useLocation(); + + useEffect(() => { + // syncs `_g` portion of url with query services + const { stop } = syncQueryStateWithUrl(query, osdUrlStateStorage); + + return () => stop(); + + // this effect should re-run when pathname is changed to preserve querystring part, + // so the global state is always preserved + }, [query, osdUrlStateStorage, pathname]); + // Render the application DOM. return ( diff --git a/src/plugins/vis_builder/public/application/components/data_tab/secondary_panel.tsx b/src/plugins/vis_builder/public/application/components/data_tab/secondary_panel.tsx index 385fcda9a805..18a1991f6d80 100644 --- a/src/plugins/vis_builder/public/application/components/data_tab/secondary_panel.tsx +++ b/src/plugins/vis_builder/public/application/components/data_tab/secondary_panel.tsx @@ -3,37 +3,42 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { Component, useCallback, useMemo, useState } from 'react'; import { cloneDeep, get } from 'lodash'; import { useDebounce } from 'react-use'; +import { i18n } from '@osd/i18n'; +import { EuiCallOut } from '@elastic/eui'; import { useTypedDispatch, useTypedSelector } from '../../utils/state_management'; import { DefaultEditorAggParams } from '../../../../../vis_default_editor/public'; import { Title } from './title'; import { useIndexPatterns, useVisualizationType } from '../../utils/use'; -import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public'; +import { + OpenSearchDashboardsContextProvider, + useOpenSearchDashboards, +} from '../../../../../opensearch_dashboards_react/public'; import { VisBuilderServices } from '../../../types'; import { AggParam, IAggType, IFieldParamType } from '../../../../../data/public'; import { saveDraftAgg, editDraftAgg } from '../../utils/state_management/visualization_slice'; -import { setValidity } from '../../utils/state_management/metadata_slice'; +import { setError } from '../../utils/state_management/metadata_slice'; +import { Storage } from '../../../../../opensearch_dashboards_utils/public'; -const EDITOR_KEY = 'CONFIG_PANEL'; +const PANEL_KEY = 'SECONDARY_PANEL'; export function SecondaryPanel() { const { draftAgg, aggConfigParams } = useTypedSelector( (state) => state.visualization.activeVisualization! ); - const isEditorValid = useTypedSelector((state) => state.metadata.editor.validity[EDITOR_KEY]); + const isEditorValid = useTypedSelector((state) => !state.metadata.editor.errors[PANEL_KEY]); const [touched, setTouched] = useState(false); const dispatch = useTypedDispatch(); const vizType = useVisualizationType(); const indexPattern = useIndexPatterns().selected; + const { services } = useOpenSearchDashboards(); const { - services: { - data: { - search: { aggs: aggService }, - }, + data: { + search: { aggs: aggService }, }, - } = useOpenSearchDashboards(); + } = services; const schemas = vizType.ui.containerConfig.data.schemas.all; const aggConfigs = useMemo(() => { @@ -71,9 +76,9 @@ export function SecondaryPanel() { (isValid: boolean) => { // Set validity state globally dispatch( - setValidity({ - key: EDITOR_KEY, - valid: isValid, + setError({ + key: PANEL_KEY, + error: !isValid, }) ); }, @@ -98,49 +103,98 @@ export function SecondaryPanel() {
{showAggParamEditor && ( - <DefaultEditorAggParams - className="vbConfig__aggEditor" - agg={aggConfig!} - indexPattern={indexPattern!} - setValidity={handleSetValid} - setTouched={setTouched} - schemas={schemas} - formIsTouched={touched} - groupName={selectedSchema?.group ?? 'none'} - metricAggs={metricAggs} - state={{ - data: {}, - description: '', - title: '', + <OpenSearchDashboardsContextProvider + services={{ + ...services, + storage: new Storage(window.localStorage), // This is necessary for filters }} - setAggParamValue={function <T extends string | number | symbol>( - aggId: string, - paramName: T, - value: any - ): void { - aggConfig.params[paramName] = value; - dispatch(editDraftAgg(aggConfig.serialize())); - }} - onAggTypeChange={function (aggId: string, aggType: IAggType): void { - aggConfig.type = aggType; - - // Persist field if the new agg type supports the existing field - const fieldParam = (aggType.params as AggParam[]).find(({ type }) => type === 'field'); - if (fieldParam) { - const availableFields = (fieldParam as IFieldParamType).getAvailableFields(aggConfig); - const indexField = availableFields.find( - ({ name }) => name === get(draftAgg, 'params.field') - ); - - if (indexField) { - aggConfig.params.field = indexField; - } - } - - dispatch(editDraftAgg(aggConfig.serialize())); - }} - /> + > + <EditorErrorBoundary> + <DefaultEditorAggParams + className="vbConfig__aggEditor" + agg={aggConfig!} + indexPattern={indexPattern!} + setValidity={handleSetValid} + setTouched={setTouched} + schemas={schemas} + formIsTouched={touched} + groupName={selectedSchema?.group ?? 'none'} + metricAggs={metricAggs} + state={{ + data: {}, + description: '', + title: '', + }} + setAggParamValue={function <T extends string | number | symbol>( + aggId: string, + paramName: T, + value: any + ): void { + aggConfig.params[paramName] = value; + dispatch(editDraftAgg(aggConfig.serialize())); + }} + onAggTypeChange={function (aggId: string, aggType: IAggType): void { + aggConfig.type = aggType; + + // Persist field if the new agg type supports the existing field + const fieldParam = (aggType.params as AggParam[]).find( + ({ type }) => type === 'field' + ); + if (fieldParam) { + const availableFields = (fieldParam as IFieldParamType).getAvailableFields( + aggConfig + ); + const indexField = availableFields.find( + ({ name }) => name === get(draftAgg, 'params.field') + ); + + if (indexField) { + aggConfig.params.field = indexField; + } + } + + dispatch(editDraftAgg(aggConfig.serialize())); + }} + /> + </EditorErrorBoundary> + </OpenSearchDashboardsContextProvider> )} </div> ); } + +class EditorErrorBoundary extends Component<{}, { error?: any }> { + state = { + error: undefined, + }; + + static getDerivedStateFromError(error: any) { + return { error }; + } + + componentDidCatch(error) { + // eslint-disable-next-line no-console + console.error(error); + } + + render() { + if (this.state.error) { + return ( + <EuiCallOut + title={i18n.translate('visBuilder.aggParamsEditor.errorTitle', { + defaultMessage: 'Error', + })} + color="danger" + iconType="alert" + > + <p> + {i18n.translate('visBuilder.aggParamsEditor.errorMsg', { + defaultMessage: 'Something went wrong while editing the aggregation', + })} + </p> + </EuiCallOut> + ); + } + return this.props.children; + } +} diff --git a/src/plugins/vis_builder/public/application/components/data_tab/utils/get_available_fields.test.ts b/src/plugins/vis_builder/public/application/components/data_tab/utils/get_available_fields.test.ts new file mode 100644 index 000000000000..60cb780245c7 --- /dev/null +++ b/src/plugins/vis_builder/public/application/components/data_tab/utils/get_available_fields.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FieldSpec } from '../../../../../../data/common'; +import { IndexPatternField, OSD_FIELD_TYPES } from '../../../../../../data/public'; +import { getAvailableFields } from './get_available_fields'; + +describe('getAvailableFields', () => { + const createIndexFields = (fields: Array<Partial<FieldSpec>>) => + fields.map( + (field) => + new IndexPatternField( + { + aggregatable: true, + name: 'field 1', + searchable: false, + type: OSD_FIELD_TYPES.STRING, + ...field, + }, + field.name || 'field' + ) + ); + + test('should return only aggregateable fields by default', () => { + const fields = createIndexFields([ + { + name: 'field 1', + }, + { + aggregatable: false, + name: 'field 2', + }, + { + scripted: true, + name: 'field 3', + }, + { + name: 'field 4', + subType: { + nested: { path: 'something' }, + }, + }, + ]); + + expect(getAvailableFields(fields).length).toBe(1); + }); + + test('should return all fields if filterFieldTypes was not specified', () => { + const fields = createIndexFields([ + { + name: 'field 1', + }, + { + name: 'field 2', + }, + ]); + + expect(getAvailableFields(fields).length).toBe(2); + }); + + test('should filterFieldTypes', () => { + const fields = createIndexFields([ + { + name: 'field 1', + }, + { + name: 'field 2', + type: OSD_FIELD_TYPES.BOOLEAN, + }, + { + name: 'field 3', + type: OSD_FIELD_TYPES.BOOLEAN, + }, + ]); + + expect(getAvailableFields(fields, OSD_FIELD_TYPES.BOOLEAN).length).toBe(2); + }); +}); diff --git a/src/plugins/vis_builder/public/application/components/data_tab/utils/get_field_details.test.ts b/src/plugins/vis_builder/public/application/components/data_tab/utils/get_field_details.test.ts new file mode 100644 index 000000000000..6f9b9f0f856a --- /dev/null +++ b/src/plugins/vis_builder/public/application/components/data_tab/utils/get_field_details.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +// @ts-ignore +import realHits from 'fixtures/real_hits.js'; +// @ts-ignore +import stubbedLogstashFields from 'fixtures/logstash_fields'; +import { coreMock } from '../../../../../../../core/public/mocks'; +import { getStubIndexPattern } from '../../../../../../data/public/test_utils'; +import { getFieldDetails } from './get_field_details'; +import _ from 'lodash'; + +describe('getFieldDetails', () => { + const indexPattern = getStubIndexPattern( + 'logstash-*', + (cfg: any) => cfg, + 'time', + stubbedLogstashFields(), + coreMock.createSetup() + ); + + test('should have error if index pattern is missing', () => { + const details = getFieldDetails(indexPattern.fields[0], []); + + expect(details.total).toBe(0); + expect(details.error).toMatchInlineSnapshot(`"Index pattern not specified."`); + }); + + test('should have error if there are no hits', () => { + const details = getFieldDetails(indexPattern.fields[0], [], indexPattern); + + expect(details.total).toBe(0); + expect(details.error).toMatchInlineSnapshot( + `"No documents match the selected query and filters. Try increasing time range or removing filters."` + ); + }); + + test('should show details if hits are available for the index pattern field', () => { + const details = getFieldDetails( + indexPattern.fields[0], + _.each(_.cloneDeep(realHits), (hit) => indexPattern.flattenHit(hit)), + indexPattern + ); + + expect(details.exists).toBe(20); + expect(details.total).toBe(20); + expect(details.buckets.length).toBe(5); + expect(details.error).toBe(''); + }); +}); diff --git a/src/plugins/vis_builder/public/application/components/top_nav.tsx b/src/plugins/vis_builder/public/application/components/top_nav.tsx index 62d3bb78cc52..768f2db35465 100644 --- a/src/plugins/vis_builder/public/application/components/top_nav.tsx +++ b/src/plugins/vis_builder/public/application/components/top_nav.tsx @@ -6,7 +6,6 @@ import React, { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { useUnmount } from 'react-use'; -import { PLUGIN_ID } from '../../../common'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; import { getTopNavConfig } from '../utils/get_top_nav_config'; import { VisBuilderServices } from '../../types'; @@ -18,6 +17,7 @@ import { setEditorState } from '../utils/state_management/metadata_slice'; import { useCanSave } from '../utils/use/use_can_save'; import { saveStateToSavedObject } from '../../saved_visualizations/transforms'; import { TopNavMenuData } from '../../../../navigation/public'; +import { opensearchFilters, connectStorageToQueryState } from '../../../../data/public'; export const TopNav = () => { // id will only be set for the edit route @@ -28,14 +28,22 @@ export const TopNav = () => { navigation: { ui: { TopNavMenu }, }, + appName, } = services; const rootState = useTypedSelector((state) => state); const dispatch = useTypedDispatch(); const saveDisabledReason = useCanSave(); const savedVisBuilderVis = useSavedVisBuilderVis(visualizationIdFromUrl); + connectStorageToQueryState(services.data.query, services.osdUrlStateStorage, { + filters: opensearchFilters.FilterStateStore.APP_STATE, + query: true, + }); const { selected: indexPattern } = useIndexPatterns(); const [config, setConfig] = useState<TopNavMenuData[] | undefined>(); + const originatingApp = useTypedSelector((state) => { + return state.metadata.originatingApp; + }); useEffect(() => { const getConfig = () => { @@ -47,6 +55,7 @@ export const TopNav = () => { savedVisBuilderVis: saveStateToSavedObject(savedVisBuilderVis, rootState, indexPattern), saveDisabledReason, dispatch, + originatingApp, }, services ); @@ -61,6 +70,7 @@ export const TopNav = () => { saveDisabledReason, dispatch, indexPattern, + originatingApp, ]); // reset validity before component destroyed @@ -71,7 +81,7 @@ export const TopNav = () => { return ( <div className="vbTopNav"> <TopNavMenu - appName={PLUGIN_ID} + appName={appName} config={config} setMenuMountPoint={setHeaderActionMenu} indexPatterns={indexPattern ? [indexPattern] : []} diff --git a/src/plugins/vis_builder/public/application/components/workspace.tsx b/src/plugins/vis_builder/public/application/components/workspace.tsx index 3742cafb976a..6e3371404355 100644 --- a/src/plugins/vis_builder/public/application/components/workspace.tsx +++ b/src/plugins/vis_builder/public/application/components/workspace.tsx @@ -9,9 +9,9 @@ import React, { FC, useState, useMemo, useEffect, useLayoutEffect } from 'react' import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; import { IExpressionLoaderParams } from '../../../../expressions/public'; import { VisBuilderServices } from '../../types'; -import { validateSchemaState } from '../utils/validate_schema_state'; +import { validateSchemaState, validateAggregations } from '../utils/validations'; import { useTypedSelector } from '../utils/state_management'; -import { useVisualizationType } from '../utils/use'; +import { useAggs, useVisualizationType } from '../utils/use'; import { PersistedState } from '../../../../visualizations/public'; import hand_field from '../../assets/hand_field.svg'; @@ -29,6 +29,7 @@ export const Workspace: FC = ({ children }) => { }, } = useOpenSearchDashboards<VisBuilderServices>(); const { toExpression, ui } = useVisualizationType(); + const { aggConfigs } = useAggs(); const [expression, setExpression] = useState<string>(); const [searchContext, setSearchContext] = useState<IExpressionLoaderParams['searchContext']>({ query: data.query.queryString.getQuery(), @@ -42,13 +43,17 @@ export const Workspace: FC = ({ children }) => { useEffect(() => { async function loadExpression() { const schemas = ui.containerConfig.data.schemas; - const [valid, errorMsg] = validateSchemaState(schemas, rootState.visualization); - if (!valid) { - if (errorMsg) { - toasts.addWarning(errorMsg); - } + const noAggs = aggConfigs?.aggs?.length === 0; + const schemaValidation = validateSchemaState(schemas, rootState.visualization); + const aggValidation = validateAggregations(aggConfigs?.aggs || []); + + if (noAggs || !aggValidation.valid || !schemaValidation.valid) { + const err = schemaValidation.errorMsg || aggValidation.errorMsg; + + if (err) toasts.addWarning(err); setExpression(undefined); + return; } @@ -57,7 +62,7 @@ export const Workspace: FC = ({ children }) => { } loadExpression(); - }, [rootState, toExpression, toasts, ui.containerConfig.data.schemas, searchContext]); + }, [rootState, toExpression, toasts, ui.containerConfig.data.schemas, searchContext, aggConfigs]); useLayoutEffect(() => { const subscription = data.query.state$.subscribe(({ state }) => { diff --git a/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx b/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx index c88bb13f3cb3..9df321822852 100644 --- a/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx @@ -46,22 +46,24 @@ export interface TopNavConfigParams { savedVisBuilderVis: VisBuilderVisSavedObject; saveDisabledReason?: string; dispatch: AppDispatch; + originatingApp?: string; } export const getTopNavConfig = ( - { visualizationIdFromUrl, savedVisBuilderVis, saveDisabledReason, dispatch }: TopNavConfigParams, + { + visualizationIdFromUrl, + savedVisBuilderVis, + saveDisabledReason, + dispatch, + originatingApp, + }: TopNavConfigParams, services: VisBuilderServices ) => { const { i18n: { Context: I18nContext }, embeddable, - scopedHistory, } = services; - const { originatingApp, embeddableId } = - embeddable - .getStateTransfer(scopedHistory) - .getIncomingEditorState({ keysToRemoveAfterFetch: ['id', 'input'] }) || {}; const stateTransfer = embeddable.getStateTransfer(); const topNavConfig: TopNavMenuData[] = [ @@ -105,7 +107,7 @@ export const getTopNavConfig = ( showSaveModal(saveModal, I18nContext); }, }, - ...(originatingApp && ((savedVisBuilderVis && savedVisBuilderVis.id) || embeddableId) + ...(originatingApp && savedVisBuilderVis && savedVisBuilderVis.id ? [ { id: 'saveAndReturn', diff --git a/src/plugins/vis_builder/public/application/utils/mocks.ts b/src/plugins/vis_builder/public/application/utils/mocks.ts index df0687efaa6b..6feb98aaa930 100644 --- a/src/plugins/vis_builder/public/application/utils/mocks.ts +++ b/src/plugins/vis_builder/public/application/utils/mocks.ts @@ -9,6 +9,7 @@ import { dataPluginMock } from '../../../../data/public/mocks'; import { embeddablePluginMock } from '../../../../embeddable/public/mocks'; import { expressionsPluginMock } from '../../../../expressions/public/mocks'; import { navigationPluginMock } from '../../../../navigation/public/mocks'; +import { createOsdUrlStateStorage } from '../../../../opensearch_dashboards_utils/public'; import { VisBuilderServices } from '../../types'; export const createVisBuilderServicesMock = () => { @@ -16,10 +17,11 @@ export const createVisBuilderServicesMock = () => { const toastNotifications = coreStartMock.notifications.toasts; const applicationMock = coreStartMock.application; const i18nContextMock = coreStartMock.i18n.Context; - const indexPatternMock = dataPluginMock.createStartContract().indexPatterns; + const indexPatternMock = dataPluginMock.createStartContract(); const embeddableMock = embeddablePluginMock.createStartContract(); const navigationMock = navigationPluginMock.createStartContract(); const expressionMock = expressionsPluginMock.createStartContract(); + const osdUrlStateStorageMock = createOsdUrlStateStorage({ useHash: false }); const visBuilderServicesMock = { ...coreStartMock, @@ -39,6 +41,21 @@ export const createVisBuilderServicesMock = () => { data: indexPatternMock, embeddable: embeddableMock, scopedHistory: (scopedHistoryMock.create() as unknown) as ScopedHistory, + osdUrlStateStorage: osdUrlStateStorageMock, + types: { + all: () => [ + { + name: 'viz', + ui: { + containerConfig: { + style: { + defaults: 'style default states', + }, + }, + }, + }, + ], + }, }; return (visBuilderServicesMock as unknown) as jest.Mocked<VisBuilderServices>; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/handlers/editor_state.ts b/src/plugins/vis_builder/public/application/utils/state_management/handlers/editor_state.ts new file mode 100644 index 000000000000..279a6cf43687 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/state_management/handlers/editor_state.ts @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { setEditorState } from '../metadata_slice'; +import { RootState, Store } from '../store'; + +export const handlerEditorState = (store: Store, state: RootState, previousState: RootState) => { + const { metadata, ...renderState } = state; + const { metadata: prevMetadata, ...prevRenderState } = previousState; + + // Need to make sure the editorStates are in the clean states(not the initial states) to indicate the viz finished loading + // Because when loading a saved viz from saved object, the previousStore will differ from + // the currentStore even tho there is no changes applied ( aggParams will + // first be empty, and it then will change to not empty once the viz finished loading) + if ( + prevMetadata.editor.state === 'clean' && + metadata.editor.state === 'clean' && + JSON.stringify(renderState) !== JSON.stringify(prevRenderState) + ) { + store.dispatch(setEditorState({ state: 'dirty' })); + } +}; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/handlers/parent_aggs.ts b/src/plugins/vis_builder/public/application/utils/state_management/handlers/parent_aggs.ts new file mode 100644 index 000000000000..255699852c8e --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/state_management/handlers/parent_aggs.ts @@ -0,0 +1,67 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { findLast } from 'lodash'; +import { BUCKET_TYPES, IMetricAggType, search } from '../../../../../../data/public'; +import { VisBuilderServices } from '../../../../types'; +import { RootState, Store } from '../store'; +import { setAggParamValue } from '../visualization_slice'; + +/** + * Parent pipeline aggs when combined with histogram aggs need `min_doc_count` to be set appropriately to avoid an error + * on opensearch engine https://opensearch.org/docs/2.4/opensearch/pipeline-agg/#parent-aggregations + */ +export const handlerParentAggs = async ( + store: Store, + state: RootState, + services: VisBuilderServices +) => { + const { + visualization: { activeVisualization, indexPattern = '' }, + } = state; + + const { + data: { + indexPatterns, + search: { aggs: aggService }, + }, + } = services; + + if (!activeVisualization) return state; + + const aggConfigs = aggService.createAggConfigs( + await indexPatterns.get(indexPattern), + activeVisualization.aggConfigParams + ); + + // Pipeline aggs should have a valid bucket agg + const metricAggs = aggConfigs.aggs.filter((agg) => agg.schema === 'metric'); + const lastParentPipelineAgg = findLast( + metricAggs, + ({ type }: { type: IMetricAggType }) => type.subtype === search.aggs.parentPipelineType + ); + const lastBucket = findLast(aggConfigs.aggs, (agg) => agg.type.type === 'buckets'); + + aggConfigs.aggs.forEach((agg) => { + const isLastBucket = lastBucket?.id === agg.id; + // When a Parent Pipeline agg is selected and this agg is the last bucket. + const isLastBucketAgg = isLastBucket && lastParentPipelineAgg && agg.type; + + if ( + isLastBucketAgg && + ([BUCKET_TYPES.DATE_HISTOGRAM, BUCKET_TYPES.HISTOGRAM] as any).includes(agg.type.name) + ) { + store.dispatch( + setAggParamValue({ + aggId: agg.id, + paramName: 'min_doc_count', + // "histogram" agg has an editor for "min_doc_count" param, which accepts boolean + // "date_histogram" agg doesn't have an editor for "min_doc_count" param, it should be set as a numeric value + value: agg.type.name === 'histogram' ? true : 0, + }) + ); + } + }); +}; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/metadata_slice.ts b/src/plugins/vis_builder/public/application/utils/state_management/metadata_slice.ts index 8cc71804f12e..05ceb324aaa1 100644 --- a/src/plugins/vis_builder/public/application/utils/state_management/metadata_slice.ts +++ b/src/plugins/vis_builder/public/application/utils/state_management/metadata_slice.ts @@ -15,26 +15,34 @@ type EditorState = 'loading' | 'clean' | 'dirty'; export interface MetadataState { editor: { - validity: { - // Validity for each section in the editor + errors: { + // Errors for each section in the editor [key: string]: boolean; }; state: EditorState; }; + originatingApp?: string; } const initialState: MetadataState = { editor: { - validity: {}, + errors: {}, state: 'loading', }, + originatingApp: undefined, }; export const getPreloadedState = async ({ types, data, + embeddable, + scopedHistory, }: VisBuilderServices): Promise<MetadataState> => { - const preloadedState = { ...initialState }; + const { originatingApp } = + embeddable + .getStateTransfer(scopedHistory) + .getIncomingEditorState({ keysToRemoveAfterFetch: ['id', 'input'] }) || {}; + const preloadedState = { ...initialState, originatingApp }; return preloadedState; }; @@ -43,13 +51,16 @@ export const slice = createSlice({ name: 'metadata', initialState, reducers: { - setValidity: (state, action: PayloadAction<{ key: string; valid: boolean }>) => { - const { key, valid } = action.payload; - state.editor.validity[key] = valid; + setError: (state, action: PayloadAction<{ key: string; error: boolean }>) => { + const { key, error } = action.payload; + state.editor.errors[key] = error; }, setEditorState: (state, action: PayloadAction<{ state: EditorState }>) => { state.editor.state = action.payload.state; }, + setOriginatingApp: (state, action: PayloadAction<{ state?: string }>) => { + state.originatingApp = action.payload.state; + }, setState: (_state, action: PayloadAction<MetadataState>) => { return action.payload; }, @@ -57,4 +68,4 @@ export const slice = createSlice({ }); export const { reducer } = slice; -export const { setValidity, setEditorState, setState } = slice.actions; +export const { setError, setEditorState, setOriginatingApp, setState } = slice.actions; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx new file mode 100644 index 000000000000..91f760bbf231 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx @@ -0,0 +1,53 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisBuilderServices } from '../../../types'; +import { createVisBuilderServicesMock } from '../mocks'; +import { loadReduxState, persistReduxState } from './redux_persistence'; +import { RootState } from './store'; + +describe('test redux state persistence', () => { + let mockServices: jest.Mocked<VisBuilderServices>; + let reduxStateParams: any; + + beforeEach(() => { + mockServices = createVisBuilderServicesMock(); + reduxStateParams = { + style: 'style', + visualization: 'visualization', + metadata: 'metadata', + }; + }); + + test('test load redux state when url is empty', async () => { + const defaultStates: RootState = { + style: 'style default states', + visualization: { + searchField: '', + activeVisualization: { name: 'viz', aggConfigParams: [] }, + indexPattern: 'id', + }, + metadata: { + editor: { errors: {}, state: 'loading' }, + originatingApp: undefined, + }, + }; + + const returnStates = await loadReduxState(mockServices); + expect(returnStates).toStrictEqual(defaultStates); + }); + + test('test load redux state', async () => { + mockServices.osdUrlStateStorage.set('_a', reduxStateParams, { replace: true }); + const returnStates = await loadReduxState(mockServices); + expect(returnStates).toStrictEqual(reduxStateParams); + }); + + test('test persist redux state', () => { + persistReduxState(reduxStateParams, mockServices); + const urlStates = mockServices.osdUrlStateStorage.get('_a'); + expect(urlStates).toStrictEqual(reduxStateParams); + }); +}); diff --git a/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts new file mode 100644 index 000000000000..a531986a9ac9 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts @@ -0,0 +1,38 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisBuilderServices } from '../../../types'; +import { getPreloadedState } from './preload'; +import { RootState } from './store'; + +export const loadReduxState = async (services: VisBuilderServices) => { + try { + const serializedState = services.osdUrlStateStorage.get<RootState>('_a'); + if (serializedState !== null) return serializedState; + } catch (err) { + /* eslint-disable no-console */ + console.error(err); + /* eslint-enable no-console */ + } + + return await getPreloadedState(services); +}; + +export const persistReduxState = ( + { style, visualization, metadata }, + services: VisBuilderServices +) => { + try { + services.osdUrlStateStorage.set<RootState>( + '_a', + { style, visualization, metadata }, + { + replace: true, + } + ); + } catch (err) { + return; + } +}; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/store.ts b/src/plugins/vis_builder/public/application/utils/state_management/store.ts index f02fc5e946dd..f1b1c0eeae2a 100644 --- a/src/plugins/vis_builder/public/application/utils/state_management/store.ts +++ b/src/plugins/vis_builder/public/application/utils/state_management/store.ts @@ -4,12 +4,14 @@ */ import { combineReducers, configureStore, PreloadedState } from '@reduxjs/toolkit'; +import { isEqual } from 'lodash'; import { reducer as styleReducer } from './style_slice'; import { reducer as visualizationReducer } from './visualization_slice'; import { reducer as metadataReducer } from './metadata_slice'; import { VisBuilderServices } from '../../..'; -import { getPreloadedState } from './preload'; -import { setEditorState } from './metadata_slice'; +import { loadReduxState, persistReduxState } from './redux_persistence'; +import { handlerEditorState } from './handlers/editor_state'; +import { handlerParentAggs } from './handlers/parent_aggs'; const rootReducer = combineReducers({ style: styleReducer, @@ -25,43 +27,23 @@ export const configurePreloadedStore = (preloadedState: PreloadedState<RootState }; export const getPreloadedStore = async (services: VisBuilderServices) => { - const preloadedState = await getPreloadedState(services); + const preloadedState = await loadReduxState(services); const store = configurePreloadedStore(preloadedState); - const { metadata: metadataState, style: styleState, visualization: vizState } = store.getState(); - let previousStore = { - viz: vizState, - style: styleState, - }; - let previousMetadata = metadataState; + let previousState = store.getState(); // Listen to changes const handleChange = () => { - const { - metadata: currentMetadataState, - style: currentStyleState, - visualization: currentVizState, - } = store.getState(); - const currentStore = { - viz: currentVizState, - style: currentStyleState, - }; - const currentMetadata = currentMetadataState; + const state = store.getState(); + persistReduxState(state, services); + + if (isEqual(state, previousState)) return; - // Need to make sure the editorStates are in the clean states(not the initial states) to indicate the viz finished loading - // Because when loading a saved viz from saved object, the previousStore will differ from - // the currentStore even tho there is no changes applied ( aggParams will - // first be empty, and it then will change to not empty once the viz finished loading) - if ( - previousMetadata.editor.state === 'clean' && - currentMetadata.editor.state === 'clean' && - JSON.stringify(currentStore) !== JSON.stringify(previousStore) - ) { - store.dispatch(setEditorState({ state: 'dirty' })); - } + // Side effects to apply after changes to the store are made + handlerEditorState(store, state, previousState); + handlerParentAggs(store, state, services); - previousStore = currentStore; - previousMetadata = currentMetadata; + previousState = state; }; // the store subscriber will automatically detect changes and call handleChange function @@ -73,7 +55,7 @@ export const getPreloadedStore = async (services: VisBuilderServices) => { // Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType<typeof rootReducer>; export type RenderState = Omit<RootState, 'metadata'>; // Remaining state after auxillary states are removed -type Store = ReturnType<typeof configurePreloadedStore>; +export type Store = ReturnType<typeof configurePreloadedStore>; export type AppDispatch = Store['dispatch']; export { setState as setStyleState, StyleState } from './style_slice'; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/visualization_slice.ts b/src/plugins/vis_builder/public/application/utils/state_management/visualization_slice.ts index 2039c93e8ade..ece2618cbbe1 100644 --- a/src/plugins/vis_builder/public/application/utils/state_management/visualization_slice.ts +++ b/src/plugins/vis_builder/public/application/utils/state_management/visualization_slice.ts @@ -98,6 +98,23 @@ export const slice = createSlice({ updateAggConfigParams: (state, action: PayloadAction<CreateAggConfigParams[]>) => { state.activeVisualization!.aggConfigParams = action.payload; }, + setAggParamValue: ( + state, + action: PayloadAction<{ + aggId: string; + paramName: string; + value: any; + }> + ) => { + const aggIndex = state.activeVisualization!.aggConfigParams.findIndex( + (agg) => agg.id === action.payload.aggId + ); + + state.activeVisualization!.aggConfigParams[aggIndex].params = { + ...state.activeVisualization!.aggConfigParams[aggIndex].params, + [action.payload.paramName]: action.payload.value, + }; + }, setState: (_state, action: PayloadAction<VisualizationState>) => { return action.payload; }, @@ -119,6 +136,7 @@ export const { editDraftAgg, saveDraftAgg, updateAggConfigParams, + setAggParamValue, reorderAgg, setState, } = slice.actions; diff --git a/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts b/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts index 6e5d861c5318..29c14dc07b08 100644 --- a/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts +++ b/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts @@ -23,7 +23,7 @@ import { } from '../state_management'; import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public'; import { setEditorState } from '../state_management/metadata_slice'; -import { validateVisBuilderState } from '../vis_builder_state_validation'; +import { validateVisBuilderState } from '../validations/vis_builder_state_validation'; // This function can be used when instantiating a saved vis or creating a new one // using url parameters, embedding and destroying it in DOM @@ -40,7 +40,7 @@ export const useSavedVisBuilderVis = (visualizationIdFromUrl: string | undefined http: { basePath }, toastNotifications, } = services; - const toastNotification = (message) => { + const toastNotification = (message: string) => { toastNotifications.addDanger({ title: i18n.translate('visualize.createVisualization.failedToLoadErrorMessage', { defaultMessage: 'Failed to load the visualization', @@ -48,6 +48,7 @@ export const useSavedVisBuilderVis = (visualizationIdFromUrl: string | undefined text: message, }); }; + const loadSavedVisBuilderVis = async () => { try { const savedVisBuilderVis = await getSavedVisBuilderVis(services, visualizationIdFromUrl); @@ -73,11 +74,13 @@ export const useSavedVisBuilderVis = (visualizationIdFromUrl: string | undefined const validateResult = validateVisBuilderState({ styleState, visualizationState }); if (!validateResult.valid) { - const err = validateResult.errors; - if (err) { - const errMsg = err[0].instancePath + ' ' + err[0].message; - throw new InvalidJSONProperty(errMsg); - } + throw new InvalidJSONProperty( + validateResult.errorMsg || + i18n.translate('visBuilder.useSavedVisBuilderVis.genericJSONError', { + defaultMessage: + 'Something went wrong while loading your saved object. The object may be corrupted or does not match the latest schema', + }) + ); } dispatch(setStyleState(styleState)); diff --git a/src/plugins/vis_builder/public/application/utils/validations/index.ts b/src/plugins/vis_builder/public/application/utils/validations/index.ts new file mode 100644 index 000000000000..2986b354f669 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/validations/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './validate_aggregations'; +export * from './validate_schema_state'; +export * from './vis_builder_state_validation'; diff --git a/src/plugins/vis_builder/public/application/utils/validations/types.ts b/src/plugins/vis_builder/public/application/utils/validations/types.ts new file mode 100644 index 000000000000..2763c476f2d3 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/validations/types.ts @@ -0,0 +1,9 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface ValidationResult<T = boolean> { + errorMsg?: string; + valid: T; +} diff --git a/src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.test.ts b/src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.test.ts new file mode 100644 index 000000000000..bec1ae506928 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BUCKET_TYPES, IndexPattern, METRIC_TYPES } from '../../../../../data/public'; +import { dataPluginMock } from '../../../../../data/public/mocks'; +import { validateAggregations } from './validate_aggregations'; + +describe('validateAggregations', () => { + const fields = [ + { + name: '@timestamp', + }, + { + name: 'bytes', + }, + ]; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: (name: string) => fields.find((f) => f.name === name), + filter: () => fields, + }, + } as any; + + const dataStart = dataPluginMock.createStartContract(); + + test('Pipeline aggs should have a bucket agg as the last agg', () => { + const aggConfigs = dataStart.search.aggs.createAggConfigs(indexPattern as IndexPattern, [ + { + id: '1', + enabled: true, + type: METRIC_TYPES.CUMULATIVE_SUM, + schema: 'metric', + params: {}, + }, + ]); + + const { valid, errorMsg } = validateAggregations(aggConfigs.aggs); + + expect(valid).toBe(false); + expect(errorMsg).toMatchInlineSnapshot( + `"Add a bucket with \\"Date Histogram\\" or \\"Histogram\\" aggregation."` + ); + }); + + test('Pipeline aggs should have a valid bucket agg', () => { + const aggConfigs = dataStart.search.aggs.createAggConfigs(indexPattern as IndexPattern, [ + { + id: '0', + enabled: true, + type: BUCKET_TYPES.SIGNIFICANT_TERMS, + schema: 'segment', + params: {}, + }, + { + id: '1', + enabled: true, + type: METRIC_TYPES.CUMULATIVE_SUM, + schema: 'metric', + params: {}, + }, + ]); + + const { valid, errorMsg } = validateAggregations(aggConfigs.aggs); + + expect(valid).toBe(false); + expect(errorMsg).toMatchInlineSnapshot( + `"Last bucket aggregation must be \\"Date Histogram\\" or \\"Histogram\\" when using \\"Cumulative Sum\\" metric aggregation."` + ); + }); + + test('Valid pipeline aggconfigs', () => { + const aggConfigs = dataStart.search.aggs.createAggConfigs(indexPattern as IndexPattern, [ + { + id: '0', + enabled: true, + type: BUCKET_TYPES.DATE_HISTOGRAM, + schema: 'segment', + params: {}, + }, + { + id: '1', + enabled: true, + type: METRIC_TYPES.CUMULATIVE_SUM, + schema: 'metric', + params: {}, + }, + ]); + + const { valid, errorMsg } = validateAggregations(aggConfigs.aggs); + + expect(valid).toBe(true); + expect(errorMsg).not.toBeDefined(); + }); +}); diff --git a/src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.ts b/src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.ts new file mode 100644 index 000000000000..470c83e96895 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/validations/validate_aggregations.ts @@ -0,0 +1,54 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { findLast } from 'lodash'; +import { AggConfig, BUCKET_TYPES, IMetricAggType } from '../../../../../data/common'; +import { search } from '../../../../../data/public'; +import { ValidationResult } from './types'; + +/** + * Validate if the aggregations to perform are possible + * @param aggs Aggregations to be performed + * @returns ValidationResult + */ +export const validateAggregations = (aggs: AggConfig[]): ValidationResult => { + // Pipeline aggs should have a valid bucket agg + const metricAggs = aggs.filter((agg) => agg.schema === 'metric'); + const lastParentPipelineAgg = findLast( + metricAggs, + ({ type }: { type: IMetricAggType }) => type.subtype === search.aggs.parentPipelineType + ); + const lastBucket = findLast(aggs, (agg) => agg.type.type === 'buckets'); + + if (!lastBucket && lastParentPipelineAgg) { + return { + valid: false, + errorMsg: i18n.translate('visBuilder.aggregation.mustHaveBucketErrorMessage', { + defaultMessage: 'Add a bucket with "Date Histogram" or "Histogram" aggregation.', + description: 'Date Histogram and Histogram should not be translated', + }), + }; + } + + // Last bucket in a Pipeline aggs should be either a date histogram or histogram + if ( + lastBucket && + lastParentPipelineAgg && + !([BUCKET_TYPES.DATE_HISTOGRAM, BUCKET_TYPES.HISTOGRAM] as any).includes(lastBucket.type.name) + ) { + return { + valid: false, + errorMsg: i18n.translate('visBuilder.aggregation.wrongLastBucketTypeErrorMessage', { + defaultMessage: + 'Last bucket aggregation must be "Date Histogram" or "Histogram" when using "{type}" metric aggregation.', + values: { type: (lastParentPipelineAgg as AggConfig).type.title }, + description: 'Date Histogram and Histogram should not be translated', + }), + }; + } + + return { valid: true }; +}; diff --git a/src/plugins/vis_builder/public/application/utils/validations/validate_schema_state.test.ts b/src/plugins/vis_builder/public/application/utils/validations/validate_schema_state.test.ts new file mode 100644 index 000000000000..a0c017cec3c4 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/validations/validate_schema_state.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Schemas } from '../../../../../vis_default_editor/public'; +import { VisualizationState } from '../state_management'; +import { validateSchemaState } from './validate_schema_state'; + +describe('validateSchemaState', () => { + const schemas = new Schemas([ + { + name: 'metrics', + group: 'metrics', + min: 1, + }, + { + name: 'buckets', + group: 'buckets', + }, + ]); + + test('should error if schema min agg requirement not met', () => { + const visState: VisualizationState = { + searchField: '', + activeVisualization: { + name: 'Test vis', + aggConfigParams: [], + }, + }; + + const { valid, errorMsg } = validateSchemaState(schemas, visState); + + expect(valid).toBe(false); + expect(errorMsg).toMatchInlineSnapshot( + `"The Test vis visualization needs at least 1 field(s) in the agg type \\"metrics\\""` + ); + }); + + test('should be valid if schema requirements are met', () => { + const visState: VisualizationState = { + searchField: '', + activeVisualization: { + name: 'Test vis', + aggConfigParams: [ + { + type: 'count', + schema: 'metrics', + }, + ], + }, + }; + + const { valid, errorMsg } = validateSchemaState(schemas, visState); + + expect(valid).toBe(true); + expect(errorMsg).not.toBeDefined(); + }); +}); diff --git a/src/plugins/vis_builder/public/application/utils/validate_schema_state.ts b/src/plugins/vis_builder/public/application/utils/validations/validate_schema_state.ts similarity index 65% rename from src/plugins/vis_builder/public/application/utils/validate_schema_state.ts rename to src/plugins/vis_builder/public/application/utils/validations/validate_schema_state.ts index 87dc19a3024e..38139768a8f0 100644 --- a/src/plugins/vis_builder/public/application/utils/validate_schema_state.ts +++ b/src/plugins/vis_builder/public/application/utils/validations/validate_schema_state.ts @@ -4,28 +4,24 @@ */ import { countBy } from 'lodash'; -import { Schemas } from '../../../../vis_default_editor/public'; -import { VisualizationState } from './state_management'; +import { Schemas } from '../../../../../vis_default_editor/public'; +import { VisualizationState } from '../state_management'; +import { ValidationResult } from './types'; /** * Validate if the visualization state fits the vis type schema criteria * @param schemas Visualization type config Schema objects * @param state visualization state - * @returns [Validity, 'Message'] + * @returns ValidationResult */ export const validateSchemaState = ( schemas: Schemas, state: VisualizationState -): [boolean, string?] => { +): ValidationResult => { const activeViz = state.activeVisualization; const vizName = activeViz?.name; const aggs = activeViz?.aggConfigParams; - // Check if any aggreagations exist - if (aggs?.length === 0) { - return [false]; - } - // Check if each schema's min agg requirement is met const aggSchemaCount = countBy(aggs, (agg) => agg.schema); const invalidsSchemas = schemas.all.filter((schema) => { @@ -36,11 +32,11 @@ export const validateSchemaState = ( }); if (invalidsSchemas.length > 0) { - return [ - false, - `The ${vizName} visualization needs at least ${invalidsSchemas[0].min} field(s) in the agg type "${invalidsSchemas[0].name}"`, - ]; + return { + valid: false, + errorMsg: `The ${vizName} visualization needs at least ${invalidsSchemas[0].min} field(s) in the agg type "${invalidsSchemas[0].name}"`, + }; } - return [true, '']; + return { valid: true }; }; diff --git a/src/plugins/vis_builder/public/application/utils/vis_builder_state_validation.test.ts b/src/plugins/vis_builder/public/application/utils/validations/vis_builder_state_validation.test.ts similarity index 83% rename from src/plugins/vis_builder/public/application/utils/vis_builder_state_validation.test.ts rename to src/plugins/vis_builder/public/application/utils/validations/vis_builder_state_validation.test.ts index c2d6d41a834f..550e59c65f20 100644 --- a/src/plugins/vis_builder/public/application/utils/vis_builder_state_validation.test.ts +++ b/src/plugins/vis_builder/public/application/utils/validations/vis_builder_state_validation.test.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { RootState } from '../state_management'; import { validateVisBuilderState } from './vis_builder_state_validation'; describe('visBuilder state validation', () => { @@ -12,7 +13,8 @@ describe('visBuilder state validation', () => { legendPosition: '', type: 'metric', }; - const validVisualizationState = { + + const validVisualizationState: RootState['visualization'] = { activeVisualization: { name: 'metric', aggConfigParams: [], @@ -20,6 +22,7 @@ describe('visBuilder state validation', () => { indexPattern: '', searchField: '', }; + describe('correct return when validation suceeds', () => { test('with correct visBuilder state', () => { const validationResult = validateVisBuilderState({ @@ -27,9 +30,10 @@ describe('visBuilder state validation', () => { visualizationState: validVisualizationState, }); expect(validationResult.valid).toBeTruthy(); - expect(validationResult.errors).toBeNull(); + expect(validationResult.errorMsg).toBeUndefined(); }); }); + describe('correct return with errors when validation fails', () => { test('with non object type styleStyle', () => { const validationResult = validateVisBuilderState({ @@ -37,7 +41,7 @@ describe('visBuilder state validation', () => { visualizationState: validVisualizationState, }); expect(validationResult.valid).toBeFalsy(); - expect(validationResult.errors).toBeDefined(); + expect(validationResult.errorMsg).toBeDefined(); }); }); }); diff --git a/src/plugins/vis_builder/public/application/utils/validations/vis_builder_state_validation.ts b/src/plugins/vis_builder/public/application/utils/validations/vis_builder_state_validation.ts new file mode 100644 index 000000000000..e1d85f9ff061 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/validations/vis_builder_state_validation.ts @@ -0,0 +1,23 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import Ajv from 'ajv'; +import visBuilderStateSchema from '../schema.json'; +import { ValidationResult } from './types'; + +const ajv = new Ajv(); +const validateState = ajv.compile(visBuilderStateSchema); + +export const validateVisBuilderState = (visBuilderState: any): ValidationResult => { + const isVisBuilderStateValid = validateState(visBuilderState); + const errorMsg = validateState.errors + ? validateState.errors[0].instancePath + ' ' + validateState.errors[0].message + : undefined; + + return { + valid: isVisBuilderStateValid, + errorMsg, + }; +}; diff --git a/src/plugins/vis_builder/public/application/utils/vis_builder_state_validation.ts b/src/plugins/vis_builder/public/application/utils/vis_builder_state_validation.ts deleted file mode 100644 index 9a601e82594d..000000000000 --- a/src/plugins/vis_builder/public/application/utils/vis_builder_state_validation.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import Ajv from 'ajv'; -import visBuilderStateSchema from './schema.json'; - -const ajv = new Ajv(); -const validateState = ajv.compile(visBuilderStateSchema); - -export const validateVisBuilderState = (visBuilderState) => { - const isVisBuilderStateValid = validateState(visBuilderState); - - return { - valid: isVisBuilderStateValid, - errors: validateState.errors, - }; -}; diff --git a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx index a8c41df6cc43..6282845372ac 100644 --- a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx +++ b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx @@ -27,7 +27,7 @@ import { TimefilterContract, TimeRange, } from '../../../data/public'; -import { validateSchemaState } from '../application/utils/validate_schema_state'; +import { validateSchemaState } from '../application/utils/validations/validate_schema_state'; import { getExpressionLoader, getTypeService } from '../plugin_services'; import { PersistedState } from '../../../visualizations/public'; import { RenderState, VisualizationState } from '../application/utils/state_management'; @@ -139,7 +139,7 @@ export class VisBuilderEmbeddable extends Embeddable<SavedObjectEmbeddableInput, } const { toExpression, ui } = visualizationType; const schemas = ui.containerConfig.data.schemas; - const [valid, errorMsg] = validateSchemaState(schemas, visualizationState); + const { valid, errorMsg } = validateSchemaState(schemas, visualizationState); if (!valid) { if (errorMsg) { diff --git a/src/plugins/vis_builder/public/plugin.test.ts b/src/plugins/vis_builder/public/plugin.test.ts index f5fac728420b..35e17865649a 100644 --- a/src/plugins/vis_builder/public/plugin.test.ts +++ b/src/plugins/vis_builder/public/plugin.test.ts @@ -28,6 +28,7 @@ describe('VisBuilderPlugin', () => { const setupDeps = { visualizations: visualizationsPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createSetupContract(), + data: dataPluginMock.createSetupContract(), }; const setup = plugin.setup(coreSetup, setupDeps); diff --git a/src/plugins/vis_builder/public/plugin.ts b/src/plugins/vis_builder/public/plugin.ts index 8e90a04784cd..3995c1246de5 100644 --- a/src/plugins/vis_builder/public/plugin.ts +++ b/src/plugins/vis_builder/public/plugin.ts @@ -4,13 +4,17 @@ */ import { i18n } from '@osd/i18n'; +import { BehaviorSubject } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; import { AppMountParameters, AppNavLinkStatus, + AppUpdater, CoreSetup, CoreStart, Plugin, PluginInitializerContext, + ScopedHistory, } from '../../../core/public'; import { VisBuilderPluginSetupDependencies, @@ -41,10 +45,17 @@ import { setUISettings, setTypeService, setReactExpressionRenderer, + setQueryService, } from './plugin_services'; import { createSavedVisBuilderLoader } from './saved_visualizations'; import { registerDefaultTypes } from './visualizations'; import { ConfigSchema } from '../config'; +import { + createOsdUrlStateStorage, + createOsdUrlTracker, + withNotifyOnErrors, +} from '../../opensearch_dashboards_utils/public'; +import { opensearchFilters } from '../../data/public'; export class VisBuilderPlugin implements @@ -55,13 +66,45 @@ export class VisBuilderPlugin VisBuilderPluginStartDependencies > { private typeService = new TypeService(); + private appStateUpdater = new BehaviorSubject<AppUpdater>(() => ({})); + private stopUrlTracking?: () => void; + private currentHistory?: ScopedHistory; constructor(public initializerContext: PluginInitializerContext<ConfigSchema>) {} public setup( core: CoreSetup<VisBuilderPluginStartDependencies, VisBuilderStart>, - { embeddable, visualizations }: VisBuilderPluginSetupDependencies + { embeddable, visualizations, data }: VisBuilderPluginSetupDependencies ) { + const { appMounted, appUnMounted, stop: stopUrlTracker } = createOsdUrlTracker({ + baseUrl: core.http.basePath.prepend(`/app/${PLUGIN_ID}`), + defaultSubUrl: '#/', + storageKey: `lastUrl:${core.http.basePath.get()}:${PLUGIN_ID}`, + navLinkUpdater$: this.appStateUpdater, + toastNotifications: core.notifications.toasts, + stateParams: [ + { + osdUrlKey: '_g', + stateUpdate$: data.query.state$.pipe( + filter( + ({ changes }) => !!(changes.globalFilters || changes.time || changes.refreshInterval) + ), + map(({ state }) => ({ + ...state, + filters: state.filters?.filter(opensearchFilters.isFilterPinned), + })) + ), + }, + ], + getHistory: () => { + return this.currentHistory!; + }, + }); + this.stopUrlTracking = () => { + stopUrlTracker(); + }; + + // Register Default Visualizations const typeService = this.typeService; registerDefaultTypes(typeService.setup()); @@ -70,43 +113,63 @@ export class VisBuilderPlugin id: PLUGIN_ID, title: PLUGIN_NAME, navLinkStatus: AppNavLinkStatus.hidden, - async mount(params: AppMountParameters) { + defaultPath: '#/', + mount: async (params: AppMountParameters) => { // Load application bundle const { renderApp } = await import('./application'); // Get start services as specified in opensearch_dashboards.json const [coreStart, pluginsStart, selfStart] = await core.getStartServices(); - const { data, savedObjects, navigation, expressions } = pluginsStart; + const { savedObjects, navigation, expressions } = pluginsStart; + this.currentHistory = params.history; // make sure the index pattern list is up to date - data.indexPatterns.clearCache(); + pluginsStart.data.indexPatterns.clearCache(); // make sure a default index pattern exists // if not, the page will be redirected to management and visualize won't be rendered // TODO: Add the redirect await pluginsStart.data.indexPatterns.ensureDefaultIndexPattern(); - // Register Default Visualizations + appMounted(); + + // dispatch synthetic hash change event to update hash history objects + // this is necessary because hash updates triggered by using popState won't trigger this event naturally. + const unlistenParentHistory = this.currentHistory.listen(() => { + window.dispatchEvent(new HashChangeEvent('hashchange')); + }); const services: VisBuilderServices = { ...coreStart, + appName: PLUGIN_ID, + scopedHistory: this.currentHistory, + history: this.currentHistory, + osdUrlStateStorage: createOsdUrlStateStorage({ + history: this.currentHistory, + useHash: coreStart.uiSettings.get('state:storeInSessionStorage'), + ...withNotifyOnErrors(coreStart.notifications.toasts), + }), toastNotifications: coreStart.notifications.toasts, - data, + data: pluginsStart.data, savedObjectsPublic: savedObjects, navigation, expressions, - history: params.history, setHeaderActionMenu: params.setHeaderActionMenu, types: typeService.start(), savedVisBuilderLoader: selfStart.savedVisBuilderLoader, embeddable: pluginsStart.embeddable, - scopedHistory: params.history, + dashboard: pluginsStart.dashboard, }; // Instantiate the store const store = await getPreloadedStore(services); + const unmount = renderApp(params, services, store); // Render the application - return renderApp(params, services, store); + return () => { + unlistenParentHistory(); + unmount(); + appUnMounted(); + }; }, }); @@ -154,7 +217,7 @@ export class VisBuilderPlugin public start( core: CoreStart, - { data, expressions }: VisBuilderPluginStartDependencies + { expressions, data }: VisBuilderPluginStartDependencies ): VisBuilderStart { const typeService = this.typeService.start(); @@ -176,6 +239,7 @@ export class VisBuilderPlugin setTimeFilter(data.query.timefilter.timefilter); setTypeService(typeService); setUISettings(core.uiSettings); + setQueryService(data.query); return { ...typeService, @@ -183,5 +247,9 @@ export class VisBuilderPlugin }; } - public stop() {} + public stop() { + if (this.stopUrlTracking) { + this.stopUrlTracking(); + } + } } diff --git a/src/plugins/vis_builder/public/plugin_services.ts b/src/plugins/vis_builder/public/plugin_services.ts index f979f3a22b11..c5583e3c5e43 100644 --- a/src/plugins/vis_builder/public/plugin_services.ts +++ b/src/plugins/vis_builder/public/plugin_services.ts @@ -37,3 +37,7 @@ export const [getTimeFilter, setTimeFilter] = createGetterSetter<TimefilterContr export const [getTypeService, setTypeService] = createGetterSetter<TypeServiceStart>('TypeService'); export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); + +export const [getQueryService, setQueryService] = createGetterSetter< + DataPublicPluginStart['query'] +>('Query'); diff --git a/src/plugins/vis_builder/public/types.ts b/src/plugins/vis_builder/public/types.ts index 131c9cc1f6bb..e79762bedc1f 100644 --- a/src/plugins/vis_builder/public/types.ts +++ b/src/plugins/vis_builder/public/types.ts @@ -14,6 +14,8 @@ import { DataPublicPluginStart } from '../../data/public'; import { TypeServiceSetup, TypeServiceStart } from './services/type_service'; import { SavedObjectLoader } from '../../saved_objects/public'; import { AppMountParameters, CoreStart, ToastsStart, ScopedHistory } from '../../../core/public'; +import { IOsdUrlStateStorage } from '../../opensearch_dashboards_utils/public'; +import { DataPublicPluginSetup } from '../../data/public'; export type VisBuilderSetup = TypeServiceSetup; export interface VisBuilderStart extends TypeServiceStart { @@ -23,6 +25,7 @@ export interface VisBuilderStart extends TypeServiceStart { export interface VisBuilderPluginSetupDependencies { embeddable: EmbeddableSetup; visualizations: VisualizationsSetup; + data: DataPublicPluginSetup; } export interface VisBuilderPluginStartDependencies { embeddable: EmbeddableStart; @@ -34,6 +37,7 @@ export interface VisBuilderPluginStartDependencies { } export interface VisBuilderServices extends CoreStart { + appName: string; setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; savedVisBuilderLoader: VisBuilderStart['savedVisBuilderLoader']; toastNotifications: ToastsStart; @@ -45,6 +49,8 @@ export interface VisBuilderServices extends CoreStart { history: History; embeddable: EmbeddableStart; scopedHistory: ScopedHistory; + osdUrlStateStorage: IOsdUrlStateStorage; + dashboard: DashboardStart; } export interface ISavedVis { diff --git a/src/plugins/vis_builder/public/visualizations/table/components/table_viz_options.tsx b/src/plugins/vis_builder/public/visualizations/table/components/table_viz_options.tsx index 8c934fff8dac..a77a0811e609 100644 --- a/src/plugins/vis_builder/public/visualizations/table/components/table_viz_options.tsx +++ b/src/plugins/vis_builder/public/visualizations/table/components/table_viz_options.tsx @@ -3,14 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { get } from 'lodash'; -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; import produce from 'immer'; import { Draft } from 'immer'; import { EuiIconTip } from '@elastic/eui'; -import { search } from '../../../../../data/public'; import { NumberInputOption, SwitchOption } from '../../../../../charts/public'; import { useTypedDispatch, diff --git a/src/plugins/vis_builder/public/visualizations/table/to_expression.ts b/src/plugins/vis_builder/public/visualizations/table/to_expression.ts index 212c93248d40..bbec4c1cc7e9 100644 --- a/src/plugins/vis_builder/public/visualizations/table/to_expression.ts +++ b/src/plugins/vis_builder/public/visualizations/table/to_expression.ts @@ -4,7 +4,7 @@ */ import { SchemaConfig } from '../../../../visualizations/public'; -import { TableVisExpressionFunctionDefinition } from '../../../../vis_type_table_new/public'; +import { TableVisExpressionFunctionDefinition } from '../../../../vis_type_table/public'; import { AggConfigs, IAggConfig } from '../../../../data/common'; import { buildExpression, buildExpressionFunction } from '../../../../expressions/public'; import { RenderState } from '../../application/utils/state_management'; @@ -120,7 +120,7 @@ export const toExpression = async ({ style: styleState, visualization }: TableRo }; const tableVis = buildExpressionFunction<TableVisExpressionFunctionDefinition>( - 'opensearch_dashboards_table_new', + 'opensearch_dashboards_table', { visConfig: JSON.stringify(visConfig), } diff --git a/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx b/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx index c3b244de14f5..1b4508549a71 100644 --- a/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx @@ -139,7 +139,7 @@ function DateRangesParamEditor({ <EuiFormRow display={'rowCompressed'} fullWidth> <> <EuiText size="xs"> - <EuiLink href={services.docLinks.links.noDocumentation.dateMath} target="_blank"> + <EuiLink href={services.docLinks.links.opensearch.dateMath} target="_blank"> <FormattedMessage id="visDefaultEditor.controls.dateRanges.acceptedDateFormatsLinkText" defaultMessage="Acceptable date formats" diff --git a/src/plugins/vis_type_table/README.md b/src/plugins/vis_type_table/README.md index cf37e133ed1c..42304c39e935 100644 --- a/src/plugins/vis_type_table/README.md +++ b/src/plugins/vis_type_table/README.md @@ -1 +1,35 @@ -Contains the data table visualization, that allows presenting data in a simple table format. \ No newline at end of file +# Data Table + +This is an OpenSearch Dashboards plugin that is used to visualize data and aggregations in tabular format. + +## Create Data Table +To create a data table in OpenSearch Dashboards, first select `Visualize` from the navigation menu. Then click `Create Visualization` and choose `Data Table` as the visualization type. + +## Select Metrics + +### Metrics Aggregation +At the `Metrics`, select the metric aggregation type from the menu and configure it accordingly. You could also add multiple metrics and each metrics is a separate column in table visualization. + +### Buckets Aggregation +At the `Buckets`, configure the columns to be displayed in the table visualization. +- `Split Rows` is used when you want to divide one row into more based on some category. It splits one row into multiple and add columns based on the categories you choose to split. For example, if you split the data based on gender then you want to know more on each gender's clothing preference. You could click `Split Rows` and input `clothing.category` in terms. Each gender's data is now split across to multiple rows based on a new added column `clothing.category`. +- `Split Table` splits the table into separate tables for the aggregation you choose. It is similar to `Split Rows`, but this time each row becomes a single table with aggregation columns arranged horizontally or vertically. + +## Select Options +In the `Options` tab, you can configure more options. +- `Max rows per page` is the maximum number of rows displayed per page. +- `Show metrics for every bucket/level` adds metrics aggregation to every column. +- `Show partial rows` will include data with missing columns. +- `Show total` calculates the selected metrics per column and displays the result at the bottom. Warning - depending on your columns and the total aggregation function selected, this may generate statistically invalid results. For example, avoid summing or averaging averages. +- `Percentage column` adds one percentage column based on the chosen metrics aggregation. + +## Example + +Below is an example of creating a table visualization using sample ecommerce data. + +- Create a new data table visualization and set a relative time 15 weeks ago. +- Compute the count of ecommerce: Choose `Count` in Metrics Aggregation. +- Split the rows on the top 5 of `manufacturer.keyword` ordered by `Metric:Count` in descending and add a label "manufacturer". +- Split the table in rows on the top 5 of `geoip.city_name` ordered by `Metric:Count` in ascending order. +- Click the `Save` button on the top left and save the visualization as "Top manufacturers by count per city". +- Choose a table and click the download icon to download the table. diff --git a/src/plugins/vis_type_table/opensearch_dashboards.json b/src/plugins/vis_type_table/opensearch_dashboards.json index e2f050534c1e..ba0ebb1bc4cc 100644 --- a/src/plugins/vis_type_table/opensearch_dashboards.json +++ b/src/plugins/vis_type_table/opensearch_dashboards.json @@ -6,11 +6,11 @@ "requiredPlugins": [ "expressions", "visualizations", - "data", - "opensearchDashboardsLegacy" + "data" ], "requiredBundles": [ "opensearchDashboardsUtils", + "opensearchDashboardsReact", "share", "charts", "visDefaultEditor" diff --git a/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap b/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap index dc6571de969f..a32609c2e3d3 100644 --- a/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap +++ b/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap @@ -2,12 +2,9 @@ exports[`interpreter/functions#table returns an object with the correct structure 1`] = ` Object { - "as": "visualization", + "as": "table_vis", "type": "render", "value": Object { - "params": Object { - "listenOnChange": true, - }, "visConfig": Object { "dimensions": Object { "buckets": Array [], diff --git a/src/plugins/vis_type_table/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_table/public/__snapshots__/to_ast.test.ts.snap new file mode 100644 index 000000000000..296f33f90c73 --- /dev/null +++ b/src/plugins/vis_type_table/public/__snapshots__/to_ast.test.ts.snap @@ -0,0 +1,115 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`table vis toExpressionAst function with customized params 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "aggConfigs": Array [ + "[]", + ], + "includeFormatHints": Array [ + false, + ], + "index": Array [ + "123", + ], + "metricsAtAllLevels": Array [ + false, + ], + "partialRows": Array [ + false, + ], + }, + "function": "opensearchaggs", + "type": "function", + }, + Object { + "arguments": Object { + "visConfig": Array [ + "{\\"perPage\\":5,\\"percentageCol\\":\\"Count\\",\\"showPartialRows\\":false,\\"showMetricsAtAllLevels\\":false,\\"showTotal\\":true,\\"totalFunc\\":\\"min\\",\\"metrics\\":[],\\"buckets\\":[]}", + ], + }, + "function": "opensearch_dashboards_table", + "type": "function", + }, + ], + "type": "expression", +} +`; + +exports[`table vis toExpressionAst function with default params 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "aggConfigs": Array [ + "[]", + ], + "includeFormatHints": Array [ + false, + ], + "index": Array [ + "123", + ], + "metricsAtAllLevels": Array [ + false, + ], + "partialRows": Array [ + false, + ], + }, + "function": "opensearchaggs", + "type": "function", + }, + Object { + "arguments": Object { + "visConfig": Array [ + "{\\"perPage\\":10,\\"percentageCol\\":\\"\\",\\"showPartialRows\\":false,\\"showMetricsAtAllLevels\\":false,\\"showTotal\\":false,\\"totalFunc\\":\\"sum\\",\\"metrics\\":[],\\"buckets\\":[]}", + ], + }, + "function": "opensearch_dashboards_table", + "type": "function", + }, + ], + "type": "expression", +} +`; + +exports[`table vis toExpressionAst function without params 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "aggConfigs": Array [ + "[]", + ], + "includeFormatHints": Array [ + false, + ], + "index": Array [ + "123", + ], + "metricsAtAllLevels": Array [ + false, + ], + "partialRows": Array [ + false, + ], + }, + "function": "opensearchaggs", + "type": "function", + }, + Object { + "arguments": Object { + "visConfig": Array [ + "{\\"metrics\\":[],\\"buckets\\":[]}", + ], + }, + "function": "opensearch_dashboards_table", + "type": "function", + }, + ], + "type": "expression", +} +`; diff --git a/src/plugins/vis_type_table/public/_table_vis.scss b/src/plugins/vis_type_table/public/_table_vis.scss deleted file mode 100644 index ea4b4d0d1c98..000000000000 --- a/src/plugins/vis_type_table/public/_table_vis.scss +++ /dev/null @@ -1,23 +0,0 @@ -// SASSTODO: Update naming to BEM -// This chart is actively being re-written to React and EUI -// Putting off renaming to avoid conflicts -.table-vis { - display: flex; - flex-direction: column; - flex: 1 0 100%; - overflow: auto; -} - -.table-vis-container { - osd-agg-table-group > .table > tbody > tr > td { - border-top: 0; - } - - .pagination-other-pages { - justify-content: flex-end; - } - - .pagination-size { - display: none; - } -} diff --git a/src/plugins/vis_type_table/public/agg_table/_agg_table.scss b/src/plugins/vis_type_table/public/agg_table/_agg_table.scss deleted file mode 100644 index 156db063c8db..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/_agg_table.scss +++ /dev/null @@ -1,42 +0,0 @@ -osd-agg-table, -osd-agg-table-group { - display: block; -} - -.osdAggTable { - display: flex; - flex: 1 1 auto; - flex-direction: column; -} - -.osdAggTable__paginated { - flex: 1 1 auto; - overflow: auto; - - th { - text-align: left; - font-weight: $euiFontWeightBold; - } - - tr:hover td, - .osdTableCellFilter { - background-color: $euiColorLightestShade; - } -} - -.osdAggTable__controls { - flex: 0 0 auto; - display: flex; - align-items: center; - margin: $euiSizeS $euiSizeXS; - - > paginate-controls { - flex: 1 0 auto; - margin: 0; - padding: 0; - } -} - -.small { - font-size: 0.9em !important; -} diff --git a/src/plugins/vis_type_table/public/agg_table/_index.scss b/src/plugins/vis_type_table/public/agg_table/_index.scss deleted file mode 100644 index ed94e8449120..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./agg_table"; diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table.html b/src/plugins/vis_type_table/public/agg_table/agg_table.html deleted file mode 100644 index 8e8aafa83fd8..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/agg_table.html +++ /dev/null @@ -1,34 +0,0 @@ -<paginated-table - ng-if="rows.length" - table="table" - rows="rows" - columns="formattedColumns" - per-page="perPage" - sort="sort" - show-total="showTotal" - percentage-col="percentageCol" - filter="filter" - totalFunc="totalFunc"> - - <div class="osdAggTable__controls"> - <small - i18n-id="visTypeTable.aggTable.exportLabel" - i18n-default-message="Export:" - ></small>   - <a class="small" ng-click="aggTable.exportAsCsv(false)"> - <span - i18n-id="visTypeTable.aggTable.rawLabel" - i18n-default-message="Raw" - ></span> - <i aria-hidden="true" class="fa fa-download"></i> - </a>    - <a class="small" ng-click="aggTable.exportAsCsv(true)"> - <span - i18n-id="visTypeTable.aggTable.formattedLabel" - i18n-default-message="Formatted" - ></span> - <i aria-hidden="true" class="fa fa-download"></i> - </a> - <paginate-controls></paginate-controls> - </div> -</paginated-table> diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table.js b/src/plugins/vis_type_table/public/agg_table/agg_table.js deleted file mode 100644 index a00aea27869f..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/agg_table.js +++ /dev/null @@ -1,295 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; -import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../share/public'; -import aggTableTemplate from './agg_table.html'; -import { getFormatService } from '../services'; -import { i18n } from '@osd/i18n'; - -export function OsdAggTable(config, RecursionHelper) { - return { - restrict: 'E', - template: aggTableTemplate, - scope: { - table: '=', - dimensions: '=', - perPage: '=?', - sort: '=?', - exportTitle: '=?', - showTotal: '=', - totalFunc: '=', - percentageCol: '=', - filter: '=', - }, - controllerAs: 'aggTable', - compile: function ($el) { - // Use the compile function from the RecursionHelper, - // And return the linking function(s) which it returns - return RecursionHelper.compile($el); - }, - controller: function ($scope) { - const self = this; - - self._saveAs = require('@elastic/filesaver').saveAs; - self.csv = { - separator: config.get(CSV_SEPARATOR_SETTING), - quoteValues: config.get(CSV_QUOTE_VALUES_SETTING), - }; - - self.exportAsCsv = function (formatted) { - const csv = new Blob([self.toCsv(formatted)], { type: 'text/csv;charset=utf-8' }); - self._saveAs(csv, self.csv.filename); - }; - - self.toCsv = function (formatted) { - const rows = formatted ? $scope.rows : $scope.table.rows; - const columns = formatted ? [...$scope.formattedColumns] : [...$scope.table.columns]; - - if ($scope.splitRow && formatted) { - columns.unshift($scope.splitRow); - } - - const nonAlphaNumRE = /[^a-zA-Z0-9]/; - const allDoubleQuoteRE = /"/g; - - function escape(val) { - if (!formatted && _.isObject(val)) val = val.valueOf(); - val = String(val); - if (self.csv.quoteValues && nonAlphaNumRE.test(val)) { - val = '"' + val.replace(allDoubleQuoteRE, '""') + '"'; - } - return val; - } - - let csvRows = []; - for (const row of rows) { - const rowArray = []; - for (const col of columns) { - const value = row[col.id]; - const formattedValue = - formatted && col.formatter ? escape(col.formatter.convert(value)) : escape(value); - rowArray.push(formattedValue); - } - csvRows = [...csvRows, rowArray]; - } - - // add the columns to the rows - csvRows.unshift( - columns.map(function (col) { - return escape(formatted ? col.title : col.name); - }) - ); - - return csvRows - .map(function (row) { - return row.join(self.csv.separator) + '\r\n'; - }) - .join(''); - }; - - $scope.$watchMulti( - ['table', 'exportTitle', 'percentageCol', 'totalFunc', '=scope.dimensions'], - function () { - const { table, exportTitle, percentageCol } = $scope; - const showPercentage = percentageCol !== ''; - - if (!table) { - $scope.rows = null; - $scope.formattedColumns = null; - $scope.splitRow = null; - return; - } - - self.csv.filename = (exportTitle || table.title || 'unsaved') + '.csv'; - $scope.rows = table.rows; - $scope.formattedColumns = []; - - if (typeof $scope.dimensions === 'undefined') return; - - const { buckets, metrics, splitColumn, splitRow } = $scope.dimensions; - - $scope.formattedColumns = table.columns - .map(function (col, i) { - const isBucket = buckets.find((bucket) => bucket.accessor === i); - const isSplitColumn = splitColumn - ? splitColumn.find((splitColumn) => splitColumn.accessor === i) - : undefined; - const isSplitRow = splitRow - ? splitRow.find((splitRow) => splitRow.accessor === i) - : undefined; - const dimension = - isBucket || isSplitColumn || metrics.find((metric) => metric.accessor === i); - - const formatter = dimension - ? getFormatService().deserialize(dimension.format) - : undefined; - - const formattedColumn = { - id: col.id, - title: col.name, - formatter: formatter, - filterable: !!isBucket, - }; - - if (isSplitRow) { - $scope.splitRow = formattedColumn; - } - - if (!dimension) return; - - const last = i === table.columns.length - 1; - - if (last || !isBucket) { - formattedColumn.class = 'visualize-table-right'; - } - - const isDate = - dimension.format?.id === 'date' || dimension.format?.params?.id === 'date'; - const allowsNumericalAggregations = formatter?.allowsNumericalAggregations; - - let { totalFunc } = $scope; - if (typeof totalFunc === 'undefined' && showPercentage) { - totalFunc = 'sum'; - } - - if (allowsNumericalAggregations || isDate || totalFunc === 'count') { - const sum = (tableRows) => { - return _.reduce( - tableRows, - function (prev, curr) { - // some metrics return undefined for some of the values - // derivative is an example of this as it returns undefined in the first row - if (curr[col.id] === undefined) return prev; - return prev + curr[col.id]; - }, - 0 - ); - }; - - formattedColumn.sumTotal = sum(table.rows); - switch (totalFunc) { - case 'sum': { - if (!isDate) { - const total = formattedColumn.sumTotal; - formattedColumn.formattedTotal = formatter.convert(total); - formattedColumn.total = formattedColumn.sumTotal; - } - break; - } - case 'avg': { - if (!isDate) { - const total = sum(table.rows) / table.rows.length; - formattedColumn.formattedTotal = formatter.convert(total); - formattedColumn.total = total; - } - break; - } - case 'min': { - const total = _.chain(table.rows).map(col.id).min().value(); - formattedColumn.formattedTotal = formatter.convert(total); - formattedColumn.total = total; - break; - } - case 'max': { - const total = _.chain(table.rows).map(col.id).max().value(); - formattedColumn.formattedTotal = formatter.convert(total); - formattedColumn.total = total; - break; - } - case 'count': { - const total = table.rows.length; - formattedColumn.formattedTotal = total; - formattedColumn.total = total; - break; - } - default: - break; - } - } - - return formattedColumn; - }) - .filter((column) => column); - - if (showPercentage) { - const insertAtIndex = _.findIndex($scope.formattedColumns, { title: percentageCol }); - - // column to show percentage for was removed - if (insertAtIndex < 0) return; - - const { cols, rows } = addPercentageCol( - $scope.formattedColumns, - percentageCol, - table.rows, - insertAtIndex - ); - $scope.rows = rows; - $scope.formattedColumns = cols; - } - } - ); - }, - }; -} - -/** - * @param {Object[]} columns - the formatted columns that will be displayed - * @param {String} title - the title of the column to add to - * @param {Object[]} rows - the row data for the columns - * @param {Number} insertAtIndex - the index to insert the percentage column at - * @returns {Object} - cols and rows for the table to render now included percentage column(s) - */ -function addPercentageCol(columns, title, rows, insertAtIndex) { - const { id, sumTotal } = columns[insertAtIndex]; - const newId = `${id}-percents`; - const formatter = getFormatService().deserialize({ id: 'percent' }); - const i18nTitle = i18n.translate('visTypeTable.params.percentageTableColumnName', { - defaultMessage: '{title} percentages', - values: { title }, - }); - const newCols = insert(columns, insertAtIndex, { - title: i18nTitle, - id: newId, - formatter, - }); - const newRows = rows.map((row) => ({ - [newId]: row[id] / sumTotal, - ...row, - })); - - return { cols: newCols, rows: newRows }; -} - -function insert(arr, index, ...items) { - const newArray = [...arr]; - newArray.splice(index + 1, 0, ...items); - return newArray; -} diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table.test.js b/src/plugins/vis_type_table/public/agg_table/agg_table.test.js deleted file mode 100644 index 14d0c7fe7952..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/agg_table.test.js +++ /dev/null @@ -1,512 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 $ from 'jquery'; -import moment from 'moment'; -import angular from 'angular'; -import 'angular-mocks'; -import sinon from 'sinon'; -import { round } from 'lodash'; - -import { getFieldFormatsRegistry } from '../../../data/public/test_utils'; -import { coreMock } from '../../../../core/public/mocks'; -import { initAngularBootstrap } from '../../../opensearch_dashboards_legacy/public'; -import { setUiSettings } from '../../../data/public/services'; -import { UI_SETTINGS } from '../../../data/public/'; -import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../share/public'; - -import { setFormatService } from '../services'; -import { getInnerAngular } from '../get_inner_angular'; -import { initTableVisLegacyModule } from '../table_vis_legacy_module'; -import { tabifiedData } from './tabified_data'; - -const uiSettings = new Map(); - -describe('Table Vis - AggTable Directive', function () { - const core = coreMock.createStart(); - - core.uiSettings.set = jest.fn((key, value) => { - uiSettings.set(key, value); - }); - - core.uiSettings.get = jest.fn((key) => { - const defaultValues = { - dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS', - 'dateFormat:tz': 'UTC', - [UI_SETTINGS.SHORT_DOTS_ENABLE]: true, - [UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN]: '($0,0.[00])', - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[000]', - [UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0,0.[000]%', - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: 'en', - [UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: {}, - [CSV_SEPARATOR_SETTING]: ',', - [CSV_QUOTE_VALUES_SETTING]: true, - }; - - return defaultValues[key] || uiSettings.get(key); - }); - - let $rootScope; - let $compile; - let settings; - - const initLocalAngular = () => { - const tableVisModule = getInnerAngular('opensearch-dashboards/table_vis', core); - initTableVisLegacyModule(tableVisModule); - }; - - beforeEach(() => { - setUiSettings(core.uiSettings); - setFormatService(getFieldFormatsRegistry(core)); - initAngularBootstrap(); - initLocalAngular(); - angular.mock.module('opensearch-dashboards/table_vis'); - angular.mock.inject(($injector, config) => { - settings = config; - - $rootScope = $injector.get('$rootScope'); - $compile = $injector.get('$compile'); - }); - }); - - let $scope; - beforeEach(function () { - $scope = $rootScope.$new(); - }); - afterEach(function () { - $scope.$destroy(); - }); - - test('renders a simple response properly', function () { - $scope.dimensions = { - metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }], - buckets: [], - }; - $scope.table = tabifiedData.metricOnly.tables[0]; - - const $el = $compile('<osd-agg-table table="table" dimensions="dimensions"></osd-agg-table>')( - $scope - ); - $scope.$digest(); - - expect($el.find('tbody').length).toBe(1); - expect($el.find('td').length).toBe(1); - expect($el.find('td').text()).toEqual('1,000'); - }); - - test('renders nothing if the table is empty', function () { - $scope.dimensions = {}; - $scope.table = null; - const $el = $compile('<osd-agg-table table="table" dimensions="dimensions"></osd-agg-table>')( - $scope - ); - $scope.$digest(); - - expect($el.find('tbody').length).toBe(0); - }); - - test('renders a complex response properly', async function () { - $scope.dimensions = { - buckets: [ - { accessor: 0, params: {} }, - { accessor: 2, params: {} }, - { accessor: 4, params: {} }, - ], - metrics: [ - { accessor: 1, params: {} }, - { accessor: 3, params: {} }, - { accessor: 5, params: {} }, - ], - }; - $scope.table = tabifiedData.threeTermBuckets.tables[0]; - const $el = $('<osd-agg-table table="table" dimensions="dimensions"></osd-agg-table>'); - $compile($el)($scope); - $scope.$digest(); - - expect($el.find('tbody').length).toBe(1); - - const $rows = $el.find('tbody tr'); - expect($rows.length).toBeGreaterThan(0); - - function validBytes(str) { - const num = str.replace(/,/g, ''); - if (num !== '-') { - expect(num).toMatch(/^\d+$/); - } - } - - $rows.each(function () { - // 6 cells in every row - const $cells = $(this).find('td'); - expect($cells.length).toBe(6); - - const txts = $cells.map(function () { - return $(this).text().trim(); - }); - - // two character country code - expect(txts[0]).toMatch(/^(png|jpg|gif|html|css)$/); - validBytes(txts[1]); - - // country - expect(txts[2]).toMatch(/^\w\w$/); - validBytes(txts[3]); - - // os - expect(txts[4]).toMatch(/^(win|mac|linux)$/); - validBytes(txts[5]); - }); - }); - - describe('renders totals row', function () { - async function totalsRowTest(totalFunc, expected) { - function setDefaultTimezone() { - moment.tz.setDefault(settings.get('dateFormat:tz')); - } - - const oldTimezoneSetting = settings.get('dateFormat:tz'); - settings.set('dateFormat:tz', 'UTC'); - setDefaultTimezone(); - - $scope.dimensions = { - buckets: [ - { accessor: 0, params: {} }, - { accessor: 1, format: { id: 'date', params: { pattern: 'YYYY-MM-DD' } } }, - ], - metrics: [ - { accessor: 2, format: { id: 'number' } }, - { accessor: 3, format: { id: 'date' } }, - { accessor: 4, format: { id: 'number' } }, - { accessor: 5, format: { id: 'number' } }, - ], - }; - $scope.table = - tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative.tables[0]; - $scope.showTotal = true; - $scope.totalFunc = totalFunc; - const $el = $(`<osd-agg-table - table="table" - show-total="showTotal" - total-func="totalFunc" - dimensions="dimensions"></osd-agg-table>`); - $compile($el)($scope); - $scope.$digest(); - - expect($el.find('tfoot').length).toBe(1); - - const $rows = $el.find('tfoot tr'); - expect($rows.length).toBe(1); - - const $cells = $($rows[0]).find('th'); - expect($cells.length).toBe(6); - - for (let i = 0; i < 6; i++) { - expect($($cells[i]).text().trim()).toBe(expected[i]); - } - settings.set('dateFormat:tz', oldTimezoneSetting); - setDefaultTimezone(); - } - test('as count', async function () { - await totalsRowTest('count', ['18', '18', '18', '18', '18', '18']); - }); - test('as min', async function () { - await totalsRowTest('min', [ - '', - '2014-09-28', - '9,283', - 'Sep 28, 2014 @ 00:00:00.000', - '1', - '11', - ]); - }); - test('as max', async function () { - await totalsRowTest('max', [ - '', - '2014-10-03', - '220,943', - 'Oct 3, 2014 @ 00:00:00.000', - '239', - '837', - ]); - }); - test('as avg', async function () { - await totalsRowTest('avg', ['', '', '87,221.5', '', '64.667', '206.833']); - }); - test('as sum', async function () { - await totalsRowTest('sum', ['', '', '1,569,987', '', '1,164', '3,723']); - }); - }); - - describe('aggTable.toCsv()', function () { - test('escapes rows and columns properly', function () { - const $el = $compile('<osd-agg-table table="table" dimensions="dimensions"></osd-agg-table>')( - $scope - ); - $scope.$digest(); - - const $tableScope = $el.isolateScope(); - const aggTable = $tableScope.aggTable; - $tableScope.table = { - columns: [ - { id: 'a', name: 'one' }, - { id: 'b', name: 'two' }, - { id: 'c', name: 'with double-quotes(")' }, - ], - rows: [{ a: 1, b: 2, c: '"foobar"' }], - }; - - expect(aggTable.toCsv()).toBe( - 'one,two,"with double-quotes("")"' + '\r\n' + '1,2,"""foobar"""' + '\r\n' - ); - }); - - test('exports rows and columns properly', async function () { - $scope.dimensions = { - buckets: [ - { accessor: 0, params: {} }, - { accessor: 2, params: {} }, - { accessor: 4, params: {} }, - ], - metrics: [ - { accessor: 1, params: {} }, - { accessor: 3, params: {} }, - { accessor: 5, params: {} }, - ], - }; - $scope.table = tabifiedData.threeTermBuckets.tables[0]; - - const $el = $compile('<osd-agg-table table="table" dimensions="dimensions"></osd-agg-table>')( - $scope - ); - $scope.$digest(); - - const $tableScope = $el.isolateScope(); - const aggTable = $tableScope.aggTable; - $tableScope.table = $scope.table; - - const raw = aggTable.toCsv(false); - expect(raw).toBe( - '"extension: Descending","Average bytes","geo.src: Descending","Average bytes","machine.os: Descending","Average bytes"' + - '\r\n' + - 'png,412032,IT,9299,win,0' + - '\r\n' + - 'png,412032,IT,9299,mac,9299' + - '\r\n' + - 'png,412032,US,8293,linux,3992' + - '\r\n' + - 'png,412032,US,8293,mac,3029' + - '\r\n' + - 'css,412032,MX,9299,win,4992' + - '\r\n' + - 'css,412032,MX,9299,mac,5892' + - '\r\n' + - 'css,412032,US,8293,linux,3992' + - '\r\n' + - 'css,412032,US,8293,mac,3029' + - '\r\n' + - 'html,412032,CN,9299,win,4992' + - '\r\n' + - 'html,412032,CN,9299,mac,5892' + - '\r\n' + - 'html,412032,FR,8293,win,3992' + - '\r\n' + - 'html,412032,FR,8293,mac,3029' + - '\r\n' - ); - }); - - test('exports formatted rows and columns properly', async function () { - $scope.dimensions = { - buckets: [ - { accessor: 0, params: {} }, - { accessor: 2, params: {} }, - { accessor: 4, params: {} }, - ], - metrics: [ - { accessor: 1, params: {} }, - { accessor: 3, params: {} }, - { accessor: 5, params: {} }, - ], - }; - $scope.table = tabifiedData.threeTermBuckets.tables[0]; - - const $el = $compile('<osd-agg-table table="table" dimensions="dimensions"></osd-agg-table>')( - $scope - ); - $scope.$digest(); - - const $tableScope = $el.isolateScope(); - const aggTable = $tableScope.aggTable; - $tableScope.table = $scope.table; - - // Create our own converter since the ones we use for tests don't actually transform the provided value - $tableScope.formattedColumns[0].formatter.convert = (v) => `${v}_formatted`; - - const formatted = aggTable.toCsv(true); - expect(formatted).toBe( - '"extension: Descending","Average bytes","geo.src: Descending","Average bytes","machine.os: Descending","Average bytes"' + - '\r\n' + - '"png_formatted",412032,IT,9299,win,0' + - '\r\n' + - '"png_formatted",412032,IT,9299,mac,9299' + - '\r\n' + - '"png_formatted",412032,US,8293,linux,3992' + - '\r\n' + - '"png_formatted",412032,US,8293,mac,3029' + - '\r\n' + - '"css_formatted",412032,MX,9299,win,4992' + - '\r\n' + - '"css_formatted",412032,MX,9299,mac,5892' + - '\r\n' + - '"css_formatted",412032,US,8293,linux,3992' + - '\r\n' + - '"css_formatted",412032,US,8293,mac,3029' + - '\r\n' + - '"html_formatted",412032,CN,9299,win,4992' + - '\r\n' + - '"html_formatted",412032,CN,9299,mac,5892' + - '\r\n' + - '"html_formatted",412032,FR,8293,win,3992' + - '\r\n' + - '"html_formatted",412032,FR,8293,mac,3029' + - '\r\n' - ); - }); - }); - - test('renders percentage columns', async function () { - $scope.dimensions = { - buckets: [ - { accessor: 0, params: {} }, - { accessor: 1, format: { id: 'date', params: { pattern: 'YYYY-MM-DD' } } }, - ], - metrics: [ - { accessor: 2, format: { id: 'number' } }, - { accessor: 3, format: { id: 'date' } }, - { accessor: 4, format: { id: 'number' } }, - { accessor: 5, format: { id: 'number' } }, - ], - }; - $scope.table = - tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative.tables[0]; - $scope.percentageCol = 'Average bytes'; - - const $el = $(`<osd-agg-table - table="table" - dimensions="dimensions" - percentage-col="percentageCol" - ></osd-agg-table>`); - - $compile($el)($scope); - $scope.$digest(); - - const $headings = $el.find('th'); - expect($headings.length).toBe(7); - expect($headings.eq(3).text().trim()).toBe('Average bytes percentages'); - - const countColId = $scope.table.columns.find((col) => col.name === $scope.percentageCol).id; - const counts = $scope.table.rows.map((row) => row[countColId]); - const total = counts.reduce((sum, curr) => sum + curr, 0); - const $percentageColValues = $el.find('tbody tr').map((i, el) => $(el).find('td').eq(3).text()); - - $percentageColValues.each((i, value) => { - const percentage = `${round((counts[i] / total) * 100, 3)}%`; - expect(value).toBe(percentage); - }); - }); - - describe('aggTable.exportAsCsv()', function () { - let origBlob; - function FakeBlob(slices, opts) { - this.slices = slices; - this.opts = opts; - } - - beforeEach(function () { - origBlob = window.Blob; - window.Blob = FakeBlob; - }); - - afterEach(function () { - window.Blob = origBlob; - }); - - test('calls _saveAs properly', function () { - const $el = $compile('<osd-agg-table table="table" dimensions="dimensions">')($scope); - $scope.$digest(); - - const $tableScope = $el.isolateScope(); - const aggTable = $tableScope.aggTable; - - const saveAs = sinon.stub(aggTable, '_saveAs'); - $tableScope.table = { - columns: [ - { id: 'a', name: 'one' }, - { id: 'b', name: 'two' }, - { id: 'c', name: 'with double-quotes(")' }, - ], - rows: [{ a: 1, b: 2, c: '"foobar"' }], - }; - - aggTable.csv.filename = 'somefilename.csv'; - aggTable.exportAsCsv(); - - expect(saveAs.callCount).toBe(1); - const call = saveAs.getCall(0); - expect(call.args[0]).toBeInstanceOf(FakeBlob); - expect(call.args[0].slices).toEqual([ - 'one,two,"with double-quotes("")"' + '\r\n' + '1,2,"""foobar"""' + '\r\n', - ]); - expect(call.args[0].opts).toEqual({ - type: 'text/csv;charset=utf-8', - }); - expect(call.args[1]).toBe('somefilename.csv'); - }); - - test('should use the export-title attribute', function () { - const expected = 'export file name'; - const $el = $compile( - `<osd-agg-table table="table" dimensions="dimensions" export-title="exportTitle">` - )($scope); - $scope.$digest(); - - const $tableScope = $el.isolateScope(); - const aggTable = $tableScope.aggTable; - $tableScope.table = { - columns: [], - rows: [], - }; - $tableScope.exportTitle = expected; - $scope.$digest(); - - expect(aggTable.csv.filename).toEqual(`${expected}.csv`); - }); - }); -}); diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table_group.html b/src/plugins/vis_type_table/public/agg_table/agg_table_group.html deleted file mode 100644 index 2dcf7f125f61..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/agg_table_group.html +++ /dev/null @@ -1,77 +0,0 @@ -<table ng-if="rows" class="table osdAggTable__group" ng-repeat="table in rows"> - <thead> - <tr> - <th ng-if="table.tables" scope="col"> - <span class="osdAggTable__groupHeader">{{ table.title }}</span> - </th> - </tr> - </thead> - <tbody> - <tr> - <td> - <osd-agg-table-group - ng-if="table.tables" - group="table" - filter="filter" - dimensions="dimensions" - per-page="perPage" - sort="sort" - export-title="exportTitle" - percentage-col="percentageCol" - show-total="showTotal" - total-func="totalFunc"></osd-agg-table-group> - <osd-agg-table - ng-if="table.rows" - filter="filter" - table="table" - dimensions="dimensions" - export-title="exportTitle" - per-page="perPage" - sort="sort" - percentage-col="percentageCol" - show-total="showTotal" - total-func="totalFunc"> - </osd-agg-table> - </td> - </tr> - </tbody> -</table> - -<table ng-if="columns" class="table osdAggTable__group"> - <thead> - <tr> - <th ng-repeat="table in columns" ng-if="table.tables" scope="col"> - <span class="osdAggTable__groupHeader">{{ table.title }}</span> - </th> - </tr> - </thead> - <tbody> - <tr> - <td ng-repeat="table in columns"> - <osd-agg-table-group - ng-if="table.tables" - filter="filter" - group="table" - dimensions="dimensions" - per-page="perPage" - sort="sort" - export-title="exportTitle" - show-total="showTotal" - percentage-col="percentageCol" - total-func="totalFunc"></osd-agg-table-group> - <osd-agg-table - ng-if="table.rows" - filter="filter" - table="table" - dimensions="dimensions" - export-title="exportTitle" - per-page="perPage" - sort="sort" - show-total="showTotal" - percentage-col="percentageCol" - total-func="totalFunc"> - </osd-agg-table> - </td> - </tr> - </tbody> -</table> diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table_group.js b/src/plugins/vis_type_table/public/agg_table/agg_table_group.js deleted file mode 100644 index 133b20800a18..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/agg_table_group.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 aggTableGroupTemplate from './agg_table_group.html'; - -export function OsdAggTableGroup(RecursionHelper) { - return { - restrict: 'E', - template: aggTableGroupTemplate, - scope: { - group: '=', - dimensions: '=', - perPage: '=?', - sort: '=?', - exportTitle: '=?', - showTotal: '=', - totalFunc: '=', - percentageCol: '=', - filter: '=', - }, - compile: function ($el) { - // Use the compile function from the RecursionHelper, - // And return the linking function(s) which it returns - return RecursionHelper.compile($el, { - post: function ($scope) { - $scope.$watch('group', function (group) { - // clear the previous "state" - $scope.rows = $scope.columns = false; - - if (!group || !group.tables.length) return; - - const childLayout = group.direction === 'row' ? 'rows' : 'columns'; - - $scope[childLayout] = group.tables; - }); - }, - }); - }, - }; -} diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table_group.test.js b/src/plugins/vis_type_table/public/agg_table/agg_table_group.test.js deleted file mode 100644 index 18a48e922116..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/agg_table_group.test.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 $ from 'jquery'; -import angular from 'angular'; -import 'angular-mocks'; -import expect from '@osd/expect'; - -import { getFieldFormatsRegistry } from '../../../data/public/test_utils'; -import { coreMock } from '../../../../core/public/mocks'; -import { initAngularBootstrap } from '../../../opensearch_dashboards_legacy/public'; -import { setUiSettings } from '../../../data/public/services'; -import { setFormatService } from '../services'; -import { getInnerAngular } from '../get_inner_angular'; -import { initTableVisLegacyModule } from '../table_vis_legacy_module'; -import { tabifiedData } from './tabified_data'; - -const uiSettings = new Map(); - -describe('Table Vis - AggTableGroup Directive', function () { - const core = coreMock.createStart(); - let $rootScope; - let $compile; - - core.uiSettings.set = jest.fn((key, value) => { - uiSettings.set(key, value); - }); - - core.uiSettings.get = jest.fn((key) => { - return uiSettings.get(key); - }); - - const initLocalAngular = () => { - const tableVisModule = getInnerAngular('opensearch-dashboards/table_vis', core); - initTableVisLegacyModule(tableVisModule); - }; - - beforeEach(() => { - setUiSettings(core.uiSettings); - setFormatService(getFieldFormatsRegistry(core)); - initAngularBootstrap(); - initLocalAngular(); - angular.mock.module('opensearch-dashboards/table_vis'); - angular.mock.inject(($injector) => { - $rootScope = $injector.get('$rootScope'); - $compile = $injector.get('$compile'); - }); - }); - - let $scope; - beforeEach(function () { - $scope = $rootScope.$new(); - }); - afterEach(function () { - $scope.$destroy(); - }); - - it('renders a simple split response properly', function () { - $scope.dimensions = { - metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }], - buckets: [], - }; - $scope.group = tabifiedData.metricOnly; - $scope.sort = { - columnIndex: null, - direction: null, - }; - const $el = $( - '<osd-agg-table-group dimensions="dimensions" group="group"></osd-agg-table-group>' - ); - - $compile($el)($scope); - $scope.$digest(); - - // should create one sub-tbale - expect($el.find('osd-agg-table').length).to.be(1); - }); - - it('renders nothing if the table list is empty', function () { - const $el = $( - '<osd-agg-table-group dimensions="dimensions" group="group"></osd-agg-table-group>' - ); - - $scope.group = { - tables: [], - }; - - $compile($el)($scope); - $scope.$digest(); - - const $subTables = $el.find('osd-agg-table'); - expect($subTables.length).to.be(0); - }); - - it('renders a complex response properly', function () { - $scope.dimensions = { - splitRow: [{ accessor: 0, params: {} }], - buckets: [ - { accessor: 2, params: {} }, - { accessor: 4, params: {} }, - ], - metrics: [ - { accessor: 1, params: {} }, - { accessor: 3, params: {} }, - { accessor: 5, params: {} }, - ], - }; - const group = ($scope.group = tabifiedData.threeTermBucketsWithSplit); - const $el = $( - '<osd-agg-table-group dimensions="dimensions" group="group"></osd-agg-table-group>' - ); - $compile($el)($scope); - $scope.$digest(); - - const $subTables = $el.find('osd-agg-table'); - expect($subTables.length).to.be(3); - - const $subTableHeaders = $el.find('.osdAggTable__groupHeader'); - expect($subTableHeaders.length).to.be(3); - - $subTableHeaders.each(function (i) { - expect($(this).text()).to.be(group.tables[i].title); - }); - }); -}); diff --git a/src/plugins/vis_type_table/public/agg_table/tabified_data.js b/src/plugins/vis_type_table/public/agg_table/tabified_data.js deleted file mode 100644 index ce344d5c48b6..000000000000 --- a/src/plugins/vis_type_table/public/agg_table/tabified_data.js +++ /dev/null @@ -1,806 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -export const tabifiedData = { - metricOnly: { - tables: [ - { - columns: [ - { - id: 'col-0-1', - name: 'Count', - }, - ], - rows: [ - { - 'col-0-1': 1000, - }, - ], - }, - ], - }, - threeTermBuckets: { - tables: [ - { - columns: [ - { - id: 'col-0-agg_2', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_1', - name: 'Average bytes', - }, - { - id: 'col-2-agg_3', - name: 'geo.src: Descending', - }, - { - id: 'col-3-agg_1', - name: 'Average bytes', - }, - { - id: 'col-4-agg_4', - name: 'machine.os: Descending', - }, - { - id: 'col-5-agg_1', - name: 'Average bytes', - }, - ], - rows: [ - { - 'col-0-agg_2': 'png', - 'col-2-agg_3': 'IT', - 'col-4-agg_4': 'win', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 9299, - 'col-5-agg_1': 0, - }, - { - 'col-0-agg_2': 'png', - 'col-2-agg_3': 'IT', - 'col-4-agg_4': 'mac', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 9299, - 'col-5-agg_1': 9299, - }, - { - 'col-0-agg_2': 'png', - 'col-2-agg_3': 'US', - 'col-4-agg_4': 'linux', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 8293, - 'col-5-agg_1': 3992, - }, - { - 'col-0-agg_2': 'png', - 'col-2-agg_3': 'US', - 'col-4-agg_4': 'mac', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 8293, - 'col-5-agg_1': 3029, - }, - { - 'col-0-agg_2': 'css', - 'col-2-agg_3': 'MX', - 'col-4-agg_4': 'win', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 9299, - 'col-5-agg_1': 4992, - }, - { - 'col-0-agg_2': 'css', - 'col-2-agg_3': 'MX', - 'col-4-agg_4': 'mac', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 9299, - 'col-5-agg_1': 5892, - }, - { - 'col-0-agg_2': 'css', - 'col-2-agg_3': 'US', - 'col-4-agg_4': 'linux', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 8293, - 'col-5-agg_1': 3992, - }, - { - 'col-0-agg_2': 'css', - 'col-2-agg_3': 'US', - 'col-4-agg_4': 'mac', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 8293, - 'col-5-agg_1': 3029, - }, - { - 'col-0-agg_2': 'html', - 'col-2-agg_3': 'CN', - 'col-4-agg_4': 'win', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 9299, - 'col-5-agg_1': 4992, - }, - { - 'col-0-agg_2': 'html', - 'col-2-agg_3': 'CN', - 'col-4-agg_4': 'mac', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 9299, - 'col-5-agg_1': 5892, - }, - { - 'col-0-agg_2': 'html', - 'col-2-agg_3': 'FR', - 'col-4-agg_4': 'win', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 8293, - 'col-5-agg_1': 3992, - }, - { - 'col-0-agg_2': 'html', - 'col-2-agg_3': 'FR', - 'col-4-agg_4': 'mac', - 'col-1-agg_1': 412032, - 'col-3-agg_1': 8293, - 'col-5-agg_1': 3029, - }, - ], - }, - ], - }, - threeTermBucketsWithSplit: { - tables: [ - { - title: 'png: extension: Descending', - name: 'extension: Descending', - key: 'png', - column: 0, - row: 0, - table: { - columns: [ - { - id: 'col-0-agg_2', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_3', - name: 'geo.src: Descending', - }, - { - id: 'col-2-agg_4', - name: 'machine.os: Descending', - }, - { - id: 'col-3-agg_1', - name: 'Average bytes', - }, - ], - rows: [ - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 0, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 9299, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - ], - }, - tables: [ - { - columns: [ - { - id: 'col-0-agg_2', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_3', - name: 'geo.src: Descending', - }, - { - id: 'col-2-agg_4', - name: 'machine.os: Descending', - }, - { - id: 'col-3-agg_1', - name: 'Average bytes', - }, - ], - rows: [ - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 0, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 9299, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - ], - }, - ], - }, - { - title: 'css: extension: Descending', - name: 'extension: Descending', - key: 'css', - column: 0, - row: 4, - table: { - columns: [ - { - id: 'col-0-agg_2', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_3', - name: 'geo.src: Descending', - }, - { - id: 'col-2-agg_4', - name: 'machine.os: Descending', - }, - { - id: 'col-3-agg_1', - name: 'Average bytes', - }, - ], - rows: [ - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 0, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 9299, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - ], - }, - tables: [ - { - columns: [ - { - id: 'col-0-agg_2', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_3', - name: 'geo.src: Descending', - }, - { - id: 'col-2-agg_4', - name: 'machine.os: Descending', - }, - { - id: 'col-3-agg_1', - name: 'Average bytes', - }, - ], - rows: [ - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - ], - }, - ], - }, - { - title: 'html: extension: Descending', - name: 'extension: Descending', - key: 'html', - column: 0, - row: 8, - table: { - columns: [ - { - id: 'col-0-agg_2', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_3', - name: 'geo.src: Descending', - }, - { - id: 'col-2-agg_4', - name: 'machine.os: Descending', - }, - { - id: 'col-3-agg_1', - name: 'Average bytes', - }, - ], - rows: [ - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 0, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'IT', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 9299, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'png', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'MX', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'linux', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'css', - 'col-1-agg_3': 'US', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - ], - }, - tables: [ - { - columns: [ - { - id: 'col-0-agg_2', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_3', - name: 'geo.src: Descending', - }, - { - id: 'col-2-agg_4', - name: 'machine.os: Descending', - }, - { - id: 'col-3-agg_1', - name: 'Average bytes', - }, - ], - rows: [ - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 4992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'CN', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 5892, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'win', - 'col-3-agg_1': 3992, - }, - { - 'col-0-agg_2': 'html', - 'col-1-agg_3': 'FR', - 'col-2-agg_4': 'mac', - 'col-3-agg_1': 3029, - }, - ], - }, - ], - }, - ], - direction: 'row', - }, - oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative: { - tables: [ - { - columns: [ - { - id: 'col-0-agg_3', - name: 'extension: Descending', - }, - { - id: 'col-1-agg_4', - name: '@timestamp per day', - }, - { - id: 'col-2-agg_1', - name: 'Average bytes', - }, - { - id: 'col-3-agg_2', - name: 'Min @timestamp', - }, - { - id: 'col-4-agg_5', - name: 'Derivative of Count', - }, - { - id: 'col-5-agg_6', - name: 'Last bytes', - }, - ], - rows: [ - { - 'col-0-agg_3': 'png', - 'col-1-agg_4': 1411862400000, - 'col-2-agg_1': 9283, - 'col-3-agg_2': 1411862400000, - 'col-5-agg_6': 23, - }, - { - 'col-0-agg_3': 'png', - 'col-1-agg_4': 1411948800000, - 'col-2-agg_1': 28349, - 'col-3-agg_2': 1411948800000, - 'col-4-agg_5': 203, - 'col-5-agg_6': 39, - }, - { - 'col-0-agg_3': 'png', - 'col-1-agg_4': 1412035200000, - 'col-2-agg_1': 84330, - 'col-3-agg_2': 1412035200000, - 'col-4-agg_5': 200, - 'col-5-agg_6': 329, - }, - { - 'col-0-agg_3': 'png', - 'col-1-agg_4': 1412121600000, - 'col-2-agg_1': 34992, - 'col-3-agg_2': 1412121600000, - 'col-4-agg_5': 103, - 'col-5-agg_6': 22, - }, - { - 'col-0-agg_3': 'png', - 'col-1-agg_4': 1412208000000, - 'col-2-agg_1': 145432, - 'col-3-agg_2': 1412208000000, - 'col-4-agg_5': 153, - 'col-5-agg_6': 93, - }, - { - 'col-0-agg_3': 'png', - 'col-1-agg_4': 1412294400000, - 'col-2-agg_1': 220943, - 'col-3-agg_2': 1412294400000, - 'col-4-agg_5': 239, - 'col-5-agg_6': 72, - }, - { - 'col-0-agg_3': 'css', - 'col-1-agg_4': 1411862400000, - 'col-2-agg_1': 9283, - 'col-3-agg_2': 1411862400000, - 'col-5-agg_6': 75, - }, - { - 'col-0-agg_3': 'css', - 'col-1-agg_4': 1411948800000, - 'col-2-agg_1': 28349, - 'col-3-agg_2': 1411948800000, - 'col-4-agg_5': 10, - 'col-5-agg_6': 11, - }, - { - 'col-0-agg_3': 'css', - 'col-1-agg_4': 1412035200000, - 'col-2-agg_1': 84330, - 'col-3-agg_2': 1412035200000, - 'col-4-agg_5': 24, - 'col-5-agg_6': 238, - }, - { - 'col-0-agg_3': 'css', - 'col-1-agg_4': 1412121600000, - 'col-2-agg_1': 34992, - 'col-3-agg_2': 1412121600000, - 'col-4-agg_5': 49, - 'col-5-agg_6': 343, - }, - { - 'col-0-agg_3': 'css', - 'col-1-agg_4': 1412208000000, - 'col-2-agg_1': 145432, - 'col-3-agg_2': 1412208000000, - 'col-4-agg_5': 100, - 'col-5-agg_6': 837, - }, - { - 'col-0-agg_3': 'css', - 'col-1-agg_4': 1412294400000, - 'col-2-agg_1': 220943, - 'col-3-agg_2': 1412294400000, - 'col-4-agg_5': 23, - 'col-5-agg_6': 302, - }, - { - 'col-0-agg_3': 'html', - 'col-1-agg_4': 1411862400000, - 'col-2-agg_1': 9283, - 'col-3-agg_2': 1411862400000, - 'col-5-agg_6': 30, - }, - { - 'col-0-agg_3': 'html', - 'col-1-agg_4': 1411948800000, - 'col-2-agg_1': 28349, - 'col-3-agg_2': 1411948800000, - 'col-4-agg_5': 1, - 'col-5-agg_6': 43, - }, - { - 'col-0-agg_3': 'html', - 'col-1-agg_4': 1412035200000, - 'col-2-agg_1': 84330, - 'col-3-agg_2': 1412035200000, - 'col-4-agg_5': 5, - 'col-5-agg_6': 88, - }, - { - 'col-0-agg_3': 'html', - 'col-1-agg_4': 1412121600000, - 'col-2-agg_1': 34992, - 'col-3-agg_2': 1412121600000, - 'col-4-agg_5': 10, - 'col-5-agg_6': 91, - }, - { - 'col-0-agg_3': 'html', - 'col-1-agg_4': 1412208000000, - 'col-2-agg_1': 145432, - 'col-3-agg_2': 1412208000000, - 'col-4-agg_5': 43, - 'col-5-agg_6': 534, - }, - { - 'col-0-agg_3': 'html', - 'col-1-agg_4': 1412294400000, - 'col-2-agg_1': 220943, - 'col-3-agg_2': 1412294400000, - 'col-4-agg_5': 1, - 'col-5-agg_6': 553, - }, - ], - }, - ], - }, -}; diff --git a/src/plugins/vis_type_table_new/public/components/table_vis_app.scss b/src/plugins/vis_type_table/public/components/table_vis_app.scss similarity index 100% rename from src/plugins/vis_type_table_new/public/components/table_vis_app.scss rename to src/plugins/vis_type_table/public/components/table_vis_app.scss diff --git a/src/plugins/vis_type_table_new/public/components/table_vis_app.tsx b/src/plugins/vis_type_table/public/components/table_vis_app.tsx similarity index 94% rename from src/plugins/vis_type_table_new/public/components/table_vis_app.tsx rename to src/plugins/vis_type_table/public/components/table_vis_app.tsx index 7958b2187620..af10500a1a92 100644 --- a/src/plugins/vis_type_table_new/public/components/table_vis_app.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_app.tsx @@ -11,7 +11,7 @@ import { I18nProvider } from '@osd/i18n/react'; import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { OpenSearchDashboardsContextProvider } from '../../../opensearch_dashboards_react/public'; import { TableContext } from '../table_vis_response_handler'; -import { TableVisConfig, SortColumn, ColumnWidth, TableUiState } from '../types'; +import { TableVisConfig, ColumnSort, ColumnWidth, TableUiState } from '../types'; import { TableVisComponent } from './table_vis_component'; import { TableVisComponentGroup } from './table_vis_component_group'; @@ -40,7 +40,7 @@ export const TableVisApp = ({ // TODO: remove duplicate sort and width state // Issue: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2704#issuecomment-1299380818 - const [sort, setSort] = useState<SortColumn>({ colIndex: null, direction: null }); + const [sort, setSort] = useState<ColumnSort>({ colIndex: null, direction: null }); const [width, setWidth] = useState<ColumnWidth[]>([]); const tableUiState: TableUiState = { sort, setSort, width, setWidth }; diff --git a/src/plugins/vis_type_table_new/public/components/table_vis_component.tsx b/src/plugins/vis_type_table/public/components/table_vis_component.tsx similarity index 79% rename from src/plugins/vis_type_table_new/public/components/table_vis_component.tsx rename to src/plugins/vis_type_table/public/components/table_vis_component.tsx index 4a25395703b0..4576e3420e22 100644 --- a/src/plugins/vis_type_table_new/public/components/table_vis_component.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_component.tsx @@ -5,11 +5,12 @@ import React, { useCallback, useMemo } from 'react'; import { orderBy } from 'lodash'; +import dompurify from 'dompurify'; import { EuiDataGridProps, EuiDataGrid, EuiDataGridSorting, EuiTitle } from '@elastic/eui'; import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { Table } from '../table_vis_response_handler'; -import { TableVisConfig, ColumnWidth, SortColumn, TableUiState } from '../types'; +import { TableVisConfig, ColumnWidth, ColumnSort, TableUiState } from '../types'; import { getDataGridColumns } from './table_vis_grid_columns'; import { usePagination } from '../utils'; import { convertToFormattedData } from '../utils/convert_to_formatted_data'; @@ -49,10 +50,18 @@ export const TableVisComponent = ({ return (({ rowIndex, columnId }) => { const rawContent = sortedRows[rowIndex][columnId]; const colIndex = columns.findIndex((col) => col.id === columnId); - const column = columns[colIndex]; - // use formatter to format raw content - // this can format date and percentage data - const formattedContent = column.formatter.convert(rawContent, 'text'); + const htmlContent = columns[colIndex].formatter.convert(rawContent, 'html'); + const formattedContent = ( + /* + * Justification for dangerouslySetInnerHTML: + * This is one of the visualizations which makes use of the HTML field formatters. + * Since these formatters produce raw HTML, this visualization needs to be able to render them as-is, relying + * on the field formatter to only produce safe HTML. + * `htmlContent` is created by converting raw data via HTML field formatter, so we need to make sure this value never contains + * any unsafe HTML (e.g. by bypassing the field formatter). + */ + <div dangerouslySetInnerHTML={{ __html: dompurify.sanitize(htmlContent) }} /> // eslint-disable-line react/no-danger + ); return sortedRows.hasOwnProperty(rowIndex) ? formattedContent || null : null; }) as EuiDataGridProps['renderCellValue']; }, [sortedRows, columns]); @@ -70,7 +79,7 @@ export const TableVisComponent = ({ const onSort = useCallback( (sortingCols: EuiDataGridSorting['columns'] | []) => { const nextSortValue = sortingCols[sortingCols.length - 1]; - const nextSort: SortColumn = + const nextSort: ColumnSort = sortingCols.length > 0 ? { colIndex: dataGridColumns.findIndex((col) => col.id === nextSortValue?.id), @@ -107,6 +116,12 @@ export const TableVisComponent = ({ const ariaLabel = title || visConfig.title || 'tableVis'; + const footerCellValue = visConfig.showTotal + ? ({ columnId }: { columnId: any }) => { + return columns.find((col) => col.id === columnId)?.formattedTotal || null; + } + : undefined; + return ( <> {title && ( @@ -131,6 +146,7 @@ export const TableVisComponent = ({ header: 'underline', }} minSizeForControls={1} + renderFooterCellValue={footerCellValue} toolbarVisibility={{ showColumnSelector: false, showSortSelector: false, diff --git a/src/plugins/vis_type_table_new/public/components/table_vis_component_group.tsx b/src/plugins/vis_type_table/public/components/table_vis_component_group.tsx similarity index 100% rename from src/plugins/vis_type_table_new/public/components/table_vis_component_group.tsx rename to src/plugins/vis_type_table/public/components/table_vis_component_group.tsx diff --git a/src/plugins/vis_type_table_new/public/components/table_vis_control.tsx b/src/plugins/vis_type_table/public/components/table_vis_control.tsx similarity index 93% rename from src/plugins/vis_type_table_new/public/components/table_vis_control.tsx rename to src/plugins/vis_type_table/public/components/table_vis_control.tsx index 26b51c9cc85b..1e11610b0c23 100644 --- a/src/plugins/vis_type_table_new/public/components/table_vis_control.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_control.tsx @@ -9,7 +9,7 @@ import { OpenSearchDashboardsDatatableRow } from 'src/plugins/expressions'; import { CoreStart } from 'opensearch-dashboards/public'; import { exportAsCsv } from '../utils/convert_to_csv_data'; import { FormattedColumn } from '../types'; -import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; +import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public'; interface TableVisControlProps { filename?: string; diff --git a/src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx b/src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx new file mode 100644 index 000000000000..77d496a1eb43 --- /dev/null +++ b/src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx @@ -0,0 +1,148 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { EuiDataGridColumn, EuiDataGridColumnCellActionProps } from '@elastic/eui'; +import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import { OpenSearchDashboardsDatatableRow } from 'src/plugins/expressions'; +import { Table } from '../table_vis_response_handler'; +import { ColumnWidth, FormattedColumn } from '../types'; + +export const getDataGridColumns = ( + rows: OpenSearchDashboardsDatatableRow[], + cols: FormattedColumn[], + table: Table, + event: IInterpreterRenderHandlers['event'], + columnWidths: ColumnWidth[] +) => { + const filterBucket = (rowIndex: number, columnIndex: number, negate: boolean) => { + const foramttedColumnId = cols[columnIndex].id; + const rawColumnIndex = table.columns.findIndex((col) => col.id === foramttedColumnId); + event({ + name: 'filterBucket', + data: { + data: [ + { + table: { + columns: table.columns, + rows, + }, + row: rowIndex, + column: rawColumnIndex, + }, + ], + negate, + }, + }); + }; + + return cols.map((col, colIndex) => { + const cellActions = col.filterable + ? [ + ({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => { + const filterValue = rows[rowIndex][columnId]; + const filterContent = col.formatter?.convert(filterValue); + + const filterForValueText = i18n.translate( + 'visTypeTable.tableVisFilter.filterForValue', + { + defaultMessage: 'Filter for value', + } + ); + const filterForValueLabel = i18n.translate( + 'visTypeTable.tableVisFilter.filterForValueLabel', + { + defaultMessage: 'Filter for value: {filterContent}', + values: { + filterContent, + }, + } + ); + + return ( + filterValue != null && ( + <Component + onClick={() => { + filterBucket(rowIndex, colIndex, false); + closePopover(); + }} + iconType="plusInCircle" + aria-label={filterForValueLabel} + data-test-subj="filterForValue" + > + {filterForValueText} + </Component> + ) + ); + }, + ({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => { + const filterValue = rows[rowIndex][columnId]; + const filterContent = col.formatter?.convert(filterValue); + + const filterOutValueText = i18n.translate( + 'visTypeTable.tableVisFilter.filterOutValue', + { + defaultMessage: 'Filter out value', + } + ); + const filterOutValueLabel = i18n.translate( + 'visTypeTable.tableVisFilter.filterOutValueLabel', + { + defaultMessage: 'Filter out value: {filterContent}', + values: { + filterContent, + }, + } + ); + + return ( + filterValue != null && ( + <Component + onClick={() => { + filterBucket(rowIndex, colIndex, true); + closePopover(); + }} + iconType="minusInCircle" + aria-label={filterOutValueLabel} + data-test-subj="filterOutValue" + > + {filterOutValueText} + </Component> + ) + ); + }, + ] + : undefined; + + const initialWidth = columnWidths.find((c) => c.colIndex === colIndex); + + const dataGridColumn: EuiDataGridColumn = { + id: col.id, + display: col.title, + displayAsText: col.title, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: { + label: i18n.translate('visTypeTable.tableVisSort.ascSortLabel', { + defaultMessage: 'Sort asc', + }), + }, + showSortDesc: { + label: i18n.translate('visTypeTable.tableVisSort.descSortLabel', { + defaultMessage: 'Sort desc', + }), + }, + }, + cellActions, + }; + if (initialWidth) { + dataGridColumn.initialWidth = initialWidth.width; + } + return dataGridColumn; + }); +}; diff --git a/src/plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx index e4cebe69fb4f..ff99972c83a5 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx @@ -132,6 +132,7 @@ function TableOptions({ paramName="showTotal" value={stateParams.showTotal} setValue={setValue} + data-test-subj="showTotal" /> <SelectOption @@ -143,6 +144,7 @@ function TableOptions({ paramName="totalFunc" value={stateParams.totalFunc} setValue={setValue} + data-test-subj="totalFunctionOptions" /> <SelectOption @@ -153,7 +155,7 @@ function TableOptions({ paramName="percentageCol" value={stateParams.percentageCol} setValue={setValue} - id="datatableVisualizationPercentageCol" + data-test-subj="datatableVisualizationPercentageCol" /> </EuiPanel> ); diff --git a/src/plugins/vis_type_table/public/get_inner_angular.ts b/src/plugins/vis_type_table/public/get_inner_angular.ts deleted file mode 100644 index 7f42984d7c02..000000000000 --- a/src/plugins/vis_type_table/public/get_inner_angular.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -// inner angular imports -// these are necessary to bootstrap the local angular. -// They can stay even after NP cutover -import angular from 'angular'; -// required for `ngSanitize` angular module -import 'angular-sanitize'; -import 'angular-recursion'; -import { i18nDirective, i18nFilter, I18nProvider } from '@osd/i18n/angular'; -import { - CoreStart, - IUiSettingsClient, - PluginInitializerContext, -} from 'opensearch-dashboards/public'; -import { - initAngularBootstrap, - PaginateDirectiveProvider, - PaginateControlsDirectiveProvider, - PrivateProvider, - watchMultiDecorator, - OsdAccessibleClickProvider, -} from '../../opensearch_dashboards_legacy/public'; - -initAngularBootstrap(); - -const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper']; - -export function getAngularModule(name: string, core: CoreStart, context: PluginInitializerContext) { - const uiModule = getInnerAngular(name, core); - return uiModule; -} - -let initialized = false; - -export function getInnerAngular(name = 'opensearch-dashboards/table_vis', core: CoreStart) { - if (!initialized) { - createLocalPrivateModule(); - createLocalI18nModule(); - createLocalConfigModule(core.uiSettings); - createLocalPaginateModule(); - initialized = true; - } - return angular - .module(name, [ - ...thirdPartyAngularDependencies, - 'tableVisPaginate', - 'tableVisConfig', - 'tableVisPrivate', - 'tableVisI18n', - ]) - .config(watchMultiDecorator) - .directive('osdAccessibleClick', OsdAccessibleClickProvider); -} - -function createLocalPrivateModule() { - angular.module('tableVisPrivate', []).provider('Private', PrivateProvider); -} - -function createLocalConfigModule(uiSettings: IUiSettingsClient) { - angular.module('tableVisConfig', []).provider('config', function () { - return { - $get: () => ({ - get: (value: string) => { - return uiSettings ? uiSettings.get(value) : undefined; - }, - // set method is used in agg_table mocha test - set: (key: string, value: string) => { - return uiSettings ? uiSettings.set(key, value) : undefined; - }, - }), - }; - }); -} - -function createLocalI18nModule() { - angular - .module('tableVisI18n', []) - .provider('i18n', I18nProvider) - .filter('i18n', i18nFilter) - .directive('i18nId', i18nDirective); -} - -function createLocalPaginateModule() { - angular - .module('tableVisPaginate', []) - .directive('paginate', PaginateDirectiveProvider) - .directive('paginateControls', PaginateControlsDirectiveProvider); -} diff --git a/src/plugins/vis_type_table/public/index.scss b/src/plugins/vis_type_table/public/index.scss deleted file mode 100644 index d21bf5262602..000000000000 --- a/src/plugins/vis_type_table/public/index.scss +++ /dev/null @@ -1,10 +0,0 @@ -// Prefix all styles with "tbv" to avoid conflicts. -// Examples -// tbvChart -// tbvChart__legend -// tbvChart__legend--small -// tbvChart__legend-isLoading - -@import "./agg_table/index"; -@import "./paginated_table/index"; -@import "./table_vis"; diff --git a/src/plugins/vis_type_table/public/index.ts b/src/plugins/vis_type_table/public/index.ts index dc7e7a16b6ce..774af319b920 100644 --- a/src/plugins/vis_type_table/public/index.ts +++ b/src/plugins/vis_type_table/public/index.ts @@ -1,37 +1,14 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ -/* - * 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 './index.scss'; import { PluginInitializerContext } from 'opensearch-dashboards/public'; import { TableVisPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new Plugin(initializerContext); } + +/* Public Types */ +export { TableVisExpressionFunctionDefinition } from './table_vis_fn'; diff --git a/src/plugins/vis_type_table/public/paginated_table/_index.scss b/src/plugins/vis_type_table/public/paginated_table/_index.scss deleted file mode 100644 index 66275b5c7da8..000000000000 --- a/src/plugins/vis_type_table/public/paginated_table/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./table_cell_filter"; diff --git a/src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss b/src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss deleted file mode 100644 index 3deece36b2c6..000000000000 --- a/src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss +++ /dev/null @@ -1,30 +0,0 @@ -.osdTableCellFilter__hover { - position: relative; - - /** - * 1. Center vertically regardless of row height. - */ - .osdTableCellFilter { - position: absolute; - white-space: nowrap; - right: 0; - top: 50%; /* 1 */ - transform: translateY(-50%); /* 1 */ - display: none; - } - - &:hover { - .osdTableCellFilter { - display: inline; - } - - .osdTableCellFilter__hover-show { - visibility: visible; - } - } -} - -.osdTableCellFilter__hover-show { - // so that the cell doesn't change size on hover - visibility: hidden; -} diff --git a/src/plugins/vis_type_table/public/paginated_table/paginated_table.html b/src/plugins/vis_type_table/public/paginated_table/paginated_table.html deleted file mode 100644 index 83de29a12737..000000000000 --- a/src/plugins/vis_type_table/public/paginated_table/paginated_table.html +++ /dev/null @@ -1,55 +0,0 @@ -<paginate - ng-if="sortedRows.length" - list="sortedRows" - per-page-prop="perPage" - class="osdAggTable"> - <div class="osdAggTable__paginated"> - <table class="table table-condensed"> - <thead data-test-subj="paginated-table-header"> - <tr> - <th - scope="col" - ng-repeat="col in columns" - ng-click="paginatedTable.sortColumn($index)" - osd-accessible-click - tabindex="0" - class="{{ col.class }}"> - <span ng-bind="::col.title"></span> - - <icon-tip - ng-if="col.info" - content="'{{ col.info }}'" - ></icon-tip> - - <i - ng-if="col.sortable !== false" - class="fa" - ng-class="{ - 'fa-sort-asc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'asc', - 'fa-sort-desc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'desc', - 'fa-sort': paginatedTable.sort.columnIndex !== $index || paginatedTable.sort.direction === null - }"> - </i> - </th> - </tr> - </thead> - <tbody - data-test-subj="paginated-table-body" - osd-rows="page" - osd-rows-min="page.length" - > - </tbody> - <tfoot ng-if="showTotal"> - <tr> - <th scope="col" ng-repeat="col in columns" class="numeric-value"> - {{ col.formattedTotal }} - </th> - </tr> - </tfoot> - </table> - </div> - - <!-- auto-inserted by the paginate directive... --> - <!-- <paginate-controls></paginate-controls> --> - <div class="pagination-container" ng-transclude></div> -</paginate> diff --git a/src/plugins/vis_type_table/public/paginated_table/paginated_table.js b/src/plugins/vis_type_table/public/paginated_table/paginated_table.js deleted file mode 100644 index c97fa6c26587..000000000000 --- a/src/plugins/vis_type_table/public/paginated_table/paginated_table.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; -import paginatedTableTemplate from './paginated_table.html'; - -export function PaginatedTable($filter) { - const orderBy = $filter('orderBy'); - - return { - restrict: 'E', - template: paginatedTableTemplate, - transclude: true, - scope: { - table: '=', - rows: '=', - columns: '=', - linkToTop: '=', - perPage: '=?', - sortHandler: '=?', - sort: '=?', - showSelector: '=?', - showTotal: '=', - totalFunc: '=', - filter: '=', - percentageCol: '=', - }, - controllerAs: 'paginatedTable', - controller: function ($scope) { - const self = this; - self.sort = { - columnIndex: null, - direction: null, - }; - - self.sortColumn = function (colIndex, sortDirection = 'asc') { - const col = $scope.columns[colIndex]; - - if (!col) return; - if (col.sortable === false) return; - - if (self.sort.columnIndex === colIndex) { - const directions = { - null: 'asc', - asc: 'desc', - desc: null, - }; - sortDirection = directions[self.sort.direction]; - } - - self.sort.columnIndex = colIndex; - self.sort.direction = sortDirection; - if ($scope.sort) { - _.assign($scope.sort, self.sort); - } - }; - - function valueGetter(row) { - const col = $scope.columns[self.sort.columnIndex]; - let value = row[col.id]; - if (typeof value === 'boolean') value = value ? 0 : 1; - return value; - } - - // Set the sort state if it is set - if ($scope.sort && $scope.sort.columnIndex !== null) { - self.sortColumn($scope.sort.columnIndex, $scope.sort.direction); - } - - function resortRows() { - const newSort = $scope.sort; - if (newSort && !_.isEqual(newSort, self.sort)) { - self.sortColumn(newSort.columnIndex, newSort.direction); - } - - if (!$scope.rows || !$scope.columns) { - $scope.sortedRows = false; - return; - } - - const sort = self.sort; - if (sort.direction == null) { - $scope.sortedRows = $scope.rows.slice(0); - } else { - $scope.sortedRows = orderBy($scope.rows, valueGetter, sort.direction === 'desc'); - } - } - - // update the sortedRows result - $scope.$watchMulti(['rows', 'columns', '[]sort', '[]paginatedTable.sort'], resortRows); - }, - }; -} diff --git a/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts b/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts deleted file mode 100644 index cc86bda46573..000000000000 --- a/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts +++ /dev/null @@ -1,485 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { isNumber, times, identity, random } from 'lodash'; -import angular, { IRootScopeService, IScope, ICompileService } from 'angular'; -import $ from 'jquery'; -import 'angular-sanitize'; -import 'angular-mocks'; - -import { getAngularModule } from '../get_inner_angular'; -import { initTableVisLegacyModule } from '../table_vis_legacy_module'; -import { coreMock } from '../../../../core/public/mocks'; - -jest.mock('../../../opensearch_dashboards_legacy/public/angular/angular_config', () => ({ - configureAppAngularModule: () => {}, -})); - -interface Sort { - columnIndex: number; - direction: string; -} - -interface Row { - [key: string]: number | string; -} - -interface Column { - id?: string; - title: string; - formatter?: { - convert?: (val: string) => string; - }; - sortable?: boolean; -} - -interface Table { - columns: Column[]; - rows: Row[]; -} - -interface PaginatedTableScope extends IScope { - table?: Table; - cols?: Column[]; - rows?: Row[]; - perPage?: number; - sort?: Sort; - linkToTop?: boolean; -} - -describe('Table Vis - Paginated table', () => { - let $el: JQuery<Element>; - let $rootScope: IRootScopeService; - let $compile: ICompileService; - let $scope: PaginatedTableScope; - const defaultPerPage = 10; - let paginatedTable: any; - - const initLocalAngular = () => { - const tableVisModule = getAngularModule( - 'opensearch-dashboards/table_vis', - coreMock.createStart(), - coreMock.createPluginInitializerContext() - ); - initTableVisLegacyModule(tableVisModule); - }; - - beforeEach(initLocalAngular); - beforeEach(angular.mock.module('opensearch-dashboards/table_vis')); - - beforeEach( - angular.mock.inject((_$rootScope_: IRootScopeService, _$compile_: ICompileService) => { - $rootScope = _$rootScope_; - $compile = _$compile_; - $scope = $rootScope.$new(); - }) - ); - - afterEach(() => { - $scope.$destroy(); - }); - - const makeData = (colCount: number | Column[], rowCount: number | string[][]) => { - let columns: Column[] = []; - let rows: Row[] = []; - - if (isNumber(colCount)) { - times(colCount, (i) => { - columns.push({ id: `${i}`, title: `column${i}`, formatter: { convert: identity } }); - }); - } else { - columns = colCount.map( - (col, i) => - ({ - id: `${i}`, - title: col.title, - formatter: col.formatter || { convert: identity }, - } as Column) - ); - } - - if (isNumber(rowCount)) { - times(rowCount, (row) => { - const rowItems: Row = {}; - - times(columns.length, (col) => { - rowItems[`${col}`] = `item-${col}-${row}`; - }); - - rows.push(rowItems); - }); - } else { - rows = rowCount.map((row: string[]) => { - const newRow: Row = {}; - row.forEach((v, i) => (newRow[i] = v)); - return newRow; - }); - } - - return { - columns, - rows, - }; - }; - - const renderTable = ( - table: { columns: Column[]; rows: Row[] } | null, - cols: Column[], - rows: Row[], - perPage?: number, - sort?: Sort, - linkToTop?: boolean - ) => { - $scope.table = table || { columns: [], rows: [] }; - $scope.cols = cols || []; - $scope.rows = rows || []; - $scope.perPage = perPage || defaultPerPage; - $scope.sort = sort; - $scope.linkToTop = linkToTop; - - const template = ` - <paginated-table - table="table" - columns="cols" - rows="rows" - per-page="perPage" - sort="sort" - link-to-top="linkToTop">`; - const element = $compile(template)($scope); - $el = $(element); - - $scope.$digest(); - paginatedTable = element.controller('paginatedTable'); - }; - - describe('rendering', () => { - test('should not display without rows', () => { - const cols: Column[] = [ - { - id: 'col-1-1', - title: 'test1', - }, - ]; - const rows: Row[] = []; - - renderTable(null, cols, rows); - expect($el.children().length).toBe(0); - }); - - test('should render columns and rows', () => { - const data = makeData(2, 2); - const cols = data.columns; - const rows = data.rows; - - renderTable(data, cols, rows); - expect($el.children().length).toBe(1); - const tableRows = $el.find('tbody tr'); - - // should contain the row data - expect(tableRows.eq(0).find('td').eq(0).text()).toBe(rows[0][0]); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe(rows[0][1]); - expect(tableRows.eq(1).find('td').eq(0).text()).toBe(rows[1][0]); - expect(tableRows.eq(1).find('td').eq(1).text()).toBe(rows[1][1]); - }); - - test('should paginate rows', () => { - // note: paginate truncates pages, so don't make too many - const rowCount = random(16, 24); - const perPageCount = random(5, 8); - const data = makeData(3, rowCount); - const pageCount = Math.ceil(rowCount / perPageCount); - - renderTable(data, data.columns, data.rows, perPageCount); - const tableRows = $el.find('tbody tr'); - expect(tableRows.length).toBe(perPageCount); - // add 2 for the first and last page links - expect($el.find('paginate-controls button').length).toBe(pageCount + 2); - }); - - test('should not show blank rows on last page', () => { - const rowCount = 7; - const perPageCount = 10; - const data = makeData(3, rowCount); - - renderTable(data, data.columns, data.rows, perPageCount); - const tableRows = $el.find('tbody tr'); - expect(tableRows.length).toBe(rowCount); - }); - - test('should not show link to top when not set', () => { - const data = makeData(5, 5); - renderTable(data, data.columns, data.rows, 10); - - const linkToTop = $el.find('[data-test-subj="paginateControlsLinkToTop"]'); - expect(linkToTop.length).toBe(0); - }); - - test('should show link to top when set', () => { - const data = makeData(5, 5); - renderTable(data, data.columns, data.rows, 10, undefined, true); - - const linkToTop = $el.find('[data-test-subj="paginateControlsLinkToTop"]'); - expect(linkToTop.length).toBe(1); - }); - }); - - describe('sorting', () => { - let data: Table; - let lastRowIndex: number; - - beforeEach(() => { - data = makeData(3, [ - ['bbbb', 'aaaa', 'zzzz'], - ['cccc', 'cccc', 'aaaa'], - ['zzzz', 'bbbb', 'bbbb'], - ['aaaa', 'zzzz', 'cccc'], - ]); - - lastRowIndex = data.rows.length - 1; - renderTable(data, data.columns, data.rows); - }); - - test('should not sort by default', () => { - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe(data.rows[0][0]); - expect(tableRows.eq(lastRowIndex).find('td').eq(0).text()).toBe(data.rows[lastRowIndex][0]); - }); - - test('should do nothing when sorting by invalid column id', () => { - // sortColumn - paginatedTable.sortColumn(999); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe('bbbb'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('aaaa'); - expect(tableRows.eq(0).find('td').eq(2).text()).toBe('zzzz'); - }); - - test('should do nothing when sorting by non sortable column', () => { - data.columns[0].sortable = false; - - // sortColumn - paginatedTable.sortColumn(0); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe('bbbb'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('aaaa'); - expect(tableRows.eq(0).find('td').eq(2).text()).toBe('zzzz'); - }); - - test("should set the sort direction to asc when it's not explicitly set", () => { - paginatedTable.sortColumn(1); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(2).find('td').eq(1).text()).toBe('cccc'); - expect(tableRows.eq(1).find('td').eq(1).text()).toBe('bbbb'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('aaaa'); - }); - - test('should allow you to explicitly set the sort direction', () => { - paginatedTable.sortColumn(1, 'desc'); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('zzzz'); - expect(tableRows.eq(1).find('td').eq(1).text()).toBe('cccc'); - expect(tableRows.eq(2).find('td').eq(1).text()).toBe('bbbb'); - }); - - test('should sort ascending on first invocation', () => { - // sortColumn - paginatedTable.sortColumn(0); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe('aaaa'); - expect(tableRows.eq(lastRowIndex).find('td').eq(0).text()).toBe('zzzz'); - }); - - test('should sort descending on second invocation', () => { - // sortColumn - paginatedTable.sortColumn(0); - paginatedTable.sortColumn(0); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe('zzzz'); - expect(tableRows.eq(lastRowIndex).find('td').eq(0).text()).toBe('aaaa'); - }); - - test('should clear sorting on third invocation', () => { - // sortColumn - paginatedTable.sortColumn(0); - paginatedTable.sortColumn(0); - paginatedTable.sortColumn(0); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe(data.rows[0][0]); - expect(tableRows.eq(lastRowIndex).find('td').eq(0).text()).toBe('aaaa'); - }); - - test('should sort new column ascending', () => { - // sort by first column - paginatedTable.sortColumn(0); - $scope.$digest(); - - // sort by second column - paginatedTable.sortColumn(1); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('aaaa'); - expect(tableRows.eq(lastRowIndex).find('td').eq(1).text()).toBe('zzzz'); - }); - }); - - describe('sorting duplicate columns', () => { - let data; - const colText = 'test row'; - - beforeEach(() => { - const cols: Column[] = [{ title: colText }, { title: colText }, { title: colText }]; - const rows = [ - ['bbbb', 'aaaa', 'zzzz'], - ['cccc', 'cccc', 'aaaa'], - ['zzzz', 'bbbb', 'bbbb'], - ['aaaa', 'zzzz', 'cccc'], - ]; - data = makeData(cols, rows); - - renderTable(data, data.columns, data.rows); - }); - - test('should have duplicate column titles', () => { - const columns = $el.find('thead th span'); - columns.each((i, col) => { - expect($(col).text()).toBe(colText); - }); - }); - - test('should handle sorting on columns with the same name', () => { - // sort by the last column - paginatedTable.sortColumn(2); - $scope.$digest(); - - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe('cccc'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('cccc'); - expect(tableRows.eq(0).find('td').eq(2).text()).toBe('aaaa'); - expect(tableRows.eq(1).find('td').eq(2).text()).toBe('bbbb'); - expect(tableRows.eq(2).find('td').eq(2).text()).toBe('cccc'); - expect(tableRows.eq(3).find('td').eq(2).text()).toBe('zzzz'); - }); - - test('should sort correctly between columns', () => { - // sort by the last column - paginatedTable.sortColumn(2); - $scope.$digest(); - - let tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe('cccc'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('cccc'); - expect(tableRows.eq(0).find('td').eq(2).text()).toBe('aaaa'); - - // sort by the first column - paginatedTable.sortColumn(0); - $scope.$digest(); - - tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('td').eq(0).text()).toBe('aaaa'); - expect(tableRows.eq(0).find('td').eq(1).text()).toBe('zzzz'); - expect(tableRows.eq(0).find('td').eq(2).text()).toBe('cccc'); - - expect(tableRows.eq(1).find('td').eq(0).text()).toBe('bbbb'); - expect(tableRows.eq(2).find('td').eq(0).text()).toBe('cccc'); - expect(tableRows.eq(3).find('td').eq(0).text()).toBe('zzzz'); - }); - - test('should not sort duplicate columns', () => { - paginatedTable.sortColumn(1); - $scope.$digest(); - - const sorters = $el.find('thead th i'); - expect(sorters.eq(0).hasClass('fa-sort')).toBe(true); - expect(sorters.eq(1).hasClass('fa-sort')).toBe(false); - expect(sorters.eq(2).hasClass('fa-sort')).toBe(true); - }); - }); - - describe('object rows', () => { - let cols: Column[]; - let rows: any; - - beforeEach(() => { - cols = [ - { - title: 'object test', - id: '0', - formatter: { - convert: (val) => { - return val === 'zzz' ? '<h1>hello</h1>' : val; - }, - }, - }, - ]; - rows = [['aaaa'], ['zzz'], ['bbbb']]; - renderTable({ columns: cols, rows }, cols, rows); - }); - - test('should append object markup', () => { - const tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('h1').length).toBe(0); - expect(tableRows.eq(1).find('h1').length).toBe(1); - expect(tableRows.eq(2).find('h1').length).toBe(0); - }); - - test('should sort using object value', () => { - paginatedTable.sortColumn(0); - $scope.$digest(); - let tableRows = $el.find('tbody tr'); - expect(tableRows.eq(0).find('h1').length).toBe(0); - expect(tableRows.eq(1).find('h1').length).toBe(0); - // html row should be the last row - expect(tableRows.eq(2).find('h1').length).toBe(1); - - paginatedTable.sortColumn(0); - $scope.$digest(); - tableRows = $el.find('tbody tr'); - // html row should be the first row - expect(tableRows.eq(0).find('h1').length).toBe(1); - expect(tableRows.eq(1).find('h1').length).toBe(0); - expect(tableRows.eq(2).find('h1').length).toBe(0); - }); - }); -}); diff --git a/src/plugins/vis_type_table/public/paginated_table/rows.js b/src/plugins/vis_type_table/public/paginated_table/rows.js deleted file mode 100644 index 5ed2de5de176..000000000000 --- a/src/plugins/vis_type_table/public/paginated_table/rows.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 $ from 'jquery'; -import _ from 'lodash'; -import angular from 'angular'; -import tableCellFilterHtml from './table_cell_filter.html'; - -export function OsdRows($compile) { - return { - restrict: 'A', - link: function ($scope, $el, attr) { - function addCell($tr, contents, column, row) { - function createCell() { - return $(document.createElement('td')); - } - - function createFilterableCell(value) { - const $template = $(tableCellFilterHtml); - $template.addClass('osdTableCellFilter__hover'); - - const scope = $scope.$new(); - - scope.onFilterClick = (event, negate) => { - // Don't add filter if a link was clicked. - if ($(event.target).is('a')) { - return; - } - - $scope.filter({ - data: [ - { - table: $scope.table, - row: $scope.rows.findIndex((r) => r === row), - column: $scope.table.columns.findIndex((c) => c.id === column.id), - value, - }, - ], - negate, - }); - }; - - return $compile($template)(scope); - } - - let $cell; - let $cellContent; - - const contentsIsDefined = contents !== null && contents !== undefined; - - if (column.filterable && contentsIsDefined) { - $cell = createFilterableCell(contents); - // in jest tests 'angular' is using jqLite. In jqLite the method find lookups only by tags. - // Because of this, we should change a way how we get cell content so that tests will pass. - $cellContent = angular.element($cell[0].querySelector('[data-cell-content]')); - } else { - $cell = $cellContent = createCell(); - } - - // An AggConfigResult can "enrich" cell contents by applying a field formatter, - // which we want to do if possible. - contents = contentsIsDefined ? column.formatter.convert(contents, 'html') : ''; - - if (_.isObject(contents)) { - if (contents.attr) { - $cellContent.attr(contents.attr); - } - - if (contents.class) { - $cellContent.addClass(contents.class); - } - - if (contents.scope) { - $cellContent = $compile($cellContent.prepend(contents.markup))(contents.scope); - } else { - $cellContent.prepend(contents.markup); - } - - if (contents.attr) { - $cellContent.attr(contents.attr); - } - } else { - if (contents === '') { - $cellContent.prepend(' '); - } else { - $cellContent.prepend(contents); - } - } - - $tr.append($cell); - } - - $scope.$watchMulti([attr.osdRows, attr.osdRowsMin], function (vals) { - let rows = vals[0]; - const min = vals[1]; - - $el.empty(); - - if (!Array.isArray(rows)) rows = []; - - if (isFinite(min) && rows.length < min) { - // clone the rows so that we can add elements to it without upsetting the original - rows = _.clone(rows); - // crate the empty row which will be pushed into the row list over and over - const emptyRow = {}; - // push as many empty rows into the row array as needed - _.times(min - rows.length, function () { - rows.push(emptyRow); - }); - } - - rows.forEach(function (row) { - const $tr = $(document.createElement('tr')).appendTo($el); - $scope.columns.forEach((column) => { - const value = row[column.id]; - addCell($tr, value, column, row); - }); - }); - }); - }, - }; -} diff --git a/src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html b/src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html deleted file mode 100644 index a9185884dae5..000000000000 --- a/src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html +++ /dev/null @@ -1,23 +0,0 @@ -<td> - <div - data-cell-content - class="osdTableCellFilter__hover" - > - <span class="osdTableCellFilter"> - <span - data-test-subj="filterForCellValue" - ng-click="onFilterClick($event, false)" - class="fa fa-search-plus" - tooltip="{{ ::'visTypeTable.directives.tableCellFilter.filterForValueTooltip' | i18n: {defaultMessage: 'Filter for value'} }}" - tooltip-append-to-body="1" - ></span> - - <span - ng-click="onFilterClick($event, true)" - class="fa fa-search-minus" - tooltip="{{ ::'visTypeTable.directives.tableCellFilter.filterOutValueTooltip' | i18n: {defaultMessage: 'Filter out value'} }}" - tooltip-append-to-body="1" - ></span> - </span> - </div> -</td> diff --git a/src/plugins/vis_type_table/public/plugin.ts b/src/plugins/vis_type_table/public/plugin.ts index 8c01fee8841e..0582ebbedeb0 100644 --- a/src/plugins/vis_type_table/public/plugin.ts +++ b/src/plugins/vis_type_table/public/plugin.ts @@ -1,31 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { @@ -40,45 +15,38 @@ import { VisualizationsSetup } from '../../visualizations/public'; import { createTableVisFn } from './table_vis_fn'; import { getTableVisTypeDefinition } from './table_vis_type'; import { DataPublicPluginStart } from '../../data/public'; -import { setFormatService, setOpenSearchDashboardsLegacy } from './services'; -import { OpenSearchDashboardsLegacyStart } from '../../opensearch_dashboards_legacy/public'; - -/** @internal */ -export interface TablePluginSetupDependencies { +import { setFormatService } from './services'; +import { ConfigSchema } from '../config'; +import { getTableVisRenderer } from './table_vis_renderer'; +export interface TableVisPluginSetupDependencies { expressions: ReturnType<ExpressionsPublicPlugin['setup']>; visualizations: VisualizationsSetup; } - -/** @internal */ -export interface TablePluginStartDependencies { +export interface TableVisPluginStartDependencies { data: DataPublicPluginStart; - opensearchDashboardsLegacy: OpenSearchDashboardsLegacyStart; } -/** @internal */ -export class TableVisPlugin implements Plugin<Promise<void>, void> { - initializerContext: PluginInitializerContext; - createBaseVisualization: any; - - constructor(initializerContext: PluginInitializerContext) { +const setupTableVis = async ( + core: CoreSetup, + { expressions, visualizations }: TableVisPluginSetupDependencies +) => { + const [coreStart] = await core.getStartServices(); + expressions.registerFunction(createTableVisFn); + expressions.registerRenderer(getTableVisRenderer(coreStart)); + visualizations.createBaseVisualization(getTableVisTypeDefinition()); +}; +export class TableVisPlugin implements Plugin<void, void> { + initializerContext: PluginInitializerContext<ConfigSchema>; + + constructor(initializerContext: PluginInitializerContext<ConfigSchema>) { this.initializerContext = initializerContext; } - public async setup( - core: CoreSetup, - { expressions, visualizations }: TablePluginSetupDependencies - ) { - expressions.registerFunction(createTableVisFn); - visualizations.createBaseVisualization( - getTableVisTypeDefinition(core, this.initializerContext) - ); + public async setup(core: CoreSetup, dependencies: TableVisPluginSetupDependencies) { + setupTableVis(core, dependencies); } - public start( - core: CoreStart, - { data, opensearchDashboardsLegacy }: TablePluginStartDependencies - ) { + public start(core: CoreStart, { data }: TableVisPluginStartDependencies) { setFormatService(data.fieldFormats); - setOpenSearchDashboardsLegacy(opensearchDashboardsLegacy); } } diff --git a/src/plugins/vis_type_table/public/services.ts b/src/plugins/vis_type_table/public/services.ts index 4fb56f6bfbdb..f8ca4b574307 100644 --- a/src/plugins/vis_type_table/public/services.ts +++ b/src/plugins/vis_type_table/public/services.ts @@ -1,41 +1,11 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { createGetterSetter } from '../../opensearch_dashboards_utils/public'; import { DataPublicPluginStart } from '../../data/public'; -import { OpenSearchDashboardsLegacyStart } from '../../opensearch_dashboards_legacy/public'; export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] >('table data.fieldFormats'); - -export const [getOpenSearchDashboardsLegacy, setOpenSearchDashboardsLegacy] = createGetterSetter< - OpenSearchDashboardsLegacyStart ->('table opensearchDashboardsLegacy'); diff --git a/src/plugins/vis_type_table/public/table_vis.html b/src/plugins/vis_type_table/public/table_vis.html deleted file mode 100644 index 169b53390fe3..000000000000 --- a/src/plugins/vis_type_table/public/table_vis.html +++ /dev/null @@ -1,29 +0,0 @@ -<div ng-controller="OsdTableVisController" class="table-vis"> - <div ng-if="!hasSomeRows && hasSomeRows !== null" class="visError"> - <div class="euiText euiText--extraSmall euiTextColor euiTextColor--subdued"> - <icon type="'visualizeApp'" size="'m'" color="'subdued'"></icon> - - <br> - <br> - - <p - i18n-id="visTypeTable.vis.noResultsFoundTitle" - i18n-default-message="No results found"> - </p> - </div> - </div> - - <div ng-if="tableGroups" class="table-vis-container" data-test-subj="tableVis"> - <osd-agg-table-group - filter="vis.API.events.filter" - dimensions="dimensions" - group="tableGroups" - export-title="visState.title" - per-page="visState.params.perPage" - sort="sort" - percentage-col="visState.params.percentageCol" - show-total="visState.params.showTotal" - total-func="visState.params.totalFunc"> - </osd-agg-table-group> - </div> -</div> diff --git a/src/plugins/vis_type_table/public/table_vis_controller.js b/src/plugins/vis_type_table/public/table_vis_controller.js deleted file mode 100644 index 9fa71534903d..000000000000 --- a/src/plugins/vis_type_table/public/table_vis_controller.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { assign } from 'lodash'; - -export function TableVisController($scope) { - const uiStateSort = $scope.uiState ? $scope.uiState.get('vis.params.sort') : {}; - assign($scope.visParams.sort, uiStateSort); - - $scope.sort = $scope.visParams.sort; - $scope.$watchCollection('sort', function (newSort) { - $scope.uiState.set('vis.params.sort', newSort); - }); - - /** - * Recreate the entire table when: - * - the underlying data changes (opensearchResponse) - * - one of the view options changes (vis.params) - */ - $scope.$watch('renderComplete', function () { - let tableGroups = ($scope.tableGroups = null); - let hasSomeRows = ($scope.hasSomeRows = null); - - if ($scope.opensearchResponse) { - tableGroups = $scope.opensearchResponse; - - hasSomeRows = tableGroups.tables.some(function haveRows(table) { - if (table.tables) return table.tables.some(haveRows); - return table.rows.length > 0; - }); - } - - $scope.hasSomeRows = hasSomeRows; - if (hasSomeRows) { - $scope.dimensions = $scope.visParams.dimensions; - $scope.tableGroups = tableGroups; - } - $scope.renderComplete(); - }); -} diff --git a/src/plugins/vis_type_table/public/table_vis_controller.test.ts b/src/plugins/vis_type_table/public/table_vis_controller.test.ts deleted file mode 100644 index db12e2b51426..000000000000 --- a/src/plugins/vis_type_table/public/table_vis_controller.test.ts +++ /dev/null @@ -1,272 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 angular, { IRootScopeService, IScope, ICompileService } from 'angular'; -import 'angular-mocks'; -import 'angular-sanitize'; -import $ from 'jquery'; - -import { getAngularModule } from './get_inner_angular'; -import { initTableVisLegacyModule } from './table_vis_legacy_module'; -import { getTableVisTypeDefinition } from './table_vis_type'; -import { Vis } from '../../visualizations/public'; -import { stubFields } from '../../data/public/stubs'; -import { tableVisResponseHandler } from './table_vis_response_handler'; -import { coreMock } from '../../../core/public/mocks'; -import { IAggConfig, search } from '../../data/public'; -import { getStubIndexPattern } from '../../data/public/test_utils'; -// TODO: remove linting disable -import { searchServiceMock } from '../../data/public/search/mocks'; - -const { createAggConfigs } = searchServiceMock.createStartContract().aggs; - -const { tabifyAggResponse } = search; - -jest.mock('../../opensearch_dashboards_legacy/public/angular/angular_config', () => ({ - configureAppAngularModule: () => {}, -})); - -interface TableVisScope extends IScope { - [key: string]: any; -} - -const oneRangeBucket = { - hits: { - total: 6039, - max_score: 0, - hits: [], - }, - aggregations: { - agg_2: { - buckets: { - '0.0-1000.0': { - from: 0, - from_as_string: '0.0', - to: 1000, - to_as_string: '1000.0', - doc_count: 606, - }, - '1000.0-2000.0': { - from: 1000, - from_as_string: '1000.0', - to: 2000, - to_as_string: '2000.0', - doc_count: 298, - }, - }, - }, - }, -}; - -describe('Table Vis - Controller', () => { - let $rootScope: IRootScopeService & { [key: string]: any }; - let $compile: ICompileService; - let $scope: TableVisScope; - let $el: JQuery<HTMLElement>; - let tableAggResponse: any; - let tabifiedResponse: any; - let stubIndexPattern: any; - - const initLocalAngular = () => { - const tableVisModule = getAngularModule( - 'opensearch-dashboards/table_vis', - coreMock.createStart(), - coreMock.createPluginInitializerContext() - ); - initTableVisLegacyModule(tableVisModule); - }; - - beforeEach(initLocalAngular); - beforeEach(angular.mock.module('opensearch-dashboards/table_vis')); - - beforeEach( - angular.mock.inject((_$rootScope_: IRootScopeService, _$compile_: ICompileService) => { - $rootScope = _$rootScope_; - $compile = _$compile_; - tableAggResponse = tableVisResponseHandler; - }) - ); - - beforeEach(() => { - stubIndexPattern = getStubIndexPattern( - 'logstash-*', - (cfg: any) => cfg, - 'time', - stubFields, - coreMock.createSetup() - ); - }); - const tableVisTypeDefinition = getTableVisTypeDefinition( - coreMock.createSetup(), - coreMock.createPluginInitializerContext() - ); - - function getRangeVis(params?: object) { - return ({ - type: tableVisTypeDefinition, - params: Object.assign({}, tableVisTypeDefinition.visConfig?.defaults, params), - data: { - aggs: createAggConfigs(stubIndexPattern, [ - { type: 'count', schema: 'metric' }, - { - type: 'range', - schema: 'bucket', - params: { - field: 'bytes', - ranges: [ - { from: 0, to: 1000 }, - { from: 1000, to: 2000 }, - ], - }, - }, - ]), - }, - } as unknown) as Vis; - } - - const dimensions = { - buckets: [ - { - accessor: 0, - }, - ], - metrics: [ - { - accessor: 1, - format: { id: 'range' }, - }, - ], - }; - - // basically a parameterized beforeEach - function initController(vis: Vis) { - vis.data.aggs!.aggs.forEach((agg: IAggConfig, i: number) => { - agg.id = 'agg_' + (i + 1); - }); - - tabifiedResponse = tabifyAggResponse(vis.data.aggs!, oneRangeBucket); - $rootScope.vis = vis; - $rootScope.visParams = vis.params; - $rootScope.uiState = { - get: jest.fn(), - set: jest.fn(), - }; - $rootScope.renderComplete = () => {}; - $rootScope.newScope = (scope: TableVisScope) => { - $scope = scope; - }; - - $el = $('<div>') - .attr('ng-controller', 'OsdTableVisController') - .attr('ng-init', 'newScope(this)'); - - $compile($el)($rootScope); - } - - // put a response into the controller - function attachOpenSearchResponseToScope(resp: object) { - $rootScope.opensearchResponse = resp; - $rootScope.$apply(); - } - - // remove the response from the controller - function removeOpenSearchResponseFromScope() { - delete $rootScope.opensearchResponse; - $rootScope.renderComplete = () => {}; - $rootScope.$apply(); - } - - test('exposes #tableGroups and #hasSomeRows when a response is attached to scope', async () => { - const vis: Vis = getRangeVis(); - initController(vis); - - expect(!$scope.tableGroups).toBeTruthy(); - expect(!$scope.hasSomeRows).toBeTruthy(); - - attachOpenSearchResponseToScope(await tableAggResponse(tabifiedResponse, dimensions)); - - expect($scope.hasSomeRows).toBeTruthy(); - expect($scope.tableGroups.tables).toBeDefined(); - expect($scope.tableGroups.tables.length).toBe(1); - expect($scope.tableGroups.tables[0].columns.length).toBe(2); - expect($scope.tableGroups.tables[0].rows.length).toBe(2); - }); - - test('clears #tableGroups and #hasSomeRows when the response is removed', async () => { - const vis = getRangeVis(); - initController(vis); - - attachOpenSearchResponseToScope(await tableAggResponse(tabifiedResponse, dimensions)); - removeOpenSearchResponseFromScope(); - - expect(!$scope.hasSomeRows).toBeTruthy(); - expect(!$scope.tableGroups).toBeTruthy(); - }); - - test('sets the sort on the scope when it is passed as a vis param', async () => { - const sortObj = { - columnIndex: 1, - direction: 'asc', - }; - const vis = getRangeVis({ sort: sortObj }); - initController(vis); - - attachOpenSearchResponseToScope(await tableAggResponse(tabifiedResponse, dimensions)); - - expect($scope.sort.columnIndex).toEqual(sortObj.columnIndex); - expect($scope.sort.direction).toEqual(sortObj.direction); - }); - - test('sets #hasSomeRows properly if the table group is empty', async () => { - const vis = getRangeVis(); - initController(vis); - - tabifiedResponse.rows = []; - - attachOpenSearchResponseToScope(await tableAggResponse(tabifiedResponse, dimensions)); - - expect($scope.hasSomeRows).toBeFalsy(); - expect(!$scope.tableGroups).toBeTruthy(); - }); - - test('passes partialRows:true to tabify based on the vis params', () => { - const vis = getRangeVis({ showPartialRows: true }); - initController(vis); - - expect((vis.type.hierarchicalData as Function)(vis)).toEqual(true); - }); - - test('passes partialRows:false to tabify based on the vis params', () => { - const vis = getRangeVis({ showPartialRows: false }); - initController(vis); - - expect((vis.type.hierarchicalData as Function)(vis)).toEqual(false); - }); -}); diff --git a/src/plugins/vis_type_table/public/table_vis_fn.test.ts b/src/plugins/vis_type_table/public/table_vis_fn.test.ts index f7723456b757..b4a4ca4776bc 100644 --- a/src/plugins/vis_type_table/public/table_vis_fn.test.ts +++ b/src/plugins/vis_type_table/public/table_vis_fn.test.ts @@ -84,6 +84,6 @@ describe('interpreter/functions#table', () => { it('calls response handler with correct values', async () => { await fn(context, { visConfig: JSON.stringify(visConfig) }, undefined); expect(tableVisResponseHandler).toHaveBeenCalledTimes(1); - expect(tableVisResponseHandler).toHaveBeenCalledWith(context, visConfig.dimensions); + expect(tableVisResponseHandler).toHaveBeenCalledWith(context, visConfig); }); }); diff --git a/src/plugins/vis_type_table/public/table_vis_fn.ts b/src/plugins/vis_type_table/public/table_vis_fn.ts index daf76580b59f..4f0fb2c0ba1f 100644 --- a/src/plugins/vis_type_table/public/table_vis_fn.ts +++ b/src/plugins/vis_type_table/public/table_vis_fn.ts @@ -1,31 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. */ import { i18n } from '@osd/i18n'; @@ -35,7 +10,7 @@ import { OpenSearchDashboardsDatatable, Render, } from '../../expressions/public'; -import { VisRenderValue } from '../../visualizations/public'; +import { TableVisConfig } from './types'; export type Input = OpenSearchDashboardsDatatable; @@ -43,17 +18,20 @@ interface Arguments { visConfig: string | null; } -interface RenderValue extends VisRenderValue { +export interface TableVisRenderValue { visData: TableContext; visType: 'table'; + visConfig: TableVisConfig; } -export const createTableVisFn = (): ExpressionFunctionDefinition< +export type TableVisExpressionFunctionDefinition = ExpressionFunctionDefinition< 'opensearch_dashboards_table', Input, Arguments, - Render<RenderValue> -> => ({ + Render<TableVisRenderValue> +>; + +export const createTableVisFn = (): TableVisExpressionFunctionDefinition => ({ name: 'opensearch_dashboards_table', type: 'render', inputTypes: ['opensearch_dashboards_datatable'], @@ -69,18 +47,15 @@ export const createTableVisFn = (): ExpressionFunctionDefinition< }, fn(input, args) { const visConfig = args.visConfig && JSON.parse(args.visConfig); - const convertedData = tableVisResponseHandler(input, visConfig.dimensions); + const convertedData = tableVisResponseHandler(input, visConfig); return { type: 'render', - as: 'visualization', + as: 'table_vis', value: { visData: convertedData, visType: 'table', visConfig, - params: { - listenOnChange: true, - }, }, }; }, diff --git a/src/plugins/vis_type_table/public/table_vis_legacy_module.ts b/src/plugins/vis_type_table/public/table_vis_legacy_module.ts deleted file mode 100644 index 49eed3494f92..000000000000 --- a/src/plugins/vis_type_table/public/table_vis_legacy_module.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { IModule } from 'angular'; - -// @ts-ignore -import { TableVisController } from './table_vis_controller.js'; -// @ts-ignore -import { OsdAggTable } from './agg_table/agg_table'; -// @ts-ignore -import { OsdAggTableGroup } from './agg_table/agg_table_group'; -// @ts-ignore -import { OsdRows } from './paginated_table/rows'; -// @ts-ignore -import { PaginatedTable } from './paginated_table/paginated_table'; - -/** @internal */ -export const initTableVisLegacyModule = (angularIns: IModule): void => { - angularIns - .controller('OsdTableVisController', TableVisController) - .directive('osdAggTable', OsdAggTable) - .directive('osdAggTableGroup', OsdAggTableGroup) - .directive('osdRows', OsdRows) - .directive('paginatedTable', PaginatedTable); -}; diff --git a/src/plugins/vis_type_table_new/public/table_vis_renderer.tsx b/src/plugins/vis_type_table/public/table_vis_renderer.tsx similarity index 100% rename from src/plugins/vis_type_table_new/public/table_vis_renderer.tsx rename to src/plugins/vis_type_table/public/table_vis_renderer.tsx diff --git a/src/plugins/vis_type_table/public/table_vis_response_handler.ts b/src/plugins/vis_type_table/public/table_vis_response_handler.ts index 78b2306e744b..b1d41edfff8b 100644 --- a/src/plugins/vis_type_table/public/table_vis_response_handler.ts +++ b/src/plugins/vis_type_table/public/table_vis_response_handler.ts @@ -28,19 +28,17 @@ * under the License. */ -import { Required } from '@osd/utility-types'; - import { getFormatService } from './services'; -import { Input } from './table_vis_fn'; +import { OpenSearchDashboardsDatatable } from '../../expressions/public'; +import { TableVisConfig } from './types'; -export interface TableContext { - tables: Array<TableGroup | Table>; - direction?: 'row' | 'column'; +export interface Table { + columns: OpenSearchDashboardsDatatable['columns']; + rows: OpenSearchDashboardsDatatable['rows']; } export interface TableGroup { - $parent: TableContext; - table: Input; + table: OpenSearchDashboardsDatatable; tables: Table[]; title: string; name: string; @@ -49,61 +47,66 @@ export interface TableGroup { row: number; } -export interface Table { - $parent?: TableGroup; - columns: Input['columns']; - rows: Input['rows']; +export interface TableContext { + table?: Table; + tableGroups: TableGroup[]; + direction?: 'row' | 'column'; } -export function tableVisResponseHandler(table: Input, dimensions: any): TableContext { - const converted: TableContext = { - tables: [], - }; +export function tableVisResponseHandler( + input: OpenSearchDashboardsDatatable, + config: TableVisConfig +): TableContext { + let table: Table | undefined; + const tableGroups: TableGroup[] = []; + let direction: TableContext['direction']; - const split = dimensions.splitColumn || dimensions.splitRow; + const split = config.splitColumn || config.splitRow; if (split) { - converted.direction = dimensions.splitRow ? 'row' : 'column'; + direction = config.splitRow ? 'row' : 'column'; const splitColumnIndex = split[0].accessor; const splitColumnFormatter = getFormatService().deserialize(split[0].format); - const splitColumn = table.columns[splitColumnIndex]; - const splitMap = {}; + const splitColumn = input.columns[splitColumnIndex]; + const splitMap: { [key: string]: number } = {}; let splitIndex = 0; - table.rows.forEach((row, rowIndex) => { + input.rows.forEach((row, rowIndex) => { const splitValue: any = row[splitColumn.id]; if (!splitMap.hasOwnProperty(splitValue as any)) { (splitMap as any)[splitValue] = splitIndex++; - const tableGroup: Required<TableGroup, 'tables'> = { - $parent: converted, + const tableGroup: TableGroup = { title: `${splitColumnFormatter.convert(splitValue)}: ${splitColumn.name}`, name: splitColumn.name, key: splitValue, column: splitColumnIndex, row: rowIndex, - table, + table: input, tables: [], }; tableGroup.tables.push({ - $parent: tableGroup, - columns: table.columns, + columns: input.columns, rows: [], }); - converted.tables.push(tableGroup); + tableGroups.push(tableGroup); } const tableIndex = (splitMap as any)[splitValue]; - (converted.tables[tableIndex] as any).tables[0].rows.push(row); + (tableGroups[tableIndex] as any).tables[0].rows.push(row); }); } else { - converted.tables.push({ - columns: table.columns, - rows: table.rows, - }); + table = { + columns: input.columns, + rows: input.rows, + }; } - return converted; + return { + table, + tableGroups, + direction, + }; } diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts index df1495a3d06b..0c27e7a8af0b 100644 --- a/src/plugins/vis_type_table/public/table_vis_type.ts +++ b/src/plugins/vis_type_table/public/table_vis_type.ts @@ -28,91 +28,78 @@ * under the License. */ -import { CoreSetup, PluginInitializerContext } from 'opensearch-dashboards/public'; import { i18n } from '@osd/i18n'; import { AggGroupNames } from '../../data/public'; import { Schemas } from '../../vis_default_editor/public'; import { BaseVisTypeOptions } from '../../visualizations/public'; import { tableVisResponseHandler } from './table_vis_response_handler'; -// @ts-ignore -import tableVisTemplate from './table_vis.html'; +import { toExpressionAst } from './to_ast'; +import { VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public'; import { TableOptions } from './components/table_vis_options_lazy'; -import { getTableVisualizationControllerClass } from './vis_controller'; -import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; -export function getTableVisTypeDefinition( - core: CoreSetup, - context: PluginInitializerContext -): BaseVisTypeOptions { - return { - name: 'table', - title: i18n.translate('visTypeTable.tableVisTitle', { - defaultMessage: 'Data Table', - }), - icon: 'visTable', - description: i18n.translate('visTypeTable.tableVisDescription', { - defaultMessage: 'Display values in a table', - }), - visualization: getTableVisualizationControllerClass(core, context), - getSupportedTriggers: () => { - return [VIS_EVENT_TO_TRIGGER.filter]; +export const getTableVisTypeDefinition = (): BaseVisTypeOptions => ({ + name: 'table', + title: i18n.translate('visTypeTable.tableVisTitle', { + defaultMessage: 'Data Table', + }), + icon: 'visTable', + description: i18n.translate('visTypeTable.tableVisDescription', { + defaultMessage: 'Display values in a table', + }), + toExpressionAst, + visConfig: { + defaults: { + perPage: 10, + showPartialRows: false, + showMetricsAtAllLevels: false, + showTotal: false, + totalFunc: 'sum', + percentageCol: '', }, - visConfig: { - defaults: { - perPage: 10, - showPartialRows: false, - showMetricsAtAllLevels: false, - sort: { - columnIndex: null, - direction: null, - }, - showTotal: false, - totalFunc: 'sum', - percentageCol: '', - }, - template: tableVisTemplate, - }, - editorConfig: { - optionsTemplate: TableOptions, - schemas: new Schemas([ - { - group: AggGroupNames.Metrics, - name: 'metric', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', { - defaultMessage: 'Metric', - }), - aggFilter: ['!geo_centroid', '!geo_bounds'], - aggSettings: { - top_hits: { - allowStrings: true, - }, + }, + editorConfig: { + optionsTemplate: TableOptions, + schemas: new Schemas([ + { + group: AggGroupNames.Metrics, + name: 'metric', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', { + defaultMessage: 'Metric', + }), + aggFilter: ['!geo_centroid', '!geo_bounds'], + aggSettings: { + top_hits: { + allowStrings: true, }, - min: 1, - defaults: [{ type: 'count', schema: 'metric' }], - }, - { - group: AggGroupNames.Buckets, - name: 'bucket', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.bucketTitle', { - defaultMessage: 'Split rows', - }), - aggFilter: ['!filter'], - }, - { - group: AggGroupNames.Buckets, - name: 'split', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.splitTitle', { - defaultMessage: 'Split table', - }), - min: 0, - max: 1, - aggFilter: ['!filter'], }, - ]), - }, - responseHandler: tableVisResponseHandler, - hierarchicalData: (vis) => { - return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); - }, - }; -} + min: 1, + defaults: [{ type: 'count', schema: 'metric' }], + }, + { + group: AggGroupNames.Buckets, + name: 'bucket', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.bucketTitle', { + defaultMessage: 'Split rows', + }), + aggFilter: ['!filter'], + }, + { + group: AggGroupNames.Buckets, + name: 'split', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.splitTitle', { + defaultMessage: 'Split table', + }), + min: 0, + max: 1, + aggFilter: ['!filter'], + }, + ]), + }, + responseHandler: tableVisResponseHandler, + getSupportedTriggers: () => { + return [VIS_EVENT_TO_TRIGGER.filter]; + }, + hierarchicalData: (vis) => { + return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); + }, +}); diff --git a/src/plugins/vis_type_table/public/to_ast.test.ts b/src/plugins/vis_type_table/public/to_ast.test.ts new file mode 100644 index 000000000000..be2741e8de43 --- /dev/null +++ b/src/plugins/vis_type_table/public/to_ast.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { toExpressionAst } from './to_ast'; +import { Vis } from '../../visualizations/public'; + +describe('table vis toExpressionAst function', () => { + let vis: Vis; + + beforeEach(() => { + vis = { + isHierarchical: () => false, + type: {}, + params: {}, + data: { + indexPattern: { id: '123' } as any, + aggs: { + getResponseAggs: () => [], + aggs: [], + } as any, + }, + } as any; + }); + + it('without params', () => { + vis.params = { table: {} }; + const actual = toExpressionAst(vis, {}); + expect(actual).toMatchSnapshot(); + }); + + it('with default params', () => { + vis.params = { + perPage: 10, + showPartialRows: false, + showMetricsAtAllLevels: false, + showTotal: false, + totalFunc: 'sum', + percentageCol: '', + }; + const actual = toExpressionAst(vis, {}); + expect(actual).toMatchSnapshot(); + }); + + it('with customized params', () => { + vis.params = { + perPage: 5, + showPartialRows: false, + showMetricsAtAllLevels: false, + showTotal: true, + totalFunc: 'min', + percentageCol: 'Count', + }; + const actual = toExpressionAst(vis, {}); + expect(actual).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/vis_type_table/public/to_ast.ts b/src/plugins/vis_type_table/public/to_ast.ts new file mode 100644 index 000000000000..3753c35cfc26 --- /dev/null +++ b/src/plugins/vis_type_table/public/to_ast.ts @@ -0,0 +1,65 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getVisSchemas, Vis } from '../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { TableVisExpressionFunctionDefinition } from './table_vis_fn'; +import { OpenSearchaggsExpressionFunctionDefinition } from '../../data/common/search/expressions'; + +export const toExpressionAst = (vis: Vis, params: any) => { + const opensearchaggs = buildExpressionFunction<OpenSearchaggsExpressionFunctionDefinition>( + 'opensearchaggs', + { + index: vis.data.indexPattern!.id!, + metricsAtAllLevels: vis.isHierarchical(), + partialRows: vis.params.showPartialRows || false, + aggConfigs: JSON.stringify(vis.data.aggs!.aggs), + includeFormatHints: false, + } + ); + + const schemas = getVisSchemas(vis, params); + // if customer selects showPartialRows without showMetricsAtAllLevels, + // we need to remove the duplicated metrics. + // First, we need to calculate the required number of metrics + // Then, we return one copy of the required metrics from metric array + const metrics = + schemas.bucket && vis.params.showPartialRows && !vis.params.showMetricsAtAllLevels + ? schemas.metric.slice(-1 * (schemas.metric.length / schemas.bucket.length)) + : schemas.metric; + + const tableData = { + title: vis.title, + metrics, + buckets: schemas.bucket || [], + splitRow: schemas.split_row, + splitColumn: schemas.split_column, + }; + + const tableConfig = { + perPage: vis.params.perPage, + percentageCol: vis.params.percentageCol, + showPartialRows: vis.params.showPartialRows, + showMetricsAtAllLevels: vis.params.showMetricsAtAllLevels, + showTotal: vis.params.showTotal, + totalFunc: vis.params.totalFunc, + }; + + const visConfig = { + ...tableConfig, + ...tableData, + }; + + const tableVis = buildExpressionFunction<TableVisExpressionFunctionDefinition>( + 'opensearch_dashboards_table', + { + visConfig: JSON.stringify(visConfig), + } + ); + + const ast = buildExpression([opensearchaggs, tableVis]); + + return ast.toAst(); +}; diff --git a/src/plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts index c780ef3b5db9..814a86f5ac69 100644 --- a/src/plugins/vis_type_table/public/types.ts +++ b/src/plugins/vis_type_table/public/types.ts @@ -28,7 +28,8 @@ * under the License. */ -import { SchemaConfig } from '../../visualizations/public'; +import { SchemaConfig } from 'src/plugins/visualizations/public'; +import { IFieldFormat } from 'src/plugins/data/public'; export enum AggTypes { SUM = 'sum', @@ -38,22 +39,46 @@ export enum AggTypes { COUNT = 'count', } -export interface Dimensions { - buckets: SchemaConfig[]; +export interface TableVisConfig extends TableVisParams { + title: string; metrics: SchemaConfig[]; + buckets: SchemaConfig[]; + splitRow?: SchemaConfig[]; + splitColumn?: SchemaConfig[]; } export interface TableVisParams { - type: 'table'; perPage: number | ''; showPartialRows: boolean; showMetricsAtAllLevels: boolean; - sort: { - columnIndex: number | null; - direction: string | null; - }; showTotal: boolean; totalFunc: AggTypes; percentageCol: string; - dimensions: Dimensions; +} + +export interface FormattedColumn { + id: string; + title: string; + formatter: IFieldFormat; + filterable: boolean; + formattedTotal?: string | number; + sumTotal?: number; + total?: number; +} + +export interface ColumnWidth { + colIndex: number; + width: number; +} + +export interface ColumnSort { + colIndex?: number; + direction?: 'asc' | 'desc'; +} + +export interface TableUiState { + sort: ColumnSort; + setSort: (sort: ColumnSort) => void; + width: ColumnWidth[]; + setWidth: (columnWidths: ColumnWidth[]) => void; } diff --git a/src/plugins/vis_type_table_new/public/utils/convert_to_csv_data.ts b/src/plugins/vis_type_table/public/utils/convert_to_csv_data.ts similarity index 100% rename from src/plugins/vis_type_table_new/public/utils/convert_to_csv_data.ts rename to src/plugins/vis_type_table/public/utils/convert_to_csv_data.ts diff --git a/src/plugins/vis_type_table/public/utils/convert_to_formatted_data.ts b/src/plugins/vis_type_table/public/utils/convert_to_formatted_data.ts new file mode 100644 index 000000000000..2ab67e3b0a67 --- /dev/null +++ b/src/plugins/vis_type_table/public/utils/convert_to_formatted_data.ts @@ -0,0 +1,179 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@osd/i18n'; +import { chain } from 'lodash'; +import { OpenSearchDashboardsDatatableRow } from 'src/plugins/expressions'; +import { Table } from '../table_vis_response_handler'; +import { AggTypes, TableVisConfig } from '../types'; +import { getFormatService } from '../services'; +import { FormattedColumn } from '../types'; + +function insert(arr: FormattedColumn[], index: number, col: FormattedColumn) { + const newArray = [...arr]; + newArray.splice(index + 1, 0, col); + return newArray; +} + +/** + * @param columns - the formatted columns that will be displayed + * @param title - the title of the column to add to + * @param rows - the row data for the columns + * @param insertAtIndex - the index to insert the percentage column at + * @returns cols and rows for the table to render now included percentage column(s) + */ +function addPercentageCol( + columns: FormattedColumn[], + title: string, + rows: Table['rows'], + insertAtIndex: number +) { + const { id, sumTotal } = columns[insertAtIndex]; + const newId = `${id}-percents`; + const formatter = getFormatService().deserialize({ id: 'percent' }); + const i18nTitle = i18n.translate('visTypeTable.params.percentageTableColumnName', { + defaultMessage: '{title} percentages', + values: { title }, + }); + const newCols = insert(columns, insertAtIndex, { + title: i18nTitle, + id: newId, + formatter, + filterable: false, + }); + const newRows = rows.map((row) => ({ + [newId]: (row[id] as number) / (sumTotal as number), + ...row, + })); + + return { cols: newCols, rows: newRows }; +} + +export interface FormattedDataProps { + formattedRows: OpenSearchDashboardsDatatableRow[]; + formattedColumns: FormattedColumn[]; +} + +export const convertToFormattedData = ( + table: Table, + visConfig: TableVisConfig +): FormattedDataProps => { + const { buckets, metrics } = visConfig; + let formattedRows: OpenSearchDashboardsDatatableRow[] = table.rows; + let formattedColumns: FormattedColumn[] = table.columns + .map(function (col, i) { + const isBucket = buckets.find((bucket) => bucket.accessor === i); + const dimension = isBucket || metrics.find((metric) => metric.accessor === i); + + if (!dimension) return undefined; + + const formatter = getFormatService().deserialize(dimension.format); + + const formattedColumn: FormattedColumn = { + id: col.id, + title: col.name, + formatter, + filterable: !!isBucket, + }; + + const isDate = dimension?.format?.id === 'date' || dimension?.format?.params?.id === 'date'; + const allowsNumericalAggregations = formatter?.allowsNumericalAggregations; + + if (allowsNumericalAggregations || isDate || visConfig.totalFunc === AggTypes.COUNT) { + const sum = table.rows.reduce((prev, curr) => { + // some metrics return undefined for some of the values + // derivative is an example of this as it returns undefined in the first row + if (curr[col.id] === undefined) return prev; + return prev + (curr[col.id] as number); + }, 0); + + formattedColumn.sumTotal = sum; + switch (visConfig.totalFunc) { + case AggTypes.SUM: { + if (!isDate) { + formattedColumn.formattedTotal = formatter?.convert(sum); + formattedColumn.total = formattedColumn.sumTotal; + } + break; + } + case AggTypes.AVG: { + if (!isDate) { + const total = sum / table.rows.length; + formattedColumn.formattedTotal = formatter?.convert(total); + formattedColumn.total = total; + } + break; + } + case AggTypes.MIN: { + const total = chain(table.rows).map(col.id).min().value() as number; + formattedColumn.formattedTotal = formatter?.convert(total); + formattedColumn.total = total; + break; + } + case AggTypes.MAX: { + const total = chain(table.rows).map(col.id).max().value() as number; + formattedColumn.formattedTotal = formatter?.convert(total); + formattedColumn.total = total; + break; + } + case 'count': { + const total = table.rows.length; + formattedColumn.formattedTotal = total; + formattedColumn.total = total; + break; + } + default: + break; + } + } + + return formattedColumn; + }) + .filter((column): column is FormattedColumn => !!column); + + if (visConfig.percentageCol) { + const insertAtIndex = formattedColumns.findIndex( + (col) => col.title === visConfig.percentageCol + ); + + // column to show percentage was removed + if (insertAtIndex < 0) return { formattedRows, formattedColumns }; + + const { cols, rows } = addPercentageCol( + formattedColumns, + visConfig.percentageCol, + table.rows, + insertAtIndex + ); + formattedRows = rows; + formattedColumns = cols; + } + return { formattedRows, formattedColumns }; +}; diff --git a/src/plugins/vis_type_table_new/public/utils/index.ts b/src/plugins/vis_type_table/public/utils/index.ts similarity index 100% rename from src/plugins/vis_type_table_new/public/utils/index.ts rename to src/plugins/vis_type_table/public/utils/index.ts diff --git a/src/plugins/vis_type_table_new/public/utils/use_pagination.ts b/src/plugins/vis_type_table/public/utils/use_pagination.ts similarity index 100% rename from src/plugins/vis_type_table_new/public/utils/use_pagination.ts rename to src/plugins/vis_type_table/public/utils/use_pagination.ts diff --git a/src/plugins/vis_type_table/public/vis_controller.ts b/src/plugins/vis_type_table/public/vis_controller.ts deleted file mode 100644 index aa7ffb05110a..000000000000 --- a/src/plugins/vis_type_table/public/vis_controller.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { CoreSetup, PluginInitializerContext } from 'opensearch-dashboards/public'; -import angular, { IModule, auto, IRootScopeService, IScope, ICompileService } from 'angular'; -import $ from 'jquery'; - -import { VisParams, ExprVis } from '../../visualizations/public'; -import { getAngularModule } from './get_inner_angular'; -import { getOpenSearchDashboardsLegacy } from './services'; -import { initTableVisLegacyModule } from './table_vis_legacy_module'; - -const innerAngularName = 'opensearch-dashboards/table_vis'; - -export function getTableVisualizationControllerClass( - core: CoreSetup, - context: PluginInitializerContext -) { - return class TableVisualizationController { - private tableVisModule: IModule | undefined; - private injector: auto.IInjectorService | undefined; - el: JQuery<Element>; - vis: ExprVis; - $rootScope: IRootScopeService | null = null; - $scope: (IScope & { [key: string]: any }) | undefined; - $compile: ICompileService | undefined; - - constructor(domeElement: Element, vis: ExprVis) { - this.el = $(domeElement); - this.vis = vis; - } - - getInjector() { - if (!this.injector) { - const mountpoint = document.createElement('div'); - mountpoint.setAttribute('style', 'height: 100%; width: 100%;'); - this.injector = angular.bootstrap(mountpoint, [innerAngularName]); - this.el.append(mountpoint); - } - - return this.injector; - } - - async initLocalAngular() { - if (!this.tableVisModule) { - const [coreStart] = await core.getStartServices(); - this.tableVisModule = getAngularModule(innerAngularName, coreStart, context); - initTableVisLegacyModule(this.tableVisModule); - } - } - - async render(opensearchResponse: object, visParams: VisParams): Promise<void> { - getOpenSearchDashboardsLegacy().loadFontAwesome(); - await this.initLocalAngular(); - - return new Promise(async (resolve, reject) => { - if (!this.$rootScope) { - const $injector = this.getInjector(); - this.$rootScope = $injector.get('$rootScope'); - this.$compile = $injector.get('$compile'); - } - const updateScope = () => { - if (!this.$scope) { - return; - } - - // How things get into this $scope? - // To inject variables into this $scope there's the following pipeline of stuff to check: - // - visualize_embeddable => that's what the editor creates to wrap this Angular component - // - build_pipeline => it serialize all the params into an Angular template compiled on the fly - // - table_vis_fn => unserialize the params and prepare them for the final React/Angular bridge - // - visualization_renderer => creates the wrapper component for this controller and passes the params - // - // In case some prop is missing check into the top of the chain if they are available and check - // the list above that it is passing through - this.$scope.vis = this.vis; - this.$scope.visState = { params: visParams, title: visParams.title }; - this.$scope.opensearchResponse = opensearchResponse; - - this.$scope.visParams = visParams; - this.$scope.renderComplete = resolve; - this.$scope.renderFailed = reject; - this.$scope.resize = Date.now(); - this.$scope.$apply(); - }; - - if (!this.$scope && this.$compile) { - this.$scope = this.$rootScope.$new(); - this.$scope.uiState = this.vis.getUiState(); - updateScope(); - this.el - .find('div') - .append(this.$compile(this.vis.type.visConfig?.template ?? '')(this.$scope)); - this.$scope.$apply(); - } else { - updateScope(); - } - }); - } - - destroy() { - if (this.$rootScope) { - this.$rootScope.$destroy(); - this.$rootScope = null; - } - } - }; -} diff --git a/src/plugins/vis_type_table_new/README.md b/src/plugins/vis_type_table_new/README.md deleted file mode 100644 index 06299ed963a2..000000000000 --- a/src/plugins/vis_type_table_new/README.md +++ /dev/null @@ -1 +0,0 @@ -Contains the data table visualization, that allows presenting data using a Datagrid component. diff --git a/src/plugins/vis_type_table_new/opensearch_dashboards.json b/src/plugins/vis_type_table_new/opensearch_dashboards.json deleted file mode 100644 index 598ca7581b83..000000000000 --- a/src/plugins/vis_type_table_new/opensearch_dashboards.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "id": "visTypeTableNew", - "version": "opensearchDashboards", - "server": false, - "ui": true, - "requiredPlugins": [ - "expressions", - "visualizations", - "data" - ], - "requiredBundles": [ - "opensearchDashboardsUtils", - "opensearchDashboardsReact", - "share" - ] -} diff --git a/src/plugins/vis_type_table_new/public/components/table_vis_grid_columns.tsx b/src/plugins/vis_type_table_new/public/components/table_vis_grid_columns.tsx deleted file mode 100644 index ba204ea6ae33..000000000000 --- a/src/plugins/vis_type_table_new/public/components/table_vis_grid_columns.tsx +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { i18n } from '@osd/i18n'; -import { EuiDataGridColumn, EuiDataGridColumnCellActionProps } from '@elastic/eui'; -import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; -import { OpenSearchDashboardsDatatableRow } from 'src/plugins/expressions'; -import { Table } from '../table_vis_response_handler'; -import { ColumnWidth, FormattedColumn } from '../types'; - -export const getDataGridColumns = ( - rows: OpenSearchDashboardsDatatableRow[], - cols: FormattedColumn[], - table: Table, - event: IInterpreterRenderHandlers['event'], - columnsWidth: ColumnWidth[] -) => { - const filterBucket = (rowIndex: number, columnIndex: number, negate: boolean) => { - const foramttedColumnId = cols[columnIndex].id; - const rawColumnIndex = table.columns.findIndex((col) => col.id === foramttedColumnId); - event({ - name: 'filterBucket', - data: { - data: [ - { - table: { - columns: table.columns, - rows, - }, - row: rowIndex, - column: rawColumnIndex, - }, - ], - negate, - }, - }); - }; - - return cols.map((col, colIndex) => { - // const cellActions = col.filterable - // ? [ - // ({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => { - // const filterValue = rows[rowIndex][columnId]; - // const filterContent = col.formatter?.convert(filterValue); - - // const filterForValueText = i18n.translate( - // 'visTypeTableNew.tableVisFilter.filterForValue', - // { - // defaultMessage: 'Filter for value', - // } - // ); - // const filterForValueLabel = i18n.translate( - // 'visTypeTableNew.tableVisFilter.filterForValueLabel', - // { - // defaultMessage: 'Filter for value: {filterContent}', - // values: { - // filterContent, - // }, - // } - // ); - - // return ( - // filterValue != null && ( - // <Component - // onClick={() => { - // filterBucket(rowIndex, colIndex, false); - // closePopover(); - // }} - // iconType="plusInCircle" - // aria-label={filterForValueLabel} - // data-test-subj="filterForValue" - // > - // {filterForValueText} - // </Component> - // ) - // ); - // }, - // ({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => { - // const filterValue = rows[rowIndex][columnId]; - // const filterContent = col.formatter?.convert(filterValue); - - // const filterOutValueText = i18n.translate( - // 'visTypeTableNew.tableVisFilter.filterOutValue', - // { - // defaultMessage: 'Filter out value', - // } - // ); - // const filterOutValueLabel = i18n.translate( - // 'visTypeTableNew.tableVisFilter.filterOutValueLabel', - // { - // defaultMessage: 'Filter out value: {filterContent}', - // values: { - // filterContent, - // }, - // } - // ); - - // return ( - // filterValue != null && ( - // <Component - // onClick={() => { - // filterBucket(rowIndex, colIndex, true); - // closePopover(); - // }} - // iconType="minusInCircle" - // aria-label={filterOutValueLabel} - // data-test-subj="filterOutValue" - // > - // {filterOutValueText} - // </Component> - // ) - // ); - // }, - // ] - // : undefined; - - const initialWidth = columnsWidth.find((c) => c.colIndex === colIndex); - - const dataGridColumn: EuiDataGridColumn = { - id: col.id, - display: col.title, - displayAsText: col.title, - actions: { - showHide: false, - showMoveLeft: false, - showMoveRight: false, - showSortAsc: { - label: i18n.translate('visTypeTableNew.tableVisSort.ascSortLabel', { - defaultMessage: 'Sort asc', - }), - }, - showSortDesc: { - label: i18n.translate('visTypeTableNew.tableVisSort.descSortLabel', { - defaultMessage: 'Sort desc', - }), - }, - }, - // cellActions, - }; - if (initialWidth) { - dataGridColumn.initialWidth = initialWidth.width; - } - return dataGridColumn; - }); -}; diff --git a/src/plugins/vis_type_table_new/public/index.ts b/src/plugins/vis_type_table_new/public/index.ts deleted file mode 100644 index 4ed30b71eeaa..000000000000 --- a/src/plugins/vis_type_table_new/public/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -// import { PluginInitializerContext } from 'opensearch-dashboards/public'; -import { TableVisPlugin as Plugin } from './plugin'; - -export function plugin() { - return new Plugin(); -} -/* Public Types */ -export { TableVisExpressionFunctionDefinition } from './table_vis_fn'; diff --git a/src/plugins/vis_type_table_new/public/plugin.ts b/src/plugins/vis_type_table_new/public/plugin.ts deleted file mode 100644 index 9cc96c3e9895..000000000000 --- a/src/plugins/vis_type_table_new/public/plugin.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { CoreSetup, CoreStart, Plugin } from 'opensearch-dashboards/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; - -import { createTableVisFn } from './table_vis_fn'; -import { DataPublicPluginStart } from '../../data/public'; -import { setFormatService } from './services'; -import { getTableVisRenderer } from './table_vis_renderer'; - -export interface TableVisPluginSetupDependencies { - expressions: ReturnType<ExpressionsPublicPlugin['setup']>; -} - -export interface TableVisPluginStartDependencies { - data: DataPublicPluginStart; -} - -const setupTableVis = async (core: CoreSetup, { expressions }: TableVisPluginSetupDependencies) => { - const [coreStart] = await core.getStartServices(); - expressions.registerFunction(createTableVisFn); - expressions.registerRenderer(getTableVisRenderer(coreStart)); -}; - -export class TableVisPlugin implements Plugin<void, void> { - public async setup(core: CoreSetup, dependencies: TableVisPluginSetupDependencies) { - setupTableVis(core, dependencies); - } - - public start(core: CoreStart, { data }: TableVisPluginStartDependencies) { - setFormatService(data.fieldFormats); - } -} diff --git a/src/plugins/vis_type_table_new/public/services.ts b/src/plugins/vis_type_table_new/public/services.ts deleted file mode 100644 index f8ca4b574307..000000000000 --- a/src/plugins/vis_type_table_new/public/services.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { createGetterSetter } from '../../opensearch_dashboards_utils/public'; -import { DataPublicPluginStart } from '../../data/public'; - -export const [getFormatService, setFormatService] = createGetterSetter< - DataPublicPluginStart['fieldFormats'] ->('table data.fieldFormats'); diff --git a/src/plugins/vis_type_table_new/public/table_vis_fn.ts b/src/plugins/vis_type_table_new/public/table_vis_fn.ts deleted file mode 100644 index ec9eafc344af..000000000000 --- a/src/plugins/vis_type_table_new/public/table_vis_fn.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { i18n } from '@osd/i18n'; -import { tableVisResponseHandler, TableContext } from './table_vis_response_handler'; -import { - ExpressionFunctionDefinition, - OpenSearchDashboardsDatatable, - Render, -} from '../../expressions/public'; -import { TableVisConfig } from './types'; - -export type Input = OpenSearchDashboardsDatatable; - -interface Arguments { - visConfig: string | null; -} - -export interface TableVisRenderValue { - visData: TableContext; - visType: 'table'; - visConfig: TableVisConfig; -} - -export type TableVisExpressionFunctionDefinition = ExpressionFunctionDefinition< - 'opensearch_dashboards_table_new', - Input, - Arguments, - Render<TableVisRenderValue> ->; - -export const createTableVisFn = (): TableVisExpressionFunctionDefinition => ({ - name: 'opensearch_dashboards_table_new', - type: 'render', - inputTypes: ['opensearch_dashboards_datatable'], - help: i18n.translate('visTypeTableNew.function.help', { - defaultMessage: 'Table visualization', - }), - args: { - visConfig: { - types: ['string', 'null'], - default: '"{}"', - help: '', - }, - }, - fn(input, args) { - const visConfig = args.visConfig && JSON.parse(args.visConfig); - const convertedData = tableVisResponseHandler(input, visConfig); - - return { - type: 'render', - as: 'table_vis', - value: { - visData: convertedData, - visType: 'table', - visConfig, - }, - params: { - listenOnChange: true, - }, - }; - }, -}); diff --git a/src/plugins/vis_type_table_new/public/table_vis_response_handler.ts b/src/plugins/vis_type_table_new/public/table_vis_response_handler.ts deleted file mode 100644 index b1d41edfff8b..000000000000 --- a/src/plugins/vis_type_table_new/public/table_vis_response_handler.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { getFormatService } from './services'; -import { OpenSearchDashboardsDatatable } from '../../expressions/public'; -import { TableVisConfig } from './types'; - -export interface Table { - columns: OpenSearchDashboardsDatatable['columns']; - rows: OpenSearchDashboardsDatatable['rows']; -} - -export interface TableGroup { - table: OpenSearchDashboardsDatatable; - tables: Table[]; - title: string; - name: string; - key: any; - column: number; - row: number; -} - -export interface TableContext { - table?: Table; - tableGroups: TableGroup[]; - direction?: 'row' | 'column'; -} - -export function tableVisResponseHandler( - input: OpenSearchDashboardsDatatable, - config: TableVisConfig -): TableContext { - let table: Table | undefined; - const tableGroups: TableGroup[] = []; - let direction: TableContext['direction']; - - const split = config.splitColumn || config.splitRow; - - if (split) { - direction = config.splitRow ? 'row' : 'column'; - const splitColumnIndex = split[0].accessor; - const splitColumnFormatter = getFormatService().deserialize(split[0].format); - const splitColumn = input.columns[splitColumnIndex]; - const splitMap: { [key: string]: number } = {}; - let splitIndex = 0; - - input.rows.forEach((row, rowIndex) => { - const splitValue: any = row[splitColumn.id]; - - if (!splitMap.hasOwnProperty(splitValue as any)) { - (splitMap as any)[splitValue] = splitIndex++; - const tableGroup: TableGroup = { - title: `${splitColumnFormatter.convert(splitValue)}: ${splitColumn.name}`, - name: splitColumn.name, - key: splitValue, - column: splitColumnIndex, - row: rowIndex, - table: input, - tables: [], - }; - - tableGroup.tables.push({ - columns: input.columns, - rows: [], - }); - - tableGroups.push(tableGroup); - } - - const tableIndex = (splitMap as any)[splitValue]; - (tableGroups[tableIndex] as any).tables[0].rows.push(row); - }); - } else { - table = { - columns: input.columns, - rows: input.rows, - }; - } - - return { - table, - tableGroups, - direction, - }; -} diff --git a/src/plugins/vis_type_table_new/public/types.ts b/src/plugins/vis_type_table_new/public/types.ts deleted file mode 100644 index 0c5a9f9955e0..000000000000 --- a/src/plugins/vis_type_table_new/public/types.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { SchemaConfig } from 'src/plugins/visualizations/public'; -import { IFieldFormat } from 'src/plugins/data/public'; - -export interface TableVisConfig extends TableVisParams { - title: string; - metrics: SchemaConfig[]; - buckets: SchemaConfig[]; - splitRow?: SchemaConfig[]; - splitColumn?: SchemaConfig[]; -} - -export interface TableVisParams { - perPage: number | ''; - showPartialRows: boolean; - showMetricsAtAllLevels: boolean; -} - -export interface FormattedColumn { - id: string; - title: string; - formatter: IFieldFormat; - filterable: boolean; -} - -export interface ColumnWidth { - colIndex: number; - width: number; -} - -export interface SortColumn { - colIndex: number | null; - direction: 'asc' | 'desc' | null; -} - -export interface TableUiState { - sort: SortColumn; - setSort: (sort: SortColumn) => void; - width: ColumnWidth[]; - setWidth: (columnsWidth: ColumnWidth[]) => void; -} diff --git a/src/plugins/vis_type_table_new/public/utils/convert_to_formatted_data.ts b/src/plugins/vis_type_table_new/public/utils/convert_to_formatted_data.ts deleted file mode 100644 index cd997dfe5d5e..000000000000 --- a/src/plugins/vis_type_table_new/public/utils/convert_to_formatted_data.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { OpenSearchDashboardsDatatableRow } from 'src/plugins/expressions'; -import { Table } from '../table_vis_response_handler'; -import { TableVisConfig } from '../types'; -import { getFormatService } from '../services'; -import { FormattedColumn } from '../types'; -export interface FormattedDataProps { - formattedRows: OpenSearchDashboardsDatatableRow[]; - formattedColumns: FormattedColumn[]; -} - -export const convertToFormattedData = ( - table: Table, - visConfig: TableVisConfig -): FormattedDataProps => { - const { buckets, metrics } = visConfig; - const formattedRows: OpenSearchDashboardsDatatableRow[] = table.rows; - const formattedColumns: FormattedColumn[] = table.columns - .map(function (col, i) { - const isBucket = buckets.find((bucket) => bucket.accessor === i); - const dimension = isBucket || metrics.find((metric) => metric.accessor === i); - - if (!dimension) return undefined; - - const formatter = getFormatService().deserialize(dimension.format); - - const formattedColumn: FormattedColumn = { - id: col.id, - title: col.name, - formatter, - filterable: !!isBucket, - }; - - return formattedColumn; - }) - .filter((column): column is FormattedColumn => !!column); - - return { formattedRows, formattedColumns }; -}; diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js index 72921f8213e9..5dd326c0c586 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js @@ -37,7 +37,7 @@ jest.mock('../services'); jest.mock('../lib/vega', () => ({ vega: jest.requireActual('vega'), - vegaLite: jest.requireActual('vega-lite'), + vegaLite: jest.requireActual('vega-lite/src'), })); describe(`VegaParser.parseAsync`, () => { @@ -245,11 +245,15 @@ describe('VegaParser.parseSchema', () => { test( 'should not warn on current vega-lite version', + check('https://vega.github.io/schema/vega-lite/v5.json', true, 0) + ); + test( + 'should not warn on older vega-lite version', check('https://vega.github.io/schema/vega-lite/v4.json', true, 0) ); test( 'should warn on vega-lite version too new to be supported', - check('https://vega.github.io/schema/vega-lite/v5.json', true, 1) + check('https://vega.github.io/schema/vega-lite/v6.json', true, 1) ); }); diff --git a/src/plugins/vis_type_vega/public/default.spec.hjson b/src/plugins/vis_type_vega/public/default.spec.hjson index 98a0787c46c1..b4e126bfa3d3 100644 --- a/src/plugins/vis_type_vega/public/default.spec.hjson +++ b/src/plugins/vis_type_vega/public/default.spec.hjson @@ -1,31 +1,31 @@ { -/* + /* -Welcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments. + Welcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments. -This example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner. -*/ + This example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner. + */ - $schema: https://vega.github.io/schema/vega-lite/v4.json + $schema: https://vega.github.io/schema/vega-lite/v5.json title: Event counts from all indexes // Define the data source data: { url: { -/* -An object instead of a string for the "url" param is treated as an OpenSearch query. Anything inside this object is not part of the Vega language, but only understood by OpenSearch Dashboards and OpenSearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data. + /* + An object instead of a string for the "url" param is treated as an OpenSearch query. Anything inside this object is not part of the Vega language, but only understood by OpenSearch Dashboards and OpenSearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data. -OpenSearch Dashboards has a special handling for the fields surrounded by "%". They are processed before the the query is sent to OpenSearch. This way the query becomes context aware, and can use the time range and the dashboard filters. -*/ + OpenSearch Dashboards has a special handling for the fields surrounded by "%". They are processed before the the query is sent to OpenSearch. This way the query becomes context aware, and can use the time range and the dashboard filters. + */ // Apply dashboard context filters when set %context%: true // Filter the time picker (upper right corner) with this field %timefield%: @timestamp -/* -See .search() documentation for : https://opensearch.org/docs/latest/clients/javascript/ -*/ + /* + See .search() documentation for : https://opensearch.org/docs/latest/clients/javascript/ + */ // Which index to search index: _all @@ -53,29 +53,29 @@ See .search() documentation for : https://opensearch.org/docs/latest/clients/ja size: 0 } } -/* -OpenSearch will return results in this format: + /* + OpenSearch will return results in this format: -aggregations: { - time_buckets: { - buckets: [ - { - key_as_string: 2015-11-30T22:00:00.000Z - key: 1448920800000 - doc_count: 0 - }, - { - key_as_string: 2015-11-30T23:00:00.000Z - key: 1448924400000 - doc_count: 0 + aggregations: { + time_buckets: { + buckets: [ + { + key_as_string: 2015-11-30T22:00:00.000Z + key: 1448920800000 + doc_count: 0 + }, + { + key_as_string: 2015-11-30T23:00:00.000Z + key: 1448924400000 + doc_count: 0 + } + ... + ] } - ... - ] - } -} + } -For our graph, we only need the list of bucket values. Use the format.property to discard everything else. -*/ + For our graph, we only need the list of bucket values. Use the format.property to discard everything else. + */ format: {property: "aggregations.time_buckets.buckets"} } diff --git a/src/plugins/vis_type_vega/public/lib/vega.js b/src/plugins/vis_type_vega/public/lib/vega.js index 1c3068bae9ec..9d4f7983591f 100644 --- a/src/plugins/vis_type_vega/public/lib/vega.js +++ b/src/plugins/vis_type_vega/public/lib/vega.js @@ -28,8 +28,11 @@ * under the License. */ -import * as vegaLite from 'vega-lite/build-es5/vega-lite'; +// Build vega-lite from source for es5 compatibility +import { compile, version } from 'vega-lite/src'; import * as vega from 'vega/build-es5/vega'; import { expressionInterpreter as vegaExpressionInterpreter } from 'vega-interpreter/build/vega-interpreter.module'; +const vegaLite = { compile, version }; + export { vega, vegaLite, vegaExpressionInterpreter }; diff --git a/src/plugins/vis_type_vega/public/test_utils/default.spec.json b/src/plugins/vis_type_vega/public/test_utils/default.spec.json index 8cf763647115..266f2bd6eead 100644 --- a/src/plugins/vis_type_vega/public/test_utils/default.spec.json +++ b/src/plugins/vis_type_vega/public/test_utils/default.spec.json @@ -1,5 +1,5 @@ { - "$schema": "https://vega.github.io/schema/vega-lite/v4.json", + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "title": "Event counts from all indexes", "data": { "url": { diff --git a/src/plugins/vis_type_vega/public/test_utils/vegalite_graph.json b/src/plugins/vis_type_vega/public/test_utils/vegalite_graph.json index 5394f009b074..5a5e72f59022 100644 --- a/src/plugins/vis_type_vega/public/test_utils/vegalite_graph.json +++ b/src/plugins/vis_type_vega/public/test_utils/vegalite_graph.json @@ -1,5 +1,5 @@ { - "$schema": "https://vega.github.io/schema/vega-lite/v4.json", + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "data": { "format": {"property": "aggregations.time_buckets.buckets"}, "values": { diff --git a/src/plugins/vis_type_vega/public/vega_inspector/components/inspector_data_grid.tsx b/src/plugins/vis_type_vega/public/vega_inspector/components/inspector_data_grid.tsx index bf0eb584fc58..e5cd924e6e48 100644 --- a/src/plugins/vis_type_vega/public/vega_inspector/components/inspector_data_grid.tsx +++ b/src/plugins/vis_type_vega/public/vega_inspector/components/inspector_data_grid.tsx @@ -110,26 +110,26 @@ export const InspectorDataGrid = ({ columns, data, dataGridAriaLabel }: Inspecto }, [gridData, pagination]); // Resize - const [columnsWidth, setColumnsWidth] = useState<Record<string, number>>({}); + const [columnWidths, setColumnWidths] = useState<Record<string, number>>({}); const onColumnResize: EuiDataGridProps['onColumnResize'] = useCallback( ({ columnId, width }) => { - setColumnsWidth({ - ...columnsWidth, + setColumnWidths({ + ...columnWidths, [columnId]: width, }); }, - [columnsWidth] + [columnWidths] ); return ( <EuiDataGrid aria-label={dataGridAriaLabel} columns={columns.map((column) => { - if (columnsWidth[column.id]) { + if (columnWidths[column.id]) { return { ...column, - initialWidth: columnsWidth[column.id], + initialWidth: columnWidths[column.id], }; } return column; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js index 6f0a961f7a62..16fa0466bd5b 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js @@ -144,7 +144,8 @@ export class VegaMapView extends VegaBaseView { bindingsContainer: this._$controls.get(0), delayRepaint: mapConfig.delayRepaint, viewConfig: this._vegaViewConfig, - viewOptions: this._vegaViewOptions, + parseConfig: null, + parseOptions: this._vegaViewOptions, onWarning: this.onWarn.bind(this), onError: this.onError.bind(this), }, diff --git a/src/plugins/vis_type_vega/public/vega_visualization.test.js b/src/plugins/vis_type_vega/public/vega_visualization.test.js index f809a28bc0b0..2d4d648828b0 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.test.js +++ b/src/plugins/vis_type_vega/public/vega_visualization.test.js @@ -53,7 +53,7 @@ jest.mock('./default_spec', () => ({ jest.mock('./lib/vega', () => ({ vega: jest.requireActual('vega'), - vegaLite: jest.requireActual('vega-lite'), + vegaLite: jest.requireActual('vega-lite/src'), })); // FLAKY: https://github.com/elastic/kibana/issues/71713 diff --git a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts index 3fcc50de941e..c0c21266f0e4 100644 --- a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts +++ b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts @@ -44,7 +44,7 @@ const mockedSavedObjects = [ visState: JSON.stringify({ type: 'vega', params: { - spec: '{"$schema": "https://vega.github.io/schema/vega-lite/v4.json" }', + spec: '{"$schema": "https://vega.github.io/schema/vega-lite/v5.json" }', }, }), }, diff --git a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap index b3b4dc5e09b1..5712f358f7d1 100644 --- a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap +++ b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap @@ -12,16 +12,6 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles region_map function without buckets 1`] = `"regionmap visConfig='{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with showPartialRows=true and showMetricsAtAllLevels=false 1`] = `"opensearch_dashboards_table visConfig='{\\"showMetricsAtAllLevels\\":false,\\"showPartialRows\\":true,\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":4,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":5,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[0,3]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with showPartialRows=true and showMetricsAtAllLevels=true 1`] = `"opensearch_dashboards_table visConfig='{\\"showMetricsAtAllLevels\\":true,\\"showPartialRows\\":true,\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":1,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":2,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":4,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":5,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[0,3]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits 1`] = `"opensearch_dashboards_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[],\\"splitRow\\":[1,2]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits and buckets 1`] = `"opensearch_dashboards_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":1,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[3],\\"splitRow\\":[2,4]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function without splits or buckets 1`] = `"opensearch_dashboards_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":1,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[]}}' "`; - exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},\\"geohash\\":1,\\"geocentroid\\":3}}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles vega function 1`] = `"vega spec='this is a test' "`; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts index 90721f66c4af..5f240f82602c 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts @@ -128,84 +128,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => { expect(actual).toMatchSnapshot(); }); - describe('handles table function', () => { - it('without splits or buckets', () => { - const params = { foo: 'bar' }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 0 }, - { ...schemaConfig, accessor: 1 }, - ], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with splits', () => { - const params = { foo: 'bar' }; - const schemas = { - ...schemasDef, - split_row: [1, 2], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with splits and buckets', () => { - const params = { foo: 'bar' }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 0 }, - { ...schemaConfig, accessor: 1 }, - ], - split_row: [2, 4], - bucket: [3], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with showPartialRows=true and showMetricsAtAllLevels=true', () => { - const params = { - showMetricsAtAllLevels: true, - showPartialRows: true, - }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 1 }, - { ...schemaConfig, accessor: 2 }, - { ...schemaConfig, accessor: 4 }, - { ...schemaConfig, accessor: 5 }, - ], - bucket: [0, 3], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with showPartialRows=true and showMetricsAtAllLevels=false', () => { - const params = { - showMetricsAtAllLevels: false, - showPartialRows: true, - }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 1 }, - { ...schemaConfig, accessor: 2 }, - { ...schemaConfig, accessor: 4 }, - { ...schemaConfig, accessor: 5 }, - ], - bucket: [0, 3], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - }); - describe('handles region_map function', () => { it('without buckets', () => { const params = { metric: {} }; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index 7a28ae7ac394..1cbb3bc38879 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -278,13 +278,6 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = { const paramsArray = [paramsJson, uiStateJson].filter((param) => Boolean(param)); return `tsvb ${paramsArray.join(' ')}`; }, - table: (params, schemas) => { - const visConfig = { - ...params, - ...buildVisConfig.table(schemas, params), - }; - return `opensearch_dashboards_table ${prepareJson('visConfig', visConfig)}`; - }, region_map: (params, schemas) => { const visConfig = { ...params, @@ -309,26 +302,6 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = { }; const buildVisConfig: BuildVisConfigFunction = { - table: (schemas, visParams = {}) => { - const visConfig = {} as any; - const metrics = schemas.metric; - const buckets = schemas.bucket || []; - visConfig.dimensions = { - metrics, - buckets, - splitRow: schemas.split_row, - splitColumn: schemas.split_column, - }; - - if (visParams.showMetricsAtAllLevels === false && visParams.showPartialRows === true) { - // Handle case where user wants to see partial rows but not metrics at all levels. - // This requires calculating how many metrics will come back in the tabified response, - // and removing all metrics from the dimensions except the last set. - const metricsPerBucket = metrics.length / buckets.length; - visConfig.dimensions.metrics.splice(0, metricsPerBucket * buckets.length - metricsPerBucket); - } - return visConfig; - }, region_map: (schemas) => { const visConfig = {} as any; visConfig.metric = schemas.metric[0]; diff --git a/tasks/config/run.js b/tasks/config/run.js index 7e3afceef553..474c6c535f13 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -38,107 +38,82 @@ const OPENSEARCH_DASHBOARDS_INSTALL_DIR = module.exports = function () { const NODE = 'node'; const YARN = 'yarn'; - const scriptWithGithubChecks = ({ title, options, cmd, args }) => - process.env.CHECKS_REPORTER_ACTIVE === 'true' - ? { - options, - cmd: YARN, - args: ['run', 'github-checks-reporter', title, cmd, ...args], - } - : { options, cmd, args }; - const gruntTaskWithGithubChecks = (title, task) => - scriptWithGithubChecks({ - title, - cmd: YARN, - args: ['run', 'grunt', task], - }); return { // used by the test and jenkins:unit tasks // runs the eslint script to check for linting errors - eslint: scriptWithGithubChecks({ - title: 'eslint', + eslint: { cmd: NODE, args: ['scripts/eslint', '--no-cache'], - }), + }, - stylelint: scriptWithGithubChecks({ - title: 'stylelint', + stylelint: { cmd: NODE, args: ['scripts/stylelint'], - }), + }, // used by the test tasks // runs the check_file_casing script to ensure filenames use correct casing - checkFileCasing: scriptWithGithubChecks({ - title: 'Check file casing', + checkFileCasing: { cmd: NODE, args: [ 'scripts/check_file_casing', '--quiet', // only log errors, not warnings ], - }), + }, // used by the test tasks // runs the check_lockfile_symlinks script to ensure manifests with non-dev dependencies have adjacent lockfile symlinks - checkLockfileSymlinks: scriptWithGithubChecks({ - title: 'Check lockfile symlinks', + checkLockfileSymlinks: { cmd: NODE, args: [ 'scripts/check_lockfile_symlinks', '--quiet', // only log errors, not warnings ], - }), + }, // used by the test tasks // runs the check_published_api_changes script to ensure API changes are explictily accepted - checkDocApiChanges: scriptWithGithubChecks({ - title: 'Check core API changes', + checkDocApiChanges: { cmd: NODE, args: ['scripts/check_published_api_changes'], - }), + }, // used by the test and jenkins:unit tasks // runs the typecheck script to check for Typescript type errors - typeCheck: scriptWithGithubChecks({ - title: 'Type check', + typeCheck: { cmd: NODE, args: ['scripts/type_check'], - }), + }, // used by the test and jenkins:unit tasks // ensures that all typescript files belong to a typescript project - checkTsProjects: scriptWithGithubChecks({ - title: 'TypeScript - all files belong to a TypeScript project', + checkTsProjects: { cmd: NODE, args: ['scripts/check_ts_projects'], - }), + }, // used by the test and jenkins:unit tasks // runs the i18n_check script to check i18n engine usage - i18nCheck: scriptWithGithubChecks({ - title: 'Internationalization check', + i18nCheck: { cmd: NODE, args: ['scripts/i18n_check', '--ignore-missing'], - }), + }, - telemetryCheck: scriptWithGithubChecks({ - title: 'Telemetry Schema check', + telemetryCheck: { cmd: NODE, args: ['scripts/telemetry_check'], - }), + }, // used by the test:quick task // runs all node.js/server mocha tests - mocha: scriptWithGithubChecks({ - title: 'Mocha tests', + mocha: { cmd: NODE, args: ['scripts/mocha'], - }), + }, // used by the test:mochaCoverage task - mochaCoverage: scriptWithGithubChecks({ - title: 'Mocha tests coverage', + mochaCoverage: { cmd: YARN, args: [ 'nyc', @@ -147,25 +122,22 @@ module.exports = function () { NODE, 'scripts/mocha', ], - }), + }, - verifyNotice: scriptWithGithubChecks({ - title: 'Verify NOTICE.txt', + verifyNotice: { options: { wait: true, }, cmd: NODE, args: ['scripts/notice', '--validate'], - }), + }, - test_hardening: scriptWithGithubChecks({ - title: 'Node.js hardening tests', + test_hardening: { cmd: NODE, args: ['scripts/test_hardening.js'], - }), + }, - apiIntegrationTests: scriptWithGithubChecks({ - title: 'API integration tests', + apiIntegrationTests: { cmd: NODE, args: [ 'scripts/functional_tests', @@ -174,10 +146,9 @@ module.exports = function () { '--bail', '--debug', ], - }), + }, - serverIntegrationTests: scriptWithGithubChecks({ - title: 'Server integration tests', + serverIntegrationTests: { cmd: NODE, args: [ 'scripts/functional_tests', @@ -196,10 +167,9 @@ module.exports = function () { '--opensearch-dashboards-install-dir', OPENSEARCH_DASHBOARDS_INSTALL_DIR, ], - }), + }, - interpreterFunctionalTestsRelease: scriptWithGithubChecks({ - title: 'Interpreter functional tests', + interpreterFunctionalTestsRelease: { cmd: NODE, args: [ 'scripts/functional_tests', @@ -210,10 +180,9 @@ module.exports = function () { '--opensearch-dashboards-install-dir', OPENSEARCH_DASHBOARDS_INSTALL_DIR, ], - }), + }, - pluginFunctionalTestsRelease: scriptWithGithubChecks({ - title: 'Plugin functional tests', + pluginFunctionalTestsRelease: { cmd: NODE, args: [ 'scripts/functional_tests', @@ -222,10 +191,9 @@ module.exports = function () { '--bail', '--debug', ], - }), + }, - exampleFunctionalTestsRelease: scriptWithGithubChecks({ - title: 'Example functional tests', + exampleFunctionalTestsRelease: { cmd: NODE, args: [ 'scripts/functional_tests', @@ -234,10 +202,9 @@ module.exports = function () { '--bail', '--debug', ], - }), + }, - functionalTests: scriptWithGithubChecks({ - title: 'Functional tests', + functionalTests: { cmd: NODE, args: [ 'scripts/functional_tests', @@ -246,20 +213,25 @@ module.exports = function () { '--bail', '--debug', ], - }), + }, - licenses: scriptWithGithubChecks({ - title: 'Check licenses', + licenses: { cmd: NODE, args: ['scripts/check_licenses', '--dev'], - }), + }, - test_jest: gruntTaskWithGithubChecks('Jest tests', 'test:jest'), - test_jest_integration: gruntTaskWithGithubChecks( - 'Jest integration tests', - 'test:jest_integration' - ), - test_projects: gruntTaskWithGithubChecks('Project tests', 'test:projects'), + test_jest: { + cmd: YARN, + args: ['run', 'grunt', 'test:jest'], + }, + test_jest_integration: { + cmd: YARN, + args: ['run', 'grunt', 'test:jest_integration'], + }, + test_projects: { + cmd: YARN, + args: ['run', 'grunt', 'test:projects'], + }, ...getFunctionalTestGroupRunConfigs({ opensearchDashboardsInstallDir: OPENSEARCH_DASHBOARDS_INSTALL_DIR, diff --git a/test/functional/apps/console/_console.ts b/test/functional/apps/console/_console.ts index f5cd93c35280..302197bb4550 100644 --- a/test/functional/apps/console/_console.ts +++ b/test/functional/apps/console/_console.ts @@ -71,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('default request response should include `"timed_out" : false`', async () => { - const expectedResponseContains = '"timed_out" : false,'; + const expectedResponseContains = '"timed_out": false,'; await PageObjects.console.clickPlay(); await retry.try(async () => { const actualResponse = await PageObjects.console.getResponse(); diff --git a/test/functional/apps/dashboard/dashboard_filtering.js b/test/functional/apps/dashboard/dashboard_filtering.js index fd05f5b134e9..deb2ae399240 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.js +++ b/test/functional/apps/dashboard/dashboard_filtering.js @@ -98,10 +98,6 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.seriesElementCount(0); }); - it('data tables are filtered', async () => { - await dashboardExpect.dataTableRowCount(0); - }); - it('goal and guages are filtered', async () => { await dashboardExpect.goalAndGuageLabelsExist(['0', '0%']); }); @@ -159,10 +155,6 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.seriesElementCount(0); }); - it('data tables are filtered', async () => { - await dashboardExpect.dataTableRowCount(0); - }); - it('goal and guages are filtered', async () => { await dashboardExpect.goalAndGuageLabelsExist(['0', '0%']); }); @@ -212,10 +204,6 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.seriesElementCount(3); }); - it('data tables', async () => { - await dashboardExpect.dataTableRowCount(10); - }); - it('goal and guages', async () => { await dashboardExpect.goalAndGuageLabelsExist(['39.958%', '7,544']); }); diff --git a/test/functional/apps/dashboard/dashboard_grid.js b/test/functional/apps/dashboard/dashboard_grid.js index 3e68f33f2043..0da0c58573aa 100644 --- a/test/functional/apps/dashboard/dashboard_grid.js +++ b/test/functional/apps/dashboard/dashboard_grid.js @@ -52,7 +52,7 @@ export default function ({ getService, getPageObjects }) { describe('move panel', () => { // Specific test after https://github.com/elastic/kibana/issues/14764 fix it('Can move panel from bottom to top row', async () => { - const lastVisTitle = 'Rendering Test: datatable'; + const lastVisTitle = 'Rendering Test: pie'; const panelTitleBeforeMove = await dashboardPanelActions.getPanelHeading(lastVisTitle); const position1 = await panelTitleBeforeMove.getPosition(); await browser.dragAndDrop( diff --git a/test/functional/apps/dashboard/embeddable_rendering.js b/test/functional/apps/dashboard/embeddable_rendering.js index b11955a1e24d..5cacc85cb0ef 100644 --- a/test/functional/apps/dashboard/embeddable_rendering.js +++ b/test/functional/apps/dashboard/embeddable_rendering.js @@ -67,7 +67,6 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.markdownWithValuesExists(["I'm a markdown!"]); await dashboardExpect.vegaTextsExist(['5,000']); await dashboardExpect.goalAndGuageLabelsExist(['62.925%', '55.625%', '11.915 GB']); - await dashboardExpect.dataTableRowCount(5); await dashboardExpect.tagCloudWithValuesFound(['CN', 'IN', 'US', 'BR', 'ID']); // TODO add test for 'region map viz' // TODO add test for 'tsvb gauge' viz @@ -90,7 +89,6 @@ export default function ({ getService, getPageObjects }) { const expectNoDataRenders = async () => { await pieChart.expectPieSliceCount(0); await dashboardExpect.seriesElementCount(0); - await dashboardExpect.dataTableRowCount(0); await dashboardExpect.savedSearchRowCount(0); await dashboardExpect.inputControlItemCount(5); await dashboardExpect.metricValuesExist(['0']); @@ -146,7 +144,7 @@ export default function ({ getService, getPageObjects }) { visNames.push(await dashboardAddPanel.addVisualization('Filter Bytes Test: vega')); await PageObjects.header.waitUntilLoadingHasFinished(); await dashboardExpect.visualizationsArePresent(visNames); - expect(visNames.length).to.be.equal(27); + expect(visNames.length).to.be.equal(26); await PageObjects.dashboard.waitForRenderComplete(); }); @@ -157,7 +155,7 @@ export default function ({ getService, getPageObjects }) { await dashboardAddPanel.closeAddPanel(); await PageObjects.header.waitUntilLoadingHasFinished(); await dashboardExpect.visualizationsArePresent(visAndSearchNames); - expect(visAndSearchNames.length).to.be.equal(28); + expect(visAndSearchNames.length).to.be.equal(27); await PageObjects.dashboard.waitForRenderComplete(); await PageObjects.dashboard.saveDashboard('embeddable rendering test', { diff --git a/test/functional/apps/dashboard/url_field_formatter.ts b/test/functional/apps/dashboard/url_field_formatter.ts index 0f7d81720634..798adeb99bf6 100644 --- a/test/functional/apps/dashboard/url_field_formatter.ts +++ b/test/functional/apps/dashboard/url_field_formatter.ts @@ -74,14 +74,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await settings.controlChangeSave(); }); - it('applied on dashboard', async () => { - await common.navigateToApp('dashboard'); - await dashboard.loadSavedDashboard('dashboard with everything'); - await dashboard.waitForRenderComplete(); - const fieldLink = await visChart.getFieldLinkInVisTable(`${fieldName}: Descending`, 1); - await clickFieldAndCheckUrl(fieldLink); - }); - it('applied on discover', async () => { await common.navigateToApp('discover'); await timePicker.setAbsoluteRange( diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js deleted file mode 100644 index fde30412bdcb..000000000000 --- a/test/functional/apps/visualize/_data_table.js +++ /dev/null @@ -1,485 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '@osd/expect'; - -export default function ({ getService, getPageObjects }) { - const log = getService('log'); - const inspector = getService('inspector'); - const testSubjects = getService('testSubjects'); - const retry = getService('retry'); - const filterBar = getService('filterBar'); - const PageObjects = getPageObjects(['visualize', 'timePicker', 'visEditor', 'visChart']); - - describe('data table', function indexPatternCreation() { - const vizName1 = 'Visualization DataTable'; - - before(async function () { - log.debug('navigateToApp visualize'); - await PageObjects.visualize.navigateToNewVisualization(); - log.debug('clickDataTable'); - await PageObjects.visualize.clickDataTable(); - log.debug('clickNewSearch'); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - log.debug('Bucket = Split rows'); - await PageObjects.visEditor.clickBucket('Split rows'); - log.debug('Aggregation = Histogram'); - await PageObjects.visEditor.selectAggregation('Histogram'); - log.debug('Field = bytes'); - await PageObjects.visEditor.selectField('bytes'); - log.debug('Interval = 2000'); - await PageObjects.visEditor.setInterval('2000', { type: 'numeric' }); - await PageObjects.visEditor.clickGo(); - }); - - it('should allow applying changed params', async () => { - await PageObjects.visEditor.setInterval('1', { type: 'numeric', append: true }); - const interval = await PageObjects.visEditor.getNumericInterval(); - expect(interval).to.be('20001'); - const isApplyButtonEnabled = await PageObjects.visEditor.isApplyEnabled(); - expect(isApplyButtonEnabled).to.be(true); - }); - - it('should allow reseting changed params', async () => { - await PageObjects.visEditor.clickReset(); - const interval = await PageObjects.visEditor.getNumericInterval(); - expect(interval).to.be('2000'); - }); - - it('should be able to save and load', async function () { - await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); - - await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visChart.waitForVisualization(); - }); - - it('should have inspector enabled', async function () { - await inspector.expectIsEnabled(); - }); - - it('should show correct data', function () { - const expectedChartData = [ - ['0B', '2,088'], - ['1.953KB', '2,748'], - ['3.906KB', '2,707'], - ['5.859KB', '2,876'], - ['7.813KB', '2,863'], - ['9.766KB', '147'], - ['11.719KB', '148'], - ['13.672KB', '129'], - ['15.625KB', '161'], - ['17.578KB', '137'], - ]; - - return retry.try(async function () { - await inspector.open(); - await inspector.expectTableData(expectedChartData); - await inspector.close(); - }); - }); - - it('should show percentage columns', async () => { - async function expectValidTableData() { - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql([ - '≥ 0B and < 1,000B', - '1,351 64.703%', - '≥ 1,000B and < 1.953KB', - '737 35.297%', - ]); - } - - // load a plain table - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Range'); - await PageObjects.visEditor.selectField('bytes'); - await PageObjects.visEditor.clickGo(); - await PageObjects.visEditor.clickOptionsTab(); - await PageObjects.visEditor.setSelectByOptionText( - 'datatableVisualizationPercentageCol', - 'Count' - ); - await PageObjects.visEditor.clickGo(); - - await expectValidTableData(); - - // check that it works after a save and reload - const SAVE_NAME = 'viz w/ percents'; - await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(SAVE_NAME); - - await PageObjects.visualize.loadSavedVisualization(SAVE_NAME); - await PageObjects.visChart.waitForVisualization(); - - await expectValidTableData(); - - // check that it works after selecting a column that's deleted - await PageObjects.visEditor.clickDataTab(); - await PageObjects.visEditor.clickBucket('Metric', 'metrics'); - await PageObjects.visEditor.selectAggregation('Average', 'metrics'); - await PageObjects.visEditor.selectField('bytes', 'metrics'); - await PageObjects.visEditor.removeDimension(1); - await PageObjects.visEditor.clickGo(); - await PageObjects.visEditor.clickOptionsTab(); - - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql([ - '≥ 0B and < 1,000B', - '344.094B', - '≥ 1,000B and < 1.953KB', - '1.697KB', - ]); - }); - - it('should show correct data when using average pipeline aggregation', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Metric', 'metrics'); - await PageObjects.visEditor.selectAggregation('Average Bucket', 'metrics'); - await PageObjects.visEditor.selectAggregation('Terms', 'metrics', 'buckets'); - await PageObjects.visEditor.selectField('geo.src', 'metrics', 'buckets'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data.split('\n')); - expect(data.trim().split('\n')).to.be.eql(['14,004 1,412.6']); - }); - - it('should show correct data for a data table with date histogram', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Date Histogram'); - await PageObjects.visEditor.selectField('@timestamp'); - await PageObjects.visEditor.setInterval('Day'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data.split('\n')); - expect(data.trim().split('\n')).to.be.eql([ - '2015-09-20', - '4,757', - '2015-09-21', - '4,614', - '2015-09-22', - '4,633', - ]); - }); - - it('should show correct data for a data table with date histogram', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Date Histogram'); - await PageObjects.visEditor.selectField('@timestamp'); - await PageObjects.visEditor.setInterval('Day'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql([ - '2015-09-20', - '4,757', - '2015-09-21', - '4,614', - '2015-09-22', - '4,633', - ]); - }); - - it('should correctly filter for applied time filter on the main timefield', async () => { - await filterBar.addFilter('@timestamp', 'is between', '2015-09-19', '2015-09-21'); - await PageObjects.visChart.waitForVisualizationRenderingStabilized(); - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); - }); - - it('should correctly filter for pinned filters', async () => { - await filterBar.toggleFilterPinned('@timestamp'); - await PageObjects.visChart.waitForVisualizationRenderingStabilized(); - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); - }); - - it('should show correct data for a data table with top hits', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickMetricEditor(); - await PageObjects.visEditor.selectAggregation('Top Hit', 'metrics'); - await PageObjects.visEditor.selectField('agent.raw', 'metrics'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data); - expect(data.length).to.be.greaterThan(0); - }); - - it('should show correct data for a data table with range agg', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Range'); - await PageObjects.visEditor.selectField('bytes'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql([ - '≥ 0B and < 1,000B', - '1,351', - '≥ 1,000B and < 1.953KB', - '737', - ]); - }); - - describe('otherBucket', () => { - before(async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Terms'); - await PageObjects.visEditor.selectField('extension.raw'); - await PageObjects.visEditor.setSize(2); - await PageObjects.visEditor.clickGo(); - - await PageObjects.visEditor.toggleOtherBucket(); - await PageObjects.visEditor.toggleMissingBucket(); - await PageObjects.visEditor.clickGo(); - }); - - it('should show correct data', async () => { - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - ['jpg', '9,109'], - ['css', '2,159'], - ['Other', '2,736'], - ]); - }); - - it('should apply correct filter', async () => { - await PageObjects.visChart.filterOnTableCell(1, 3); - await PageObjects.visChart.waitForVisualizationRenderingStabilized(); - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - ['png', '1,373'], - ['gif', '918'], - ['Other', '445'], - ]); - }); - }); - - describe('metricsOnAllLevels', () => { - before(async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Terms'); - await PageObjects.visEditor.selectField('extension.raw'); - await PageObjects.visEditor.setSize(2); - await PageObjects.visEditor.toggleOpenEditor(2, 'false'); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Terms'); - await PageObjects.visEditor.selectField('geo.dest'); - await PageObjects.visEditor.toggleOpenEditor(3, 'false'); - await PageObjects.visEditor.clickGo(); - }); - - it('should show correct data without showMetricsAtAllLevels', async () => { - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - ['jpg', 'CN', '1,718'], - ['jpg', 'IN', '1,511'], - ['jpg', 'US', '770'], - ['jpg', 'ID', '314'], - ['jpg', 'PK', '244'], - ['css', 'CN', '422'], - ['css', 'IN', '346'], - ['css', 'US', '189'], - ['css', 'ID', '68'], - ['css', 'BR', '58'], - ]); - }); - - it('should show correct data without showMetricsAtAllLevels even if showPartialRows is selected', async () => { - await PageObjects.visEditor.clickOptionsTab(); - await testSubjects.setCheckbox('showPartialRows', 'check'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - ['jpg', 'CN', '1,718'], - ['jpg', 'IN', '1,511'], - ['jpg', 'US', '770'], - ['jpg', 'ID', '314'], - ['jpg', 'PK', '244'], - ['css', 'CN', '422'], - ['css', 'IN', '346'], - ['css', 'US', '189'], - ['css', 'ID', '68'], - ['css', 'BR', '58'], - ]); - }); - - it('should show metrics on each level', async () => { - await PageObjects.visEditor.clickOptionsTab(); - await testSubjects.setCheckbox('showMetricsAtAllLevels', 'check'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - ['jpg', '9,109', 'CN', '1,718'], - ['jpg', '9,109', 'IN', '1,511'], - ['jpg', '9,109', 'US', '770'], - ['jpg', '9,109', 'ID', '314'], - ['jpg', '9,109', 'PK', '244'], - ['css', '2,159', 'CN', '422'], - ['css', '2,159', 'IN', '346'], - ['css', '2,159', 'US', '189'], - ['css', '2,159', 'ID', '68'], - ['css', '2,159', 'BR', '58'], - ]); - }); - - it('should show metrics other than count on each level', async () => { - await PageObjects.visEditor.clickDataTab(); - await PageObjects.visEditor.clickBucket('Metric', 'metrics'); - await PageObjects.visEditor.selectAggregation('Average', 'metrics'); - await PageObjects.visEditor.selectField('bytes', 'metrics'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - ['jpg', '9,109', '5.469KB', 'CN', '1,718', '5.477KB'], - ['jpg', '9,109', '5.469KB', 'IN', '1,511', '5.456KB'], - ['jpg', '9,109', '5.469KB', 'US', '770', '5.371KB'], - ['jpg', '9,109', '5.469KB', 'ID', '314', '5.424KB'], - ['jpg', '9,109', '5.469KB', 'PK', '244', '5.41KB'], - ['css', '2,159', '5.566KB', 'CN', '422', '5.712KB'], - ['css', '2,159', '5.566KB', 'IN', '346', '5.754KB'], - ['css', '2,159', '5.566KB', 'US', '189', '5.333KB'], - ['css', '2,159', '5.566KB', 'ID', '68', '4.82KB'], - ['css', '2,159', '5.566KB', 'BR', '58', '5.915KB'], - ]); - }); - }); - - describe('split tables', () => { - before(async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split table'); - await PageObjects.visEditor.selectAggregation('Terms'); - await PageObjects.visEditor.selectField('extension.raw'); - await PageObjects.visEditor.setSize(2); - await PageObjects.visEditor.toggleOpenEditor(2, 'false'); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Terms'); - await PageObjects.visEditor.selectField('geo.dest'); - await PageObjects.visEditor.setSize(3, 3); - await PageObjects.visEditor.toggleOpenEditor(3, 'false'); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Terms'); - await PageObjects.visEditor.selectField('geo.src'); - await PageObjects.visEditor.setSize(3, 4); - await PageObjects.visEditor.toggleOpenEditor(4, 'false'); - await PageObjects.visEditor.clickGo(); - }); - - it('should have a splitted table', async () => { - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - [ - ['CN', 'CN', '330'], - ['CN', 'IN', '274'], - ['CN', 'US', '140'], - ['IN', 'CN', '286'], - ['IN', 'IN', '281'], - ['IN', 'US', '133'], - ['US', 'CN', '135'], - ['US', 'IN', '134'], - ['US', 'US', '52'], - ], - [ - ['CN', 'CN', '90'], - ['CN', 'IN', '84'], - ['CN', 'US', '27'], - ['IN', 'CN', '69'], - ['IN', 'IN', '58'], - ['IN', 'US', '34'], - ['US', 'IN', '36'], - ['US', 'CN', '29'], - ['US', 'US', '13'], - ], - ]); - }); - - it('should show metrics for split bucket when using showMetricsAtAllLevels', async () => { - await PageObjects.visEditor.clickOptionsTab(); - await testSubjects.setCheckbox('showMetricsAtAllLevels', 'check'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisContent(); - expect(data).to.be.eql([ - [ - ['CN', '1,718', 'CN', '330'], - ['CN', '1,718', 'IN', '274'], - ['CN', '1,718', 'US', '140'], - ['IN', '1,511', 'CN', '286'], - ['IN', '1,511', 'IN', '281'], - ['IN', '1,511', 'US', '133'], - ['US', '770', 'CN', '135'], - ['US', '770', 'IN', '134'], - ['US', '770', 'US', '52'], - ], - [ - ['CN', '422', 'CN', '90'], - ['CN', '422', 'IN', '84'], - ['CN', '422', 'US', '27'], - ['IN', '346', 'CN', '69'], - ['IN', '346', 'IN', '58'], - ['IN', '346', 'US', '34'], - ['US', '189', 'IN', '36'], - ['US', '189', 'CN', '29'], - ['US', '189', 'US', '13'], - ], - ]); - }); - }); - }); -} diff --git a/test/functional/apps/visualize/_data_table_nontimeindex.js b/test/functional/apps/visualize/_data_table_nontimeindex.js deleted file mode 100644 index 37ad4c2a9eb6..000000000000 --- a/test/functional/apps/visualize/_data_table_nontimeindex.js +++ /dev/null @@ -1,170 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '@osd/expect'; - -export default function ({ getService, getPageObjects }) { - const log = getService('log'); - const inspector = getService('inspector'); - const retry = getService('retry'); - const filterBar = getService('filterBar'); - const renderable = getService('renderable'); - const PageObjects = getPageObjects(['visualize', 'visEditor', 'header', 'visChart']); - - describe('data table with index without time filter', function indexPatternCreation() { - const vizName1 = 'Visualization DataTable without time filter'; - - before(async function () { - log.debug('navigateToApp visualize'); - await PageObjects.visualize.navigateToNewVisualization(); - log.debug('clickDataTable'); - await PageObjects.visualize.clickDataTable(); - log.debug('clickNewSearch'); - await PageObjects.visualize.clickNewSearch( - PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED - ); - log.debug('Bucket = Split Rows'); - await PageObjects.visEditor.clickBucket('Split rows'); - log.debug('Aggregation = Histogram'); - await PageObjects.visEditor.selectAggregation('Histogram'); - log.debug('Field = bytes'); - await PageObjects.visEditor.selectField('bytes'); - log.debug('Interval = 2000'); - await PageObjects.visEditor.setInterval('2000', { type: 'numeric' }); - await PageObjects.visEditor.clickGo(); - }); - - it('should allow applying changed params', async () => { - await PageObjects.visEditor.setInterval('1', { type: 'numeric', append: true }); - const interval = await PageObjects.visEditor.getNumericInterval(); - expect(interval).to.be('20001'); - const isApplyButtonEnabled = await PageObjects.visEditor.isApplyEnabled(); - expect(isApplyButtonEnabled).to.be(true); - }); - - it('should allow reseting changed params', async () => { - await PageObjects.visEditor.clickReset(); - const interval = await PageObjects.visEditor.getNumericInterval(); - expect(interval).to.be('2000'); - }); - - it('should be able to save and load', async function () { - await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); - - await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visChart.waitForVisualization(); - }); - - it('should have inspector enabled', async function () { - await inspector.expectIsEnabled(); - }); - - it('should show correct data', function () { - const expectedChartData = [ - ['0B', '2,088'], - ['1.953KB', '2,748'], - ['3.906KB', '2,707'], - ['5.859KB', '2,876'], - ['7.813KB', '2,863'], - ['9.766KB', '147'], - ['11.719KB', '148'], - ['13.672KB', '129'], - ['15.625KB', '161'], - ['17.578KB', '137'], - ]; - - return retry.try(async function () { - await inspector.open(); - await inspector.expectTableData(expectedChartData); - await inspector.close(); - }); - }); - - it('should show correct data when using average pipeline aggregation', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch( - PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED - ); - await PageObjects.visEditor.clickBucket('Metric', 'metrics'); - await PageObjects.visEditor.selectAggregation('Average Bucket', 'metrics'); - await PageObjects.visEditor.selectAggregation('Terms', 'metrics', 'buckets'); - await PageObjects.visEditor.selectField('geo.src', 'metrics', 'buckets'); - await PageObjects.visEditor.clickGo(); - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data.split('\n')); - expect(data.trim().split('\n')).to.be.eql(['14,004 1,412.6']); - }); - - describe('data table with date histogram', async () => { - before(async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch( - PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED - ); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Date Histogram'); - await PageObjects.visEditor.selectField('@timestamp'); - await PageObjects.visEditor.setInterval('Day'); - await PageObjects.visEditor.clickGo(); - }); - - it('should show correct data', async () => { - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data.split('\n')); - expect(data.trim().split('\n')).to.be.eql([ - '2015-09-20', - '4,757', - '2015-09-21', - '4,614', - '2015-09-22', - '4,633', - ]); - }); - - it('should correctly filter for applied time filter on the main timefield', async () => { - await filterBar.addFilter('@timestamp', 'is between', '2015-09-19', '2015-09-21'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await renderable.waitForRender(); - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); - }); - - it('should correctly filter for pinned filters', async () => { - await filterBar.toggleFilterPinned('@timestamp'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await renderable.waitForRender(); - const data = await PageObjects.visChart.getTableVisData(); - expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); - }); - }); - }); -} diff --git a/test/functional/apps/visualize/_data_table_notimeindex_filters.ts b/test/functional/apps/visualize/_data_table_notimeindex_filters.ts deleted file mode 100644 index 3661a4847bd6..000000000000 --- a/test/functional/apps/visualize/_data_table_notimeindex_filters.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '@osd/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const log = getService('log'); - const filterBar = getService('filterBar'); - const renderable = getService('renderable'); - const dashboardAddPanel = getService('dashboardAddPanel'); - const PageObjects = getPageObjects([ - 'common', - 'visualize', - 'header', - 'dashboard', - 'timePicker', - 'visEditor', - 'visChart', - ]); - - describe('data table with index without time filter filters', function indexPatternCreation() { - const vizName1 = 'Visualization DataTable w/o time filter'; - - before(async function () { - log.debug('navigateToApp visualize'); - await PageObjects.visualize.navigateToNewVisualization(); - log.debug('clickDataTable'); - await PageObjects.visualize.clickDataTable(); - log.debug('clickNewSearch'); - await PageObjects.visualize.clickNewSearch( - PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED - ); - log.debug('Bucket = Split Rows'); - await PageObjects.visEditor.clickBucket('Split rows'); - log.debug('Aggregation = Histogram'); - await PageObjects.visEditor.selectAggregation('Histogram'); - log.debug('Field = bytes'); - await PageObjects.visEditor.selectField('bytes'); - log.debug('Interval = 2000'); - await PageObjects.visEditor.setInterval('2000', { type: 'numeric' }); - await PageObjects.visEditor.clickGo(); - }); - - it('should be able to save and load', async function () { - await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); - - await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visChart.waitForVisualization(); - }); - - it('timefilter should be disabled', async () => { - const isOff = await PageObjects.timePicker.isOff(); - expect(isOff).to.be(true); - }); - - // test to cover bug #54548 - add this visualization to a dashboard and filter - it('should add to dashboard and allow filtering', async function () { - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.clickNewDashboard(); - await dashboardAddPanel.addVisualization(vizName1); - - // hover and click on cell to filter - await PageObjects.visChart.filterOnTableCell('1', '2'); - - await PageObjects.header.waitUntilLoadingHasFinished(); - await renderable.waitForRender(); - const filterCount = await filterBar.getFilterCount(); - expect(filterCount).to.be(1); - - await filterBar.removeAllFilters(); - }); - }); -} diff --git a/test/functional/apps/visualize/_embedding_chart.js b/test/functional/apps/visualize/_embedding_chart.js deleted file mode 100644 index c47319303dcf..000000000000 --- a/test/functional/apps/visualize/_embedding_chart.js +++ /dev/null @@ -1,187 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '@osd/expect'; - -export default function ({ getService, getPageObjects }) { - const filterBar = getService('filterBar'); - const log = getService('log'); - const renderable = getService('renderable'); - const embedding = getService('embedding'); - const PageObjects = getPageObjects([ - 'visualize', - 'visEditor', - 'visChart', - 'header', - 'timePicker', - ]); - - describe('embedding', () => { - describe('a data table', () => { - before(async function () { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Date Histogram'); - await PageObjects.visEditor.selectField('@timestamp'); - await PageObjects.visEditor.toggleOpenEditor(2, 'false'); - await PageObjects.visEditor.clickBucket('Split rows'); - await PageObjects.visEditor.selectAggregation('Histogram'); - await PageObjects.visEditor.selectField('bytes'); - await PageObjects.visEditor.setInterval('2000', { type: 'numeric', aggNth: 3 }); - await PageObjects.visEditor.clickGo(); - }); - - it('should allow opening table vis in embedded mode', async () => { - await embedding.openInEmbeddedMode(); - await renderable.waitForRender(); - - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data.split('\n')); - expect(data.trim().split('\n')).to.be.eql([ - '2015-09-20 00:00', - '0B', - '5', - '2015-09-20 00:00', - '1.953KB', - '5', - '2015-09-20 00:00', - '3.906KB', - '9', - '2015-09-20 00:00', - '5.859KB', - '4', - '2015-09-20 00:00', - '7.813KB', - '14', - '2015-09-20 03:00', - '0B', - '32', - '2015-09-20 03:00', - '1.953KB', - '33', - '2015-09-20 03:00', - '3.906KB', - '45', - '2015-09-20 03:00', - '5.859KB', - '31', - '2015-09-20 03:00', - '7.813KB', - '48', - ]); - }); - - it('should allow to filter in embedded mode', async () => { - await filterBar.addFilter('@timestamp', 'is between', '2015-09-21', '2015-09-23'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await renderable.waitForRender(); - - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data.split('\n')); - expect(data.trim().split('\n')).to.be.eql([ - '2015-09-21 00:00', - '0B', - '7', - '2015-09-21 00:00', - '1.953KB', - '9', - '2015-09-21 00:00', - '3.906KB', - '9', - '2015-09-21 00:00', - '5.859KB', - '6', - '2015-09-21 00:00', - '7.813KB', - '10', - '2015-09-21 00:00', - '11.719KB', - '1', - '2015-09-21 03:00', - '0B', - '28', - '2015-09-21 03:00', - '1.953KB', - '39', - '2015-09-21 03:00', - '3.906KB', - '36', - '2015-09-21 03:00', - '5.859KB', - '43', - ]); - }); - - it('should allow to change timerange from the visualization in embedded mode', async () => { - await PageObjects.visChart.filterOnTableCell(1, 7); - await PageObjects.header.waitUntilLoadingHasFinished(); - await renderable.waitForRender(); - - const data = await PageObjects.visChart.getTableVisData(); - log.debug(data.split('\n')); - expect(data.trim().split('\n')).to.be.eql([ - '03:00', - '0B', - '1', - '03:00', - '1.953KB', - '1', - '03:00', - '3.906KB', - '1', - '03:00', - '5.859KB', - '2', - '03:10', - '0B', - '1', - '03:10', - '5.859KB', - '1', - '03:10', - '7.813KB', - '1', - '03:15', - '0B', - '1', - '03:15', - '1.953KB', - '1', - '03:20', - '1.953KB', - '1', - ]); - }); - }); - }); -} diff --git a/test/functional/apps/visualize/_histogram_request_start.js b/test/functional/apps/visualize/_histogram_request_start.js deleted file mode 100644 index f797b83af730..000000000000 --- a/test/functional/apps/visualize/_histogram_request_start.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '@osd/expect'; - -export default function ({ getService, getPageObjects }) { - const log = getService('log'); - const retry = getService('retry'); - const PageObjects = getPageObjects([ - 'common', - 'visualize', - 'visEditor', - 'visChart', - 'timePicker', - ]); - - describe('histogram agg onSearchRequestStart', function () { - before(async function () { - log.debug('navigateToApp visualize'); - await PageObjects.visualize.navigateToNewVisualization(); - log.debug('clickDataTable'); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - log.debug('Bucket = Split Rows'); - await PageObjects.visEditor.clickBucket('Split rows'); - log.debug('Aggregation = Histogram'); - await PageObjects.visEditor.selectAggregation('Histogram'); - log.debug('Field = machine.ram'); - await PageObjects.visEditor.selectField('machine.ram'); - }); - - describe('interval parameter uses autoBounds', function () { - it('should use provided value when number of generated buckets is less than histogram:maxBars', async function () { - const providedInterval = 2400000000; - log.debug(`Interval = ${providedInterval}`); - await PageObjects.visEditor.setInterval(providedInterval, { type: 'numeric' }); - await PageObjects.visEditor.clickGo(); - await retry.try(async () => { - const data = await PageObjects.visChart.getTableVisData(); - const dataArray = data.replace(/,/g, '').split('\n'); - expect(dataArray.length).to.eql(20); - const bucketStart = parseInt(dataArray[0], 10); - const bucketEnd = parseInt(dataArray[2], 10); - const actualInterval = bucketEnd - bucketStart; - expect(actualInterval).to.eql(providedInterval); - }); - }); - - it('should scale value to round number when number of generated buckets is greater than histogram:maxBars', async function () { - const providedInterval = 100; - log.debug(`Interval = ${providedInterval}`); - await PageObjects.visEditor.setInterval(providedInterval, { type: 'numeric' }); - await PageObjects.visEditor.clickGo(); - await PageObjects.common.sleep(1000); //fix this - await retry.try(async () => { - const data = await PageObjects.visChart.getTableVisData(); - const dataArray = data.replace(/,/g, '').split('\n'); - expect(dataArray.length).to.eql(20); - const bucketStart = parseInt(dataArray[0], 10); - const bucketEnd = parseInt(dataArray[2], 10); - const actualInterval = bucketEnd - bucketStart; - expect(actualInterval).to.eql(1200000000); - }); - }); - }); - }); -} diff --git a/test/functional/apps/visualize/_linked_saved_searches.ts b/test/functional/apps/visualize/_linked_saved_searches.ts deleted file mode 100644 index 72c2a4322a2d..000000000000 --- a/test/functional/apps/visualize/_linked_saved_searches.ts +++ /dev/null @@ -1,138 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '@osd/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const browser = getService('browser'); - const filterBar = getService('filterBar'); - const retry = getService('retry'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects([ - 'common', - 'discover', - 'visualize', - 'header', - 'timePicker', - 'visChart', - ]); - - describe('saved search visualizations from visualize app', function describeIndexTests() { - describe('linked saved searched', () => { - const savedSearchName = 'vis_saved_search'; - let discoverSavedSearchUrlPath: string; - - before(async () => { - await PageObjects.common.navigateToApp('discover'); - await filterBar.addFilter('extension.raw', 'is', 'jpg'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.discover.saveSearch(savedSearchName); - discoverSavedSearchUrlPath = (await browser.getCurrentUrl()).split('?')[0]; - }); - - it('should create a visualization from a saved search', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickDataTable(); - await PageObjects.visualize.clickSavedSearch(savedSearchName); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await retry.waitFor('wait for count to equal 9,109', async () => { - const data = await PageObjects.visChart.getTableVisData(); - return data.trim() === '9,109'; - }); - }); - - it('should have a valid link to the saved search from the visualization', async () => { - await testSubjects.click('showUnlinkSavedSearchPopover'); - await testSubjects.click('viewSavedSearch'); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.waitFor('wait discover load its breadcrumbs', async () => { - const discoverBreadcrumb = await PageObjects.discover.getCurrentQueryName(); - return discoverBreadcrumb === savedSearchName; - }); - - const discoverURLPath = (await browser.getCurrentUrl()).split('?')[0]; - expect(discoverURLPath).to.equal(discoverSavedSearchUrlPath); - - // go back to visualize - await browser.goBack(); - await PageObjects.header.waitUntilLoadingHasFinished(); - }); - - it('should respect the time filter when linked to a saved search', async () => { - await PageObjects.timePicker.setAbsoluteRange( - 'Sep 19, 2015 @ 06:31:44.000', - 'Sep 21, 2015 @ 10:00:00.000' - ); - await retry.waitFor('wait for count to equal 3,950', async () => { - const data = await PageObjects.visChart.getTableVisData(); - return data.trim() === '3,950'; - }); - }); - - it('should allow adding filters while having a linked saved search', async () => { - await filterBar.addFilter('bytes', 'is between', '100', '3000'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.waitFor('wait for count to equal 707', async () => { - const data = await PageObjects.visChart.getTableVisData(); - return data.trim() === '707'; - }); - }); - - it('should allow unlinking from a linked search', async () => { - await PageObjects.visualize.clickUnlinkSavedSearch(); - await retry.waitFor('wait for count to equal 707', async () => { - const data = await PageObjects.visChart.getTableVisData(); - return data.trim() === '707'; - }); - // The filter on the saved search should now be in the editor - expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); - - // Disabling this filter should now result in different values, since - // the visualization should not be linked anymore with the saved search. - await filterBar.toggleFilterEnabled('extension.raw'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.waitFor('wait for count to equal 1,293', async () => { - const unfilteredData = await PageObjects.visChart.getTableVisData(); - return unfilteredData.trim() === '1,293'; - }); - }); - - it('should not break when saving after unlinking', async () => { - await PageObjects.visualize.saveVisualizationExpectSuccess('Unlinked before saved'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.waitFor('wait for count to equal 1,293', async () => { - const data = await PageObjects.visChart.getTableVisData(); - return data.trim() === '1,293'; - }); - }); - }); - }); -} diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index b6563a1c28ca..fb7e721db7de 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -57,12 +57,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { this.tags('ciGroup9'); loadTestFile(require.resolve('./_custom_branding')); - loadTestFile(require.resolve('./_embedding_chart')); loadTestFile(require.resolve('./_chart_types')); loadTestFile(require.resolve('./_area_chart')); - loadTestFile(require.resolve('./_data_table')); - loadTestFile(require.resolve('./_data_table_nontimeindex')); - loadTestFile(require.resolve('./_data_table_notimeindex_filters')); }); describe('', function () { @@ -73,7 +69,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_gauge_chart')); loadTestFile(require.resolve('./_heatmap_chart')); loadTestFile(require.resolve('./input_control_vis')); - loadTestFile(require.resolve('./_histogram_request_start')); loadTestFile(require.resolve('./_metric_chart')); }); @@ -86,7 +81,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_markdown_vis')); loadTestFile(require.resolve('./_shared_item')); loadTestFile(require.resolve('./_lab_mode')); - loadTestFile(require.resolve('./_linked_saved_searches')); loadTestFile(require.resolve('./_visualize_listing')); if (isOss) { loadTestFile(require.resolve('./_tile_map')); diff --git a/test/functional/fixtures/opensearch_archiver/dashboard/current/opensearch_dashboards/data.json.gz b/test/functional/fixtures/opensearch_archiver/dashboard/current/opensearch_dashboards/data.json.gz index 9944453bff1a01420b3f46c35af304c4fc5122ee..1ca01589588ce98027d39d1650ce7ae1cfc11e43 100644 GIT binary patch literal 20707 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 a1HA+c%{cPyE5niQ_5TBtQrudj5d#2C5MPe~ delta 21371 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 M3qUV`;*%f)00w`{3;+NC diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 40e177790695..58f75555b157 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -425,7 +425,6 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide { name: PIE_CHART_VIS_NAME, description: 'PieChart' }, { name: 'Visualization☺ VerticalBarChart', description: 'VerticalBarChart' }, { name: AREA_CHART_VIS_NAME, description: 'AreaChart' }, - { name: 'Visualization☺漢字 DataTable', description: 'DataTable' }, { name: LINE_CHART_VIS_NAME, description: 'LineChart' }, { name: 'Visualization TileMap', description: 'TileMap' }, { name: 'Visualization MetricChart', description: 'MetricChart' }, diff --git a/test/functional/page_objects/visualize_chart_page.ts b/test/functional/page_objects/visualize_chart_page.ts index 02d9e60ca06d..e13d8eed6081 100644 --- a/test/functional/page_objects/visualize_chart_page.ts +++ b/test/functional/page_objects/visualize_chart_page.ts @@ -36,7 +36,6 @@ export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrPr const find = getService('find'); const log = getService('log'); const retry = getService('retry'); - const table = getService('table'); const defaultFindTimeout = config.get('timeouts.find'); const { common } = getPageObjects(['common']); @@ -294,18 +293,6 @@ export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrPr }); } - public async filterOnTableCell(column: string, row: string) { - await retry.try(async () => { - const tableVis = await testSubjects.find('tableVis'); - const cell = await tableVis.findByCssSelector( - `tbody tr:nth-child(${row}) td:nth-child(${column})` - ); - await cell.moveMouseTo(); - const filterBtn = await testSubjects.findDescendant('filterForCellValue', cell); - await filterBtn.click(); - }); - } - public async getMarkdownText() { const markdownContainer = await testSubjects.find('markdownBody'); return markdownContainer.getVisibleText(); @@ -317,65 +304,6 @@ export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrPr return element.getVisibleText(); } - public async getFieldLinkInVisTable(fieldName: string, rowIndex: number = 1) { - const tableVis = await testSubjects.find('tableVis'); - const $ = await tableVis.parseDomContent(); - const headers = $('span[ng-bind="::col.title"]') - .toArray() - .map((header: any) => $(header).text()); - const fieldColumnIndex = headers.indexOf(fieldName); - return await find.byCssSelector( - `[data-test-subj="paginated-table-body"] tr:nth-of-type(${rowIndex}) td:nth-of-type(${ - fieldColumnIndex + 1 - }) a` - ); - } - - /** - * If you are writing new tests, you should rather look into getTableVisContent method instead. - * @deprecated Use getTableVisContent instead. - */ - public async getTableVisData() { - return await testSubjects.getVisibleText('paginated-table-body'); - } - - /** - * This function is the newer function to retrieve data from within a table visualization. - * It uses a better return format, than the old getTableVisData, by properly splitting - * cell values into arrays. Please use this function for newer tests. - */ - public async getTableVisContent({ stripEmptyRows = true } = {}) { - return await retry.try(async () => { - const container = await testSubjects.find('tableVis'); - const allTables = await testSubjects.findAllDescendant('paginated-table-body', container); - - if (allTables.length === 0) { - return []; - } - - const allData = await Promise.all( - allTables.map(async (t) => { - let data = await table.getDataFromElement(t); - if (stripEmptyRows) { - data = data.filter( - (row) => row.length > 0 && row.some((cell) => cell.trim().length > 0) - ); - } - return data; - }) - ); - - if (allTables.length === 1) { - // If there was only one table we return only the data for that table - // This prevents an unnecessary array around that single table, which - // is the case we have in most tests. - return allData[0]; - } - - return allData; - }); - } - public async getMetric() { const elements = await find.allByCssSelector( '[data-test-subj="visualizationLoader"] .mtrVis__container' diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index 4f019c1a4a04..b60d50b449c8 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -99,10 +99,6 @@ export function VisualizePageProvider({ getService, getPageObjects }: FtrProvide await this.clickVisType('area'); } - public async clickDataTable() { - await this.clickVisType('table'); - } - public async clickLineChart() { await this.clickVisType('line'); } diff --git a/test/functional/services/dashboard/expectations.ts b/test/functional/services/dashboard/expectations.ts index a676730f4c1b..ab6eaed8a9bc 100644 --- a/test/functional/services/dashboard/expectations.ts +++ b/test/functional/services/dashboard/expectations.ts @@ -241,17 +241,6 @@ export function DashboardExpectProvider({ getService, getPageObjects }: FtrProvi }); } - async dataTableRowCount(expectedCount: number) { - log.debug(`DashboardExpect.dataTableRowCount(${expectedCount})`); - await retry.try(async () => { - const dataTableRows = await find.allByCssSelector( - '[data-test-subj="paginated-table-body"] [data-cell-content]', - findTimeout - ); - expect(dataTableRows.length).to.be(expectedCount); - }); - } - async seriesElementCount(expectedCount: number) { log.debug(`DashboardExpect.seriesElementCount(${expectedCount})`); await retry.try(async () => { diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index 391967de7bce..c09d63995163 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -57,7 +57,6 @@ import { ManagementMenuProvider } from './management'; import { QueryBarProvider } from './query_bar'; import { RemoteProvider } from './remote'; import { RenderableProvider } from './renderable'; -import { TableProvider } from './table'; import { ToastsProvider } from './toasts'; import { DataGridProvider } from './data_grid'; import { @@ -93,7 +92,6 @@ export const services = { dataGrid: DataGridProvider, embedding: EmbeddingProvider, renderable: RenderableProvider, - table: TableProvider, browser: BrowserProvider, pieChart: PieChartProvider, inspector: InspectorProvider, diff --git a/test/functional/services/table.ts b/test/functional/services/table.ts deleted file mode 100644 index 34578df40e80..000000000000 --- a/test/functional/services/table.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { FtrProviderContext } from '../ftr_provider_context'; -import { WebElementWrapper } from './lib/web_element_wrapper'; - -export function TableProvider({ getService }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - - class Table { - /** - * Finds table and returns data in the nested array format - * [ [cell1_in_row1, cell2_in_row1], [cell1_in_row2, cell2_in_row2] ] - * @param dataTestSubj data-test-subj selector - */ - - public async getDataFromTestSubj(dataTestSubj: string): Promise<string[][]> { - const table = await testSubjects.find(dataTestSubj); - return await this.getDataFromElement(table); - } - - /** - * Converts the table data into nested array - * [ [cell1_in_row1, cell2_in_row1], [cell1_in_row2, cell2_in_row2] ] - * @param element table - */ - public async getDataFromElement(element: WebElementWrapper): Promise<string[][]> { - const $ = await element.parseDomContent(); - return $('tr') - .toArray() - .map((row) => - $(row) - .find('td') - .toArray() - .map((cell) => - $(cell) - .text() - .replace(/ /g, '') - .trim() - ) - ); - } - } - - return new Table(); -} diff --git a/yarn.lock b/yarn.lock index 822684d097bd..d4c3e3187449 100644 --- a/yarn.lock +++ b/yarn.lock @@ -345,6 +345,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== +"@babel/helper-plugin-utils@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + "@babel/helper-remap-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" @@ -495,6 +500,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-json-strings" "^7.8.3" +"@babel/plugin-proposal-logical-assignment-operators@^7.16.5": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" @@ -1309,18 +1322,6 @@ resolved "https://registry.yarnpkg.com/@elastic/filesaver/-/filesaver-1.1.2.tgz#1998ffb3cd89c9da4ec12a7793bfcae10e30c77a" integrity sha512-YZbSufYFBhAj+S2cJgiKALoxIJevqXN2MSr6Yqr42rJdaPuM31cj6pUDwflkql1oDjupqD9la+MfxPFjXI1JFQ== -"@elastic/github-checks-reporter@0.0.20b3": - version "0.0.20-b3" - resolved "https://registry.yarnpkg.com/@elastic/github-checks-reporter/-/github-checks-reporter-0.0.20-b3.tgz#025ac0e152cda03d947faec190c244fbbe59bdfc" - integrity sha512-OmhbddqNkFZMYVQxMqpqLj7NJhqphN+wQb68IeiewxvWXq8NEPaBpaZ9f+nUbixmMY2Q/XA0JgtuE4EhMGIljg== - dependencies: - "@octokit/app" "^2.2.2" - "@octokit/plugin-retry" "^2.2.0" - "@octokit/request" "^2.4.2" - "@octokit/rest" "^16.23.2" - async-retry "^1.2.3" - strip-ansi "^5.2.0" - "@elastic/good@^9.0.1-kibana3": version "9.0.1-kibana3" resolved "https://registry.yarnpkg.com/@elastic/good/-/good-9.0.1-kibana3.tgz#a70c2b30cbb4f44d1cf4a464562e0680322eac9b" @@ -2337,173 +2338,6 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@octokit/app@^2.2.2": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@octokit/app/-/app-2.2.5.tgz#2cdd1eed763a822ed5cd0ffcf38fbdd0d40acbf9" - integrity sha512-WIvIVzZItDWSvnkleA6e5wmNBqH4dfzFZsB5GV0QWiMNAOT7TjecVcB6Uz6GhQvfuV4rmjY3/al3akWNNuPLmg== - dependencies: - "@octokit/request" "^3.0.0" - "@types/lru-cache" "^5.1.0" - jsonwebtoken "^8.3.0" - lru-cache "^5.1.1" - -"@octokit/auth-token@^2.4.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== - dependencies: - "@octokit/types" "^6.0.3" - -"@octokit/endpoint@^3.2.0": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-3.2.3.tgz#bd9aea60cd94ce336656b57a5c9cb7f10be8f4f3" - integrity sha512-yUPCt4vMIOclox13CUxzuKiPJIFo46b/6GhUnUTw5QySczN1L0DtSxgmIZrZV4SAb9EyAqrceoyrWoYVnfF2AA== - dependencies: - deepmerge "3.2.0" - is-plain-object "^2.0.4" - universal-user-agent "^2.0.1" - url-template "^2.0.8" - -"@octokit/endpoint@^5.1.0": - version "5.5.3" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.5.3.tgz#0397d1baaca687a4c8454ba424a627699d97c978" - integrity sha512-EzKwkwcxeegYYah5ukEeAI/gYRLv2Y9U5PpIsseGSFDk+G3RbipQGBs8GuYS1TLCtQaqoO66+aQGtITPalxsNQ== - dependencies: - "@octokit/types" "^2.0.0" - is-plain-object "^3.0.0" - universal-user-agent "^5.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== - -"@octokit/plugin-paginate-rest@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz#004170acf8c2be535aba26727867d692f7b488fc" - integrity sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q== - dependencies: - "@octokit/types" "^2.0.1" - -"@octokit/plugin-request-log@^1.0.0": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz#3288ecf5481f68c494dd0602fc15407a59faf61e" - integrity sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ== - dependencies: - "@octokit/types" "^2.0.1" - deprecation "^2.3.1" - -"@octokit/plugin-retry@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-2.2.0.tgz#11f3957a46ccdb7b7f33caabf8c17e57b25b80b2" - integrity sha512-x5Kd8Lke+a4hTDCe5akZxpGmVwu1eeVt2FJX0jeo3CxHGbfHbXb4zhN5quKfGL9oBLV/EdHQIJ6zwIMjuzxOlw== - dependencies: - bottleneck "^2.15.3" - -"@octokit/request-error@^1.0.2": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801" - integrity sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA== - dependencies: - "@octokit/types" "^2.0.0" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^2.4.2": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-2.4.2.tgz#87c36e820dd1e43b1629f4f35c95b00cd456320b" - integrity sha512-lxVlYYvwGbKSHXfbPk5vxEA8w4zHOH1wobado4a9EfsyD3Cbhuhus1w0Ye9Ro0eMubGO8kNy5d+xNFisM3Tvaw== - dependencies: - "@octokit/endpoint" "^3.2.0" - deprecation "^1.0.1" - is-plain-object "^2.0.4" - node-fetch "^2.3.0" - once "^1.4.0" - universal-user-agent "^2.0.1" - -"@octokit/request@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-3.0.3.tgz#ace63b5ea196cc00ad27f3fbe5c13a9698681ec8" - integrity sha512-M7pUfsiaiiUMEP4/SMysTeWxyGrkoQg6FBPEtCBIFgeDnzHaPboTpUZGTh6u1GQXdrlzMfPVn/vQs98js1QtwQ== - dependencies: - "@octokit/endpoint" "^5.1.0" - deprecation "^1.0.1" - is-plain-object "^3.0.0" - node-fetch "^2.3.0" - once "^1.4.0" - universal-user-agent "^2.0.1" - -"@octokit/request@^5.2.0": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" - integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.7" - universal-user-agent "^6.0.0" - -"@octokit/rest@^16.23.2": - version "16.43.2" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.43.2.tgz#c53426f1e1d1044dee967023e3279c50993dd91b" - integrity sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ== - dependencies: - "@octokit/auth-token" "^2.4.0" - "@octokit/plugin-paginate-rest" "^1.1.1" - "@octokit/plugin-request-log" "^1.0.0" - "@octokit/plugin-rest-endpoint-methods" "2.4.0" - "@octokit/request" "^5.2.0" - "@octokit/request-error" "^1.0.2" - atob-lite "^2.0.0" - before-after-hook "^2.0.0" - btoa-lite "^1.0.0" - deprecation "^2.0.0" - lodash.get "^4.4.2" - lodash.set "^4.3.2" - lodash.uniq "^4.5.0" - octokit-pagination-methods "^1.1.0" - once "^1.4.0" - universal-user-agent "^4.0.0" - -"@octokit/types@^2.0.0", "@octokit/types@^2.0.1": - version "2.16.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.16.2.tgz#4c5f8da3c6fecf3da1811aef678fda03edac35d2" - integrity sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q== - dependencies: - "@types/node" ">= 8" - -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1": - version "6.34.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== - dependencies: - "@octokit/openapi-types" "^11.2.0" - "@opensearch-project/opensearch@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-1.1.0.tgz#8b3c8b4cbcea01755ba092d2997bf0b4ca7f22f7" @@ -2763,10 +2597,10 @@ dependencies: defer-to-connect "^2.0.0" -"@testim/chrome-version@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.2.tgz#092005c5b77bd3bb6576a4677110a11485e11864" - integrity sha512-1c4ZOETSRpI0iBfIFUqU4KqwBAB2lHUAlBjZz/YqOHqwM9dTTzjV6Km0ZkiEiSCx/tLr1BtESIKyWWMww+RUqw== +"@testim/chrome-version@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.3.tgz#fbb68696899d7b8c1b9b891eded9c04fe2cd5529" + integrity sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A== "@testing-library/dom@^8.0.0", "@testing-library/dom@^8.11.3": version "8.12.0" @@ -2921,14 +2755,7 @@ resolved "https://registry.yarnpkg.com/@types/chroma-js/-/chroma-js-2.1.3.tgz#0b03d737ff28fad10eb884e0c6cedd5ffdc4ba0a" integrity sha512-1xGPhoSGY1CPmXLCBcjVZSQinFjL26vlR8ZqprsBWiFyED4JacJJ9zHhh5aaUXqbY9B37mKQ73nlydVAXmr1+g== -"@types/chromedriver@^81.0.0": - version "81.0.1" - resolved "https://registry.yarnpkg.com/@types/chromedriver/-/chromedriver-81.0.1.tgz#bff3e4cdc7830dc0f115a9c0404f6979771064d4" - integrity sha512-I7ma6bBzfWc5YiMV/OZ6lYMZIANAwGbDH+QRYKnbXRptdAvUhSoFP5iHzQHas6QZCRDtefMvbxCjySUyXhxafQ== - dependencies: - "@types/node" "*" - -"@types/clone@~2.1.0": +"@types/clone@~2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@types/clone/-/clone-2.1.1.tgz#9b880d0ce9b1f209b5e0bd6d9caa38209db34024" integrity sha512-BZIU34bSYye0j/BFcPraiDZ5ka6MJADjcDVELGf7glr9K+iE8NYVjFslJFVWzskSxkLLyCrSPScE82/UUoBSvg== @@ -3004,6 +2831,13 @@ resolved "https://registry.yarnpkg.com/@types/delete-empty/-/delete-empty-2.0.0.tgz#1647ae9e68f708a6ba778531af667ec55bc61964" integrity sha512-sq+kwx8zA9BSugT9N+Jr8/uWjbHMZ+N/meJEzRyT3gmLq/WMtx/iSIpvdpmBUi/cvXl6Kzpvve8G2ESkabFwmg== +"@types/dompurify@^2.3.3": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.4.0.tgz#fd9706392a88e0e0e6d367f3588482d817df0ab9" + integrity sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg== + dependencies: + "@types/trusted-types" "*" + "@types/duplexify@^3.6.0": version "3.6.1" resolved "https://registry.yarnpkg.com/@types/duplexify/-/duplexify-3.6.1.tgz#5685721cf7dc4a21b6f0e8a8efbec6b4d2fbafad" @@ -3070,13 +2904,6 @@ resolved "https://registry.yarnpkg.com/@types/extract-zip/-/extract-zip-1.6.2.tgz#5c7eb441c41136167a42b88b64051e6260c29e86" integrity sha1-XH60QcQRNhZ6QriLZAUeYmDCnoY= -"@types/fast-json-stable-stringify@^2.0.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#c4d9c003d546b7ca9496ea924e9e9faca72873b4" - integrity sha512-IyNhGHu71jH1jCXTHmafuoAAdsbBON3kDh7u/UUhLmjYgN5TYB54e1R8ckTCiIevl2UuZaCsi9XRxineY5yUjw== - dependencies: - fast-json-stable-stringify "*" - "@types/fetch-mock@^7.3.1": version "7.3.5" resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-7.3.5.tgz#7aee678c4e7c7e1a168bae8fdab5b8d712e377f6" @@ -3448,7 +3275,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@12.20.24", "@types/node@16.9.1", "@types/node@>= 8", "@types/node@^14.17.32": +"@types/node@*", "@types/node@12.20.24", "@types/node@16.9.1", "@types/node@^14.17.32": version "14.18.12" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.12.tgz#0d4557fd3b94497d793efd4e7d92df2f83b4ef24" integrity sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A== @@ -3735,9 +3562,9 @@ csstype "^3.0.2" "@types/superagent@*": - version "4.1.15" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.15.tgz#63297de457eba5e2bc502a7609426c4cceab434a" - integrity sha512-mu/N4uvfDN2zVQQ5AYJI/g4qxn2bHB6521t1UuH09ShNWjebTqN0ZFuYK9uYjcgmI0dTQEs+Owi1EO6U0OkOZQ== + version "4.1.16" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.16.tgz#12c9c16f232f9d89beab91d69368f96ce8e2d881" + integrity sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ== dependencies: "@types/cookiejar" "*" "@types/node" "*" @@ -3751,7 +3578,7 @@ "@types/superagent" "*" "@types/supertest" "*" -"@types/supertest@*", "@types/supertest@^2.0.11": +"@types/supertest@*", "@types/supertest@^2.0.12": version "2.0.12" resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== @@ -3800,6 +3627,11 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40" integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg== +"@types/trusted-types@*": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" + integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== + "@types/type-detect@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/type-detect/-/type-detect-4.0.1.tgz#3b0f5ac82ea630090cbf57c57a1bf5a63a29b9b6" @@ -4278,9 +4110,9 @@ acorn@^7.0.0, acorn@^7.1.1: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4: - version "8.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" - integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== adm-zip@0.5.9: version "0.5.9" @@ -4299,7 +4131,7 @@ agent-base@4: dependencies: es6-promisify "^5.0.0" -agent-base@6: +agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -4313,7 +4145,7 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -agentkeepalive@^4.2.1: +agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== @@ -4355,7 +4187,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@~6.12.6: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.5, ajv@~6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -4527,7 +4359,12 @@ append-transform@^2.0.0: dependencies: default-require-extensions "^3.0.0" -aproba@^1.0.3, aproba@^1.1.1: +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== @@ -4566,13 +4403,13 @@ archy@^1.0.0: resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= -are-we-there-yet@~1.1.2: - version "1.1.7" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" - integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== +are-we-there-yet@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" + integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== dependencies: delegates "^1.0.0" - readable-stream "^2.0.6" + readable-stream "^3.6.0" argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" @@ -4636,11 +4473,6 @@ array-find@^1.0.0: resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" integrity sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg= -array-flat-polyfill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz#1e3a4255be619dfbffbfd1d635c1cf357cd034e7" - integrity sha512-hfJmKupmQN0lwi0xG6FQ5U8Rd97RnIERplymOv/qpq8AoNKPPAnxJadjFA23FNWm88wykh9HmpLJUUwUtNU/iw== - array-from@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" @@ -4757,18 +4589,6 @@ asn1.js@^5.2.0, asn1.js@^5.3.0: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" @@ -4814,13 +4634,6 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-retry@^1.2.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" - integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== - dependencies: - retry "0.13.1" - async-value-promise@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/async-value-promise/-/async-value-promise-1.1.1.tgz#68957819e3eace804f3b4b69477e2bd276c15378" @@ -4843,11 +4656,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -atob-lite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696" - integrity sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY= - atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -4901,22 +4709,12 @@ aws-sdk@^2.650.0: uuid "8.0.0" xml2js "0.4.19" -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - axe-core@^4.0.2, axe-core@^4.3.5: version "4.4.1" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== -axios@^0.24.0, axios@^0.27.2: +axios@^0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== @@ -4924,6 +4722,15 @@ axios@^0.24.0, axios@^0.27.2: follow-redirects "^1.14.9" form-data "^4.0.0" +axios@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383" + integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" @@ -5136,18 +4943,6 @@ batch-processor@1.0.0: resolved "https://registry.yarnpkg.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" integrity sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg= -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -before-after-hook@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -5214,11 +5009,6 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -bottleneck@^2.15.3: - version "2.19.5" - resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" - integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -5227,6 +5017,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + brace@0.11.1, brace@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" @@ -5356,21 +5153,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -btoa-lite@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" - integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= - buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= - buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" @@ -5466,7 +5253,7 @@ cacache@^13.0.1: ssri "^7.0.0" unique-filename "^1.1.1" -cacache@^15.0.5: +cacache@^15.0.5, cacache@^15.2.0: version "15.3.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== @@ -5580,11 +5367,6 @@ caniuse-lite@^1.0.30001317: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz" integrity sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA== -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - ccount@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" @@ -5781,16 +5563,16 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -chromedriver@^100.0.0: - version "100.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-100.0.0.tgz#1b4bf5c89cea12c79f53bc94d8f5bb5aa79ed7be" - integrity sha512-oLfB0IgFEGY9qYpFQO/BNSXbPw7bgfJUN5VX8Okps9W2qNT4IqKh5hDwKWtpUIQNI6K3ToWe2/J5NdpurTY02g== +chromedriver@^107.0.3: + version "107.0.3" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-107.0.3.tgz#330c0808bb14a53f13ab7e2b0c78adf3cdb4c14b" + integrity sha512-jmzpZgctCRnhYAn0l/NIjP4vYN3L8GFVbterTrRr2Ly3W5rFMb9H8EKGuM5JCViPKSit8FbE718kZTEt3Yvffg== dependencies: - "@testim/chrome-version" "^1.1.2" - axios "^0.24.0" - del "^6.0.0" + "@testim/chrome-version" "^1.1.3" + axios "^1.1.3" + compare-versions "^5.0.1" extract-zip "^2.0.1" - https-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" proxy-from-env "^1.1.0" tcp-port-used "^1.0.1" @@ -5881,7 +5663,7 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -cliui@^7.0.0, cliui@^7.0.2: +cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== @@ -5890,6 +5672,15 @@ cliui@^7.0.0, cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -6020,6 +5811,11 @@ color-string@^1.4.0: color-name "^1.0.0" simple-swizzle "^0.2.2" +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + color@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/color/-/color-1.0.3.tgz#e48e832d85f14ef694fb468811c2d5cfe729b55d" @@ -6048,7 +5844,7 @@ colors@~1.2.1: resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -6095,6 +5891,11 @@ compare-versions@3.5.1: resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.5.1.tgz#26e1f5cf0d48a77eced5046b9f67b6b61075a393" integrity sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg== +compare-versions@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.1.tgz#14c6008436d994c3787aba38d4087fabe858555e" + integrity sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ== + component-emitter@^1.2.1, component-emitter@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -6141,10 +5942,10 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -console-control-strings@^1.0.0, console-control-strings@~1.1.0: +console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== console-log-level@^1.4.1: version "1.4.1" @@ -6180,10 +5981,10 @@ cookie@^0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -cookiejar@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" - integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== +cookiejar@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== copy-anything@^2.0.1: version "2.0.6" @@ -6256,11 +6057,6 @@ core-js@^3.6.5: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - core-util-is@^1.0.2, core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -6356,7 +6152,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -6781,13 +6577,6 @@ damerau-levenshtein@^1.0.7: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - dashify@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dashify/-/dashify-0.1.0.tgz#107daf9cca5e326e30a8b39ffa5048b6684922ea" @@ -6885,9 +6674,9 @@ decimal.js@^10.2.1: integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== decompress-response@^6.0.0: version "6.0.0" @@ -6939,11 +6728,6 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e" - integrity sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow== - deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" @@ -7045,20 +6829,6 @@ del@^5.1.0: rimraf "^3.0.0" slash "^3.0.0" -del@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" - integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== - dependencies: - globby "^11.0.1" - graceful-fs "^4.2.4" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.2" - p-map "^4.0.0" - rimraf "^3.0.2" - slash "^3.0.0" - del@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" @@ -7119,16 +6889,6 @@ dependency-check@^4.1.0: read-package-json "^2.0.10" resolve "^1.1.7" -deprecation@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-1.0.1.tgz#2df79b79005752180816b7b6e079cbd80490d711" - integrity sha512-ccVHpE72+tcIKaGMql33x5MAjKQIZrk+3x2GbJ7TeraUCZWHoT+KSZpoC+JQFsUBlSTXUrBaGiF0j6zVTepPLg== - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -7176,7 +6936,7 @@ detective@^5.0.2: defined "^1.0.0" minimist "^1.1.1" -dezalgo@1.0.3, dezalgo@^1.0.0: +dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= @@ -7184,6 +6944,14 @@ dezalgo@1.0.3, dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" +dezalgo@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + diff-match-patch@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" @@ -7338,6 +7106,11 @@ domhandler@^4.0, domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.2.2, domhan dependencies: domelementtype "^2.2.0" +dompurify@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.1.tgz#f9cb1a275fde9af6f2d0a2644ef648dd6847b631" + integrity sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA== + domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -7402,21 +7175,6 @@ eachr@^4.5.0: dependencies: typechecker "^6.2.0" -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - ejs@^3.1.7: version "3.1.7" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006" @@ -7548,6 +7306,13 @@ emoticon@^3.2.0: resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -7676,6 +7441,11 @@ enzyme@^3.11.0: rst-selector-parser "^2.2.3" string.prototype.trim "^1.2.1" +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + errno@^0.1.1, errno@^0.1.3, errno@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -7845,7 +7615,7 @@ es6-weak-map@^2.0.3: es6-iterator "^2.0.3" es6-symbol "^3.1.1" -escalade@^3.0.2, escalade@^3.1.1: +escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== @@ -8278,19 +8048,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" @@ -8400,7 +8157,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: +extend@^3.0.0, extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -8447,16 +8204,6 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - fast-deep-equal@^3.1.1, fast-deep-equal@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -8490,7 +8237,7 @@ fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.4, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@*, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@~2.1.0: +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -8820,10 +8567,10 @@ focus-lock@^0.10.2: dependencies: tslib "^2.0.3" -follow-redirects@^1.14.9: - version "1.15.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.0.tgz#06441868281c86d0dda4ad8bdaead2d02dca89d4" - integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ== +follow-redirects@^1.14.9, follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== font-awesome@4.7.0: version "4.7.0" @@ -8862,11 +8609,6 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -8885,24 +8627,15 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -formidable@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.0.1.tgz#4310bc7965d185536f9565184dee74fbb75557ff" - integrity sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ== +formidable@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" + integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== dependencies: - dezalgo "1.0.3" - hexoid "1.0.0" - once "1.4.0" - qs "6.9.3" + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + qs "^6.11.0" forwarded-parse@^2.1.0: version "2.1.2" @@ -9031,19 +8764,19 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21" integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA== -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" geckodriver@^3.0.2: version "3.2.0" @@ -9104,13 +8837,6 @@ get-stdin@^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: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - get-stream@^5.0.0, get-stream@^5.1.0, get-stream@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -9160,13 +8886,6 @@ getos@^3.2.1: dependencies: async "^3.2.0" -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - gifwrap@^0.9.2: version "0.9.4" resolved "https://registry.yarnpkg.com/gifwrap/-/gifwrap-0.9.4.tgz#f4eb6169ba027d61df64aafbdcb1f8ae58ccc0c5" @@ -9393,7 +9112,7 @@ got@11.8.5, got@^11.8.2: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -9559,19 +9278,6 @@ handlebars@4.7.7: optionalDependencies: uglify-js "^3.1.4" -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -9652,10 +9358,10 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.0: +has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== has-value@^1.0.0: version "1.0.0" @@ -9821,7 +9527,7 @@ he@1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hexoid@1.0.0: +hexoid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== @@ -9970,7 +9676,7 @@ htmlparser2@^7.0: domutils "^2.8.0" entities "^3.0.1" -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -9999,15 +9705,6 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" @@ -10021,7 +9718,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0: +https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -10058,7 +9755,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6: +iconv-lite@0.6, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -10246,10 +9943,10 @@ inquirer@^7.0.0, inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -install-artifact-from-github@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.0.tgz#cab6ff821976b8a35b0c079da19a727c90381a40" - integrity sha512-iT8v1GwOAX0pPXifF/5ihnMhHOCo3OeK7z3TQa4CtSNCIg8k0UxqBEk9jRwz8OP68hHXvJ2gxRa89KYHtBkqGA== +install-artifact-from-github@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.1.tgz#eefaad9af35d632e5d912ad1569c1de38c3c2462" + integrity sha512-3l3Bymg2eKDsN5wQuMfgGEj2x6l5MCAv0zPL6rxHESufFVlEAKW/6oY9F1aGgvY/EgWm5+eWGRjINveL4X7Hgg== internal-slot@^1.0.3: version "1.0.3" @@ -10342,6 +10039,11 @@ ip-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + irregular-plurals@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.3.0.tgz#67d0715d4361a60d9fd9ee80af3881c631a31ee2" @@ -10582,6 +10284,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + is-map@^2.0.1, is-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" @@ -10677,11 +10384,6 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-plain-object@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" - integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== - is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -10785,7 +10487,7 @@ is-typed-array@^1.1.7: foreach "^2.0.5" has-tostringtag "^1.0.0" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -10915,11 +10617,6 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -11062,10 +10759,10 @@ jest-cli@^27.5.1: prompts "^2.0.1" yargs "^16.2.0" -jest-config@^27.5.1: +jest-config@^27.5.1, "jest-config@npm:@amoo-miki/jest-config@27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== + resolved "https://registry.yarnpkg.com/@amoo-miki/jest-config/-/jest-config-27.5.1.tgz#3afdb485fdccad8fd8b19be505747cafbf8e6707" + integrity sha512-6pSD/Lo5axflM2NO0eIHuJ40nxfXn2NQGqzOCrZO1EE7yS3k90p10RalInf8RXl2g9/f1Ax4U4aU2e3BWSaSaQ== dependencies: "@babel/core" "^7.8.0" "@jest/test-sequencer" "^27.5.1" @@ -11170,10 +10867,10 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: +jest-jasmine2@^27.5.1, "jest-jasmine2@npm:@amoo-miki/jest-jasmine2@27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + resolved "https://registry.yarnpkg.com/@amoo-miki/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#0c99502f9879e1504b124da8355880924de1f310" + integrity sha512-oOihnJGLdFZO780Ts6XR+Sbs8d4J0+sqcNvBNcJch/LSKpQt7rARi0V+TgF80LRAvJI4gyq97az/2fdXXgxDNg== dependencies: "@jest/environment" "^27.5.1" "@jest/source-map" "^27.5.1" @@ -11529,11 +11226,6 @@ jsbn@1.1.0, jsbn@^1.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" integrity sha1-sBMHyym2GKHtJux56RH4A8TaAEA= -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - jsdom@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" @@ -11602,7 +11294,7 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.4.0, json-schema@^0.4.0: +json-schema@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== @@ -11624,27 +11316,27 @@ json-stringify-pretty-compact@1.2.0: resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-1.2.0.tgz#0bc316b5e6831c07041fc35612487fb4e9ab98b8" integrity sha512-/11Pj1OyX814QMKO7K8l85SHPTr/KsFxHp8GE2zVa0BtJgGimDjXHfM3FhC7keQdWDea7+nXf+f1de7ATZcZkQ== -json-stringify-pretty-compact@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz#e77c419f52ff00c45a31f07f4c820c2433143885" - integrity sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ== +json-stringify-pretty-compact@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz#f71ef9d82ef16483a407869556588e91b681d9ab" + integrity sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA== -json-stringify-safe@5.0.1, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@5.0.1, json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" json5@^2.1.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^4.0.0: version "4.0.0" @@ -11663,32 +11355,6 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= -jsonwebtoken@^8.3.0: - version "8.5.1" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== - dependencies: - jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - semver "^5.6.0" - -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" @@ -11717,23 +11383,6 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - keyv@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.1.1.tgz#02c538bfdbd2a9308cc932d4096f05ae42bfa06a" @@ -11797,10 +11446,10 @@ leaflet-responsive-popup@0.6.4: resolved "https://registry.yarnpkg.com/leaflet-responsive-popup/-/leaflet-responsive-popup-0.6.4.tgz#b93d9368ef9f96d6dc911cf5b96d90e08601c6b3" integrity sha512-2D8G9aQA6NHkulDBPN9kqbUCkCpWQQ6dF0xFL11AuEIWIbsL4UC/ZPP5m8GYM0dpU6YTlmyyCh1Tz+cls5Q4dg== -"leaflet-vega@npm:@amoo-miki/leaflet-vega@0.8.8": - version "0.8.8" - resolved "https://registry.yarnpkg.com/@amoo-miki/leaflet-vega/-/leaflet-vega-0.8.8.tgz#675abf37d72fbea859755e982f4fd19dea776557" - integrity sha512-W2gGgFDxzy/XUx+fQJfz0NYVXsKl7V+G6QywiMcOV5NEodDId9c60up7NNf+cfM7ggpo+5BuLqrKmosuGO1CsA== +leaflet-vega@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/leaflet-vega/-/leaflet-vega-0.9.0.tgz#ea3d87221bae452dbeea7f435f4d6165d3c71eb7" + integrity sha512-mECYEAf4/k9JPWyWv0/OhA0vTjkRilsQas2PdDjALR1q9/SHkA056SDFNidIgQzg4/d/5NiQLpwBC7Hk4k43wA== dependencies: vega-spec-injector "^0.0.2" @@ -12000,10 +11649,10 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@^1.2.3, loader-utils@^2.0.0, loader-utils@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.3.tgz#d4b15b8504c63d1fc3f2ade52d41bc8459d6ede1" - integrity sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A== +loader-utils@^1.2.3, loader-utils@^2.0.0, loader-utils@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" @@ -12109,41 +11758,16 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= - lodash.isequal@^4.0.0, lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= - lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= - lodash.map@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" @@ -12159,11 +11783,6 @@ lodash.merge@4.6.2, lodash.merge@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - lodash.padstart@4.6.1: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" @@ -12189,11 +11808,6 @@ lodash.repeat@4.1.0: resolved "https://registry.yarnpkg.com/lodash.repeat/-/lodash.repeat-4.1.0.tgz#fc7de8131d8c8ac07e4b49f74ffe829d1f2bec44" integrity sha1-/H3oEx2MisB+S0n3T/6CnR8r7EQ= -lodash.set@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= - lodash.some@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" @@ -12214,11 +11828,6 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - lodash@4.17.21, lodash@^4.0.1, lodash@^4.10.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.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.15, lodash@~4.17.19, lodash@~4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -12283,9 +11892,9 @@ lolex@^5.0.1: "@sinonjs/commons" "^1.7.0" long@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" - integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w== + version "5.2.1" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" + integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" @@ -12333,11 +11942,6 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= -macos-release@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" - integrity sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g== - make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -12353,6 +11957,28 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" +make-fetch-happen@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^6.0.0" + ssri "^8.0.0" + make-iterator@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" @@ -12610,23 +12236,23 @@ mime-db@1.52.0, mime-db@1.x.x: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.34, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@^2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime@2.6.0, mime@^2.4.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.4, mime@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -12686,13 +12312,27 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@5.0.1, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2, minimatch@~3.0.4: +"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@~3.0.4: + version "3.0.8" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" + integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== + dependencies: + brace-expansion "^1.1.7" + 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" @@ -12714,6 +12354,17 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" +minipass-fetch@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -12721,13 +12372,20 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" -minipass-pipeline@^1.2.2: +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + minipass@^3.0.0, minipass@^3.1.1: version "3.1.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" @@ -12735,7 +12393,21 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minizlib@^2.1.1: +minipass@^3.1.0, minipass@^3.1.3: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" + integrity sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw== + dependencies: + yallist "^4.0.0" + +minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -12948,11 +12620,16 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1, nan@^2.14.1, nan@^2.14.2: +nan@^2.12.1, nan@^2.14.2: version "2.15.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== +nan@^2.15.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + nano-css@^5.2.1: version "5.3.4" resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.4.tgz#40af6a83a76f84204f346e8ccaa9169cdae9167b" @@ -13023,6 +12700,11 @@ needle@^2.5.2: iconv-lite "^0.4.4" sax "^1.2.4" +negotiator@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -13089,7 +12771,7 @@ node-emoji@^1.10.0: dependencies: lodash "^4.17.21" -node-fetch@^2.3.0, node-fetch@^2.6.7: +node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -13106,20 +12788,20 @@ node-gyp-build@^4.2.3: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -node-gyp@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" - integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== +node-gyp@^8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== dependencies: env-paths "^2.2.0" glob "^7.1.4" - graceful-fs "^4.2.3" + graceful-fs "^4.2.6" + make-fetch-happen "^9.1.0" nopt "^5.0.0" - npmlog "^4.1.2" - request "^2.88.2" + npmlog "^6.0.0" rimraf "^3.0.2" - semver "^7.3.2" - tar "^6.0.2" + semver "^7.3.5" + tar "^6.1.2" which "^2.0.2" node-int64@^0.4.0: @@ -13276,13 +12958,6 @@ npm-normalize-package-bin@^1.0.0: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -13290,15 +12965,15 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== +npmlog@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" nth-check@^2.0.1, nth-check@~1.0.1: version "2.0.1" @@ -13355,11 +13030,6 @@ nyc@^15.1.0: test-exclude "^6.0.0" yargs "^15.0.2" -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - object-assign@4.X, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -13496,17 +13166,12 @@ object.values@^1.1.1, object.values@^1.1.2, object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" -octokit-pagination-methods@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" - integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== - omggif@^1.0.10, omggif@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== -once@1.4.0, once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -13592,14 +13257,6 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-name@^3.0.0, os-name@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" - integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg== - dependencies: - macos-release "^2.2.0" - windows-release "^3.1.0" - os-shim@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" @@ -13750,9 +13407,9 @@ pako@^1.0.5, pako@~1.0.2, pako@~1.0.5: integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== pako@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" - integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== parallel-transform@^1.1.0: version "1.2.0" @@ -13906,7 +13563,7 @@ path-is-inside@^1.0.2: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= -path-key@^2.0.0, path-key@^2.0.1: +path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= @@ -14286,6 +13943,14 @@ promise-polyfill@^8.1.3: resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.3.tgz#2edc7e4b81aff781c88a0d577e5fe9da822107c6" integrity sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg== +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -14344,7 +14009,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.28, psl@^1.1.33: +psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== @@ -14401,7 +14066,7 @@ punycode@^1.2.4: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -qs@6.9.3, qs@^6.10.1, qs@^6.10.3, qs@~6.5.2: +qs@^6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -14528,14 +14193,14 @@ re-reselect@^3.4.0: resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-3.4.0.tgz#0f2303f3c84394f57f0cd31fea08a1ca4840a7cd" integrity sha512-JsecfN+JlckncVXTWFWjn0Vk6uInl8GSf4eEd9tTk5qXHlgqkPdILpnYpgZcISXNYAzvfvsCZviaDk8AxyS5sg== -re2@^1.15.4: - version "1.15.4" - resolved "https://registry.yarnpkg.com/re2/-/re2-1.15.4.tgz#2ffc3e4894fb60430393459978197648be01a0a9" - integrity sha512-7w3K+Daq/JjbX/dz5voMt7B9wlprVBQnMiypyCojAZ99kcAL+3LiJ5uBoX/u47l8eFTVq3Wj+V0pmvU+CT8tOg== +re2@1.17.4: + version "1.17.4" + resolved "https://registry.yarnpkg.com/re2/-/re2-1.17.4.tgz#7bf29290bdde963014e77bd2c2e799a6d788386e" + integrity sha512-xyZ4h5PqE8I9tAxTh3G0UttcK5ufrcUxReFjGzfX61vtanNbS1XZHjnwRSyPcLgChI4KLxVgOT/ioZXnUAdoTA== dependencies: - install-artifact-from-github "^1.0.2" - nan "^2.14.1" - node-gyp "^7.0.0" + install-artifact-from-github "^1.3.0" + nan "^2.15.0" + node-gyp "^8.4.1" react-ace@^7.0.5: version "7.0.5" @@ -14908,7 +14573,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -15273,32 +14938,6 @@ replace-ext@^1.0.0: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== -request@^2.88.2: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -15484,10 +15123,10 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry@0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== reusify@^1.0.4: version "1.0.4" @@ -15608,7 +15247,7 @@ safefs@^6.12.0: dependencies: graceful-fs "^4.2.6" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -15722,10 +15361,10 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@~7.3.0: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.8, semver@~7.3.0: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -15750,7 +15389,7 @@ serialize-javascript@^5.0.1: dependencies: randombytes "^2.1.0" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -15852,19 +15491,19 @@ side-channel@^1.0.3, side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -simple-git@^3.4.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.5.0.tgz#3c3538f4d7a1b3c8f3904412b12740bdcad9c8b1" - integrity sha512-fZsaq5nzdxQRhMNs6ESGLpMUHoL5GRP+boWPhq9pMYMKwOGZV2jHOxi8AbFFA2Y/6u4kR99HoULizSbpzaODkA== +simple-git@^3.16.0: + version "3.16.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.16.0.tgz#421773e24680f5716999cc4a1d60127b4b6a9dec" + integrity sha512-zuWYsOLEhbJRWVxpjdiXl6eyAyGo/KzVW+KFhhw9MqEEJttcq+32jTWSGyxTdf9e/YCohxRE+9xpWFj9FdiJNw== dependencies: "@kwsites/file-exists" "^1.1.1" "@kwsites/promise-deferred" "^1.1.1" - debug "^4.3.3" + debug "^4.3.4" simple-swizzle@^0.2.2: version "0.2.2" @@ -15934,6 +15573,11 @@ slide@~1.1.3: resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -15964,6 +15608,23 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socks-proxy-agent@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" + integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + +socks@^2.6.2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + dependencies: + ip "^2.0.0" + smart-buffer "^4.2.0" + sonic-boom@^1.0.2: version "1.4.1" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" @@ -16201,21 +15862,6 @@ sql-summary@^1.0.1: resolved "https://registry.yarnpkg.com/sql-summary/-/sql-summary-1.0.1.tgz#a2dddb5435bae294eb11424a7330dc5bafe09c2b" integrity sha512-IpCr2tpnNkP3Jera4ncexsZUp0enJBLr+pHCyTweMUBrbJsTgQeLWx1FXLhoBj/MvcnUQpkgOn2EY8FKOkUzww== -sshpk@^1.7.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - ssri@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" @@ -16231,7 +15877,7 @@ ssri@^7.0.0: figgy-pudding "^3.5.1" minipass "^3.1.1" -ssri@^8.0.1: +ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== @@ -16544,11 +16190,6 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -16566,11 +16207,6 @@ strip-json-comments@3.1.1, strip-json-comments@^3.0.1, strip-json-comments@^3.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@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - 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" @@ -16713,22 +16349,21 @@ success-symbol@^0.1.0: resolved "https://registry.yarnpkg.com/success-symbol/-/success-symbol-0.1.0.tgz#24022e486f3bf1cdca094283b769c472d3b72897" integrity sha1-JAIuSG878c3KCUKDt2nEctO3KJc= -superagent@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-7.1.2.tgz#71393141edd086ccf2544a29a4a609e46b7911f3" - integrity sha512-o9/fP6dww7a4xmEF5a484o2rG34UUGo8ztDlv7vbCWuqPhpndMi0f7eXxdlryk5U12Kzy46nh8eNpLAJ93Alsg== +superagent@^8.0.5: + version "8.0.8" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.0.8.tgz#345f06f552dc23f3581f2c30fda7d6ad247f146d" + integrity sha512-OpxPrqqWKOjmuomLq5pCm4LWCSFdgAQ11XVkMl7t4ie13WxWuLkdJ83ZgG2jOQeLXKwMR2p9k30hLrKGAzkPaA== dependencies: component-emitter "^1.3.0" - cookiejar "^2.1.3" - debug "^4.3.3" + cookiejar "^2.1.4" + debug "^4.3.4" fast-safe-stringify "^2.1.1" form-data "^4.0.0" - formidable "^2.0.1" + formidable "^2.1.2" methods "^1.1.2" - mime "^2.5.0" - qs "^6.10.1" - readable-stream "^3.6.0" - semver "^7.3.5" + mime "2.6.0" + qs "^6.11.0" + semver "^7.3.8" supertest-as-promised@^4.0.2: version "4.0.2" @@ -16738,13 +16373,13 @@ supertest-as-promised@^4.0.2: bluebird "^3.3.1" methods "^1.1.1" -supertest@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.2.2.tgz#04a5998fd3efaff187cb69f07a169755d655b001" - integrity sha512-wCw9WhAtKJsBvh07RaS+/By91NNE0Wh0DN19/hWPlBOU8tAfOtbZoVSV4xXeoKoxgPx0rx2y+y+8660XtE7jzg== +supertest@^6.3.3: + version "6.3.3" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" + integrity sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA== dependencies: methods "^1.1.2" - superagent "^7.1.0" + superagent "^8.0.5" supports-color@8.1.1, supports-color@^8.0.0: version "8.1.1" @@ -16889,7 +16524,7 @@ tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@6.1.11, tar@^6.0.2, tar@^6.1.11: +tar@6.1.11: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== @@ -16901,6 +16536,18 @@ tar@6.1.11, tar@^6.0.2, tar@^6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: + version "6.1.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^4.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tcp-port-used@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" @@ -17207,14 +16854,6 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" @@ -17318,10 +16957,10 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslib@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== +tslib@~2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tsutils@^3.17.1, tsutils@^3.21.0: version "3.21.0" @@ -17335,18 +16974,6 @@ tty-browserify@0.0.0: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -17685,32 +17312,6 @@ unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" -universal-user-agent@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-2.1.0.tgz#5abfbcc036a1ba490cb941f8fd68c46d3669e8e4" - integrity sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q== - dependencies: - os-name "^3.0.0" - -universal-user-agent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557" - integrity sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg== - dependencies: - os-name "^3.1.0" - -universal-user-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-5.0.0.tgz#a3182aa758069bf0e79952570ca757de3579c1d9" - integrity sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q== - dependencies: - os-name "^3.1.0" - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -17765,11 +17366,6 @@ url-parse@^1.5.9: querystringify "^2.1.1" requires-port "^1.0.0" -url-template@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" - integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE= - url@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" @@ -17968,11 +17564,6 @@ vega-event-selector@^3.0.0, vega-event-selector@~3.0.0: resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-3.0.0.tgz#7b855ac0c3ddb59bc5b5caa0d96dbbc9fbd33a4c" integrity sha512-Gls93/+7tEJGE3kUuUnxrBIxtvaNeF01VIFB2Q2Of2hBIBvtHX74jcAdDtkh5UhhoYGD8Q1J30P5cqEBEwtPoQ== -vega-event-selector@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-2.0.6.tgz#6beb00e066b78371dde1a0f40cb5e0bbaecfd8bc" - integrity sha512-UwCu50Sqd8kNZ1X/XgiAY+QAyQUmGFAwyDu7y0T5fs6/TPQnDo/Bo346NgSgINBEhEKOAMY1Nd/rPOk4UEm/ew== - vega-expression@^5.0.0, vega-expression@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-5.0.0.tgz#938f26689693a1e0d26716030cdaed43ca7abdfb" @@ -17981,13 +17572,6 @@ vega-expression@^5.0.0, vega-expression@~5.0.0: "@types/estree" "^0.0.50" vega-util "^1.16.0" -vega-expression@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-3.0.1.tgz#bbccd8f59371a537eab16f3d9eff5cbeaa27532d" - integrity sha512-+UwOFEkBnAWo8Zud6i8O4Pd2W6QqmPUOaAhjNtj0OxRL+d+Duoy7M4edUDZ+YuoUcMnjjBFfDQu7oRAA1fIMEQ== - dependencies: - vega-util "^1.15.2" - vega-force@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.1.0.tgz#cc8dea972baa52adc60840ff744ebb9e57d8f1f5" @@ -18063,23 +17647,21 @@ vega-label@~1.2.0: vega-scenegraph "^4.9.2" vega-util "^1.15.2" -vega-lite@^4.16.8: - version "4.17.0" - resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-4.17.0.tgz#01ad4535e92f28c3852c1071711de272ddfb4631" - integrity sha512-MO2XsaVZqx6iWWmVA5vwYFamvhRUsKfVp7n0pNlkZ2/21cuxelSl92EePZ2YGmzL6z4/3K7r/45zaG8p+qNHeg== +vega-lite@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-5.6.0.tgz#0f0adfc8b86f5eea071df186b2877d828c870c11" + integrity sha512-aTjQk//SzL9ctHY4ItA8yZSGflHMWPJmCXEs8LeRlixuOaAbamZmeL8xNMbQpS/vAZQeFAqjcJ32Fuztz/oGww== dependencies: - "@types/clone" "~2.1.0" - "@types/fast-json-stable-stringify" "^2.0.0" - array-flat-polyfill "^1.0.1" + "@types/clone" "~2.1.1" clone "~2.1.2" fast-deep-equal "~3.1.3" fast-json-stable-stringify "~2.1.0" - json-stringify-pretty-compact "~2.0.0" - tslib "~2.0.3" - vega-event-selector "~2.0.6" - vega-expression "~3.0.0" - vega-util "~1.16.0" - yargs "~16.0.3" + json-stringify-pretty-compact "~3.0.0" + tslib "~2.4.0" + vega-event-selector "~3.0.0" + vega-expression "~5.0.0" + vega-util "~1.17.0" + yargs "~17.6.0" vega-loader@^4.3.2, vega-loader@^4.4.0, vega-loader@~4.5.0: version "4.5.0" @@ -18219,11 +17801,6 @@ vega-util@^1.15.2, vega-util@^1.16.0, vega-util@^1.16.1, vega-util@^1.17.0, vega resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.17.0.tgz#b72ae0baa97f943bf591f8f5bb27ceadf06834ac" integrity sha512-HTaydZd9De3yf+8jH66zL4dXJ1d1p5OIFyoBzFiOli4IJbwkL1jrefCKz6AHDm1kYBzDJ0X4bN+CzZSCTvNk1w== -vega-util@~1.16.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.16.1.tgz#992bf3c3b6e145797214d99862841baea417ba39" - integrity sha512-FdgD72fmZMPJE99FxvFXth0IL4BbLA93WmBg/lvcJmfkK4Uf90WIlvGwaIUdSePIsdpkZjBPyQcHMQ8OcS8Smg== - vega-view-transforms@~4.5.8: version "4.5.8" resolved "https://registry.yarnpkg.com/vega-view-transforms/-/vega-view-transforms-4.5.8.tgz#c8dc42c3c7d4aa725d40b8775180c9f23bc98f4e" @@ -18300,15 +17877,6 @@ vega@^5.17.3: vega-voronoi "~4.2.0" vega-wordcloud "~4.1.3" -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vfile-location@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e" @@ -18680,7 +18248,7 @@ which@^2.0.1, which@^2.0.2, which@~2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: +wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== @@ -18692,13 +18260,6 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -windows-release@^3.1.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999" - integrity sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg== - dependencies: - execa "^1.0.0" - word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -18888,7 +18449,7 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -y18n@^5.0.1, y18n@^5.0.5: +y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== @@ -18931,11 +18492,16 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.0.0, yargs-parser@^20.2.2, yargs-parser@^20.2.3: +yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" @@ -18976,18 +18542,18 @@ yargs@^15.0.2, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@~16.0.3: - version "16.0.3" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.0.3.tgz#7a919b9e43c90f80d4a142a89795e85399a7e54c" - integrity sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA== +yargs@~17.6.0: + version "17.6.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" + integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== dependencies: - cliui "^7.0.0" - escalade "^3.0.2" + cliui "^8.0.1" + escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.1" - yargs-parser "^20.0.0" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" yauzl@^2.10.0: version "2.10.0" From a990ab9ae3f487acc46a5745edf793db4510db01 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Fri, 3 Feb 2023 16:17:26 -0800 Subject: [PATCH 04/37] Update VisLayer data models (#3374) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../vis_augmenter/common/types.test.ts | 51 +++++++------------ src/plugins/vis_augmenter/common/types.ts | 18 ++++--- 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/plugins/vis_augmenter/common/types.test.ts b/src/plugins/vis_augmenter/common/types.test.ts index 9e1282f40062..9e9b59d41425 100644 --- a/src/plugins/vis_augmenter/common/types.test.ts +++ b/src/plugins/vis_augmenter/common/types.test.ts @@ -5,56 +5,39 @@ import { VisLayerTypes, VisLayer, isPointInTimeEventsVisLayer, isValidVisLayer } from './types'; +const generateVisLayer = (type: any): VisLayer => { + return { + type, + originPlugin: 'test-plugin', + pluginResource: { + type: 'test-resource-type', + id: 'test-resource-id', + name: 'test-resource-name', + urlPath: 'test-resource-url-path', + }, + }; +}; + describe('isPointInTimeEventsVisLayer()', function () { it('should return false if type does not match', function () { - const visLayer = ({ - type: 'incorrect-type', - name: 'visLayerName', - field1: 'value1', - field2: 'value2', - } as unknown) as VisLayer; + const visLayer = generateVisLayer('unknown-vis-layer-type'); expect(isPointInTimeEventsVisLayer(visLayer)).toBe(false); }); it('should return true if type matches', function () { - const visLayer = { - type: VisLayerTypes.PointInTimeEvents, - name: 'testName', - events: [ - { - timestamp: 123, - resourceId: 'testId', - resourceName: 'testName', - }, - ], - } as VisLayer; + const visLayer = generateVisLayer(VisLayerTypes.PointInTimeEvents); expect(isPointInTimeEventsVisLayer(visLayer)).toBe(true); }); }); describe('isValidVisLayer()', function () { it('should return false if no valid type', function () { - const visLayer = ({ - type: 'incorrect-type', - name: 'visLayerName', - field1: 'value1', - field2: 'value2', - } as unknown) as VisLayer; + const visLayer = generateVisLayer('unknown-vis-layer-type'); expect(isValidVisLayer(visLayer)).toBe(false); }); it('should return true if type matches', function () { - const visLayer = { - type: VisLayerTypes.PointInTimeEvents, - name: 'testName', - events: [ - { - timestamp: 123, - resourceId: 'testId', - resourceName: 'testName', - }, - ], - } as VisLayer; + const visLayer = generateVisLayer(VisLayerTypes.PointInTimeEvents); expect(isValidVisLayer(visLayer)).toBe(true); }); }); diff --git a/src/plugins/vis_augmenter/common/types.ts b/src/plugins/vis_augmenter/common/types.ts index ea4561991488..ceb8b1973ba7 100644 --- a/src/plugins/vis_augmenter/common/types.ts +++ b/src/plugins/vis_augmenter/common/types.ts @@ -9,19 +9,25 @@ export enum VisLayerTypes { PointInTimeEvents = 'PointInTimeEvents', } +export type PluginResourceType = string; + +export interface PluginResource { + type: PluginResourceType; + id: string; + name: string; + urlPath: string; +} + export interface VisLayer { type: keyof typeof VisLayerTypes; - name: string; + originPlugin: string; + pluginResource: PluginResource; } export type VisLayers = VisLayer[]; -// resourceId & resourceName are required so that the -// events flyout can partition data based on these attributes -// (e.g., partitioning anomalies based on the detector they came from) export interface EventMetadata { - resourceId: string; - resourceName: string; + pluginResourceId: string; tooltip?: string; } From eeed5990303a699e8ccea501052378855875bd44 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 8 Feb 2023 15:58:55 -0800 Subject: [PATCH 05/37] Add a new `augment-vis` saved object type (#3109) Client-side: - creates & defines an augment vis saved object class - creates helper fns to convert from client-side definition (with no references) to server-side definition (with references) & vice-versa - introduce a saved object loader for the new saved object type. This loader provides a clean way to interact with all saved objects of this type, and can be consumed in dependent plugins for performing CRUD operations on saved objects of this type - creates and sets the new loader in the `vis_augmenter` plugin's `start` lifecycle step Server-side: - creates & defines a augment vis saved object type + index mapping - registers the new type in the `vis_augmenter` plugin's `setup` lifecycle step It also registers the saved object in the Saved Objects Management plugin, by: - adding a management section in the `augment-vis` saved object server-side definition, - registering a capabilities provider to allow some of the actions to work in the management plugin, and - adding the type to the plugin registry Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- src/plugins/saved_objects/README.md | 160 ++++++++++++------ .../opensearch_dashboards.json | 9 +- .../public/lib/in_app_url.test.ts | 16 ++ .../public/lib/in_app_url.ts | 2 + .../object_view/saved_object_view.tsx | 8 +- .../__snapshots__/relationships.test.tsx.snap | 145 ++++++++++++++++ .../components/relationships.test.tsx | 49 ++++++ .../saved_objects_management/public/plugin.ts | 2 + .../public/register_services.ts | 13 +- src/plugins/vis_augmenter/public/index.ts | 9 + src/plugins/vis_augmenter/public/plugin.ts | 17 +- .../saved_augment_vis/_saved_augment_vis.ts | 59 +++++++ .../public/saved_augment_vis/index.ts | 7 + .../saved_augment_vis.test.ts | 101 +++++++++++ .../saved_augment_vis/saved_augment_vis.ts | 62 +++++++ .../saved_augment_vis_references.test.ts | 111 ++++++++++++ .../saved_augment_vis_references.ts | 74 ++++++++ .../public/saved_augment_vis/utils/helpers.ts | 16 ++ .../public/saved_augment_vis/utils/index.ts | 7 + .../saved_augment_vis/utils/test_helpers.ts | 58 +++++++ src/plugins/vis_augmenter/public/services.ts | 11 ++ src/plugins/vis_augmenter/public/types.ts | 26 +++ .../server/capabilities_provider.ts | 13 ++ src/plugins/vis_augmenter/server/plugin.ts | 4 + .../server/saved_objects/augment_vis.ts | 41 +++++ .../server/saved_objects/index.ts | 6 + 26 files changed, 968 insertions(+), 58 deletions(-) create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/index.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/utils/index.ts create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts create mode 100644 src/plugins/vis_augmenter/public/services.ts create mode 100644 src/plugins/vis_augmenter/public/types.ts create mode 100644 src/plugins/vis_augmenter/server/capabilities_provider.ts create mode 100644 src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts create mode 100644 src/plugins/vis_augmenter/server/saved_objects/index.ts diff --git a/src/plugins/saved_objects/README.md b/src/plugins/saved_objects/README.md index 3b6dc4f7a79c..2f7d98dbb36b 100644 --- a/src/plugins/saved_objects/README.md +++ b/src/plugins/saved_objects/README.md @@ -2,77 +2,78 @@ The saved object plugin provides all the core services and functionalities of saved objects. It is utilized by many core plugins such as [`visualization`](../visualizations/), [`dashboard`](../dashboard/) and [`visBuilder`](../vis_builder/), as well as external plugins. Saved object is the primary way to store app and plugin data in a standardized form in OpenSearch Dashboards. They allow plugin developers to manage creating, saving, editing and retrieving data for the application. They can also make reference to other saved objects and have useful features out of the box, such as migrations and strict typings. The saved objects can be managed by the Saved Object Management UI. -## Save relationships to index pattern +### Relationships -Saved objects that have relationships to index patterns are saved using the [`kibanaSavedObjectMeta`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts#L59) attribute and the [`references`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts#L60) array structure. Functions from the data plugin are used by the saved object plugin to manage this index pattern relationship. +Saved objects can persist parent/child relationships to other saved objects via `references`. These relationships can be viewed on the UI in the [saved objects management plugin](src/core/server/saved_objects_management/README.md). Relationships can be useful to combine existing saved objects to produce new ones, such as using an index pattern as the source for a visualization, or a dashboard consisting of many visualizations. -A standard saved object and its index pattern relationship: +Some saved object fields have pre-defined logic. For example, if a saved object type has a `searchSource` field indicating an index pattern relationship, a reference will automatically be created using the [`kibanaSavedObjectMeta`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts#L59) attribute and the [`references`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts#L60) array structure. Functions from the data plugin are used by the saved object plugin to manage this index pattern relationship. + +An example of a visualization saved object and its index pattern relationship: ```ts "kibanaSavedObjectMeta" : { "searchSourceJSON" : """{"filter":[],"query":{"query":"","language":"kuery"},"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}""" - } - }, - "type" : "visualization", - "references" : [ - { - "name" : "kibanaSavedObjectMeta.searchSourceJSON.index", - "type" : "index-pattern", - "id" : "90943e30-9a47-11e8-b64d-95841ca0b247" - } - ], +} +"type" : "visualization", +"references" : [ + { + "name" : "kibanaSavedObjectMeta.searchSourceJSON.index", + "type" : "index-pattern", + "id" : "90943e30-9a47-11e8-b64d-95841ca0b247" + } +], ``` ### Saving a saved object -When saving a saved object and its relationship to the index pattern: +When saving a saved object and its relationship to the index pattern: 1. A saved object will be built using [`buildSavedObject`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts#L46) function. Services such as hydrating index pattern, initializing and serializing the saved object are set, and configs such as saved object id, migration version are defined. -2. The saved object will then be serialized by three steps: - - a. By using [`extractReferences`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/data/common/search/search_source/extract_references.ts#L35) function from the data plugin, the index pattern information will be extracted using the index pattern id within the `kibanaSavedObjectMeta`, and the id will be replaced by a reference name, such as `indexRefName`. A corresponding index pattern object will then be created to include more detailed information of the index pattern: name (`kibanaSavedObjectMeta.searchSourceJSON.index`), type, and id. - - ```ts - let searchSourceFields = { ...state }; - const references = []; - - if (searchSourceFields.index) { - const indexId = searchSourceFields.index.id || searchSourceFields.index; - const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; - references.push({ - name: refName, - type: 'index-pattern', - id: indexId - }); - searchSourceFields = { ...searchSourceFields, - indexRefName: refName, - index: undefined - }; - } - ``` +2. The saved object will then be serialized by three steps: - b. The `indexRefName` along with other information will be stringified and saved into `kibanaSavedObjectMeta.searchSourceJSON`. - - c. Saved object client will create the reference array attribute, and the index pattern object will be pushed into the reference array. + a. By using [`extractReferences`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/data/common/search/search_source/extract_references.ts#L35) function from the data plugin, the index pattern information will be extracted using the index pattern id within the `kibanaSavedObjectMeta`, and the id will be replaced by a reference name, such as `indexRefName`. A corresponding index pattern object will then be created to include more detailed information of the index pattern: name (`kibanaSavedObjectMeta.searchSourceJSON.index`), type, and id. + ```ts + let searchSourceFields = { ...state }; + const references = []; + + if (searchSourceFields.index) { + const indexId = searchSourceFields.index.id || searchSourceFields.index; + const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + references.push({ + name: refName, + type: 'index-pattern', + id: indexId, + }); + searchSourceFields = { ...searchSourceFields, indexRefName: refName, index: undefined }; + } + ``` + + b. The `indexRefName` along with other information will be stringified and saved into `kibanaSavedObjectMeta.searchSourceJSON`. + + c. Saved object client will create the reference array attribute, and the index pattern object will be pushed into the reference array. ### Loading an existing or creating a new saved object -1. When loading an existing object or creating a new saved object, [`initializeSavedObject`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/initialize_saved_object.ts#L38) function will be called. +1. When loading an existing object or creating a new saved object, [`initializeSavedObject`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/initialize_saved_object.ts#L38) function will be called. 2. The saved object will be deserialized in the [`applyOpenSearchResp`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/saved_objects/public/saved_object/helpers/apply_opensearch_resp.ts#L50) function. - a. Using [`injectReferences`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/data/common/search/search_source/inject_references.ts#L34) function from the data plugin, the index pattern reference name within the `kibanaSavedObject` will be substituted by the index pattern id and the corresponding index pattern reference object will be deleted if filters are applied. + a. Using [`injectReferences`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/data/common/search/search_source/inject_references.ts#L34) function from the data plugin, the index pattern reference name within the `kibanaSavedObject` will be substituted by the index pattern id and the corresponding index pattern reference object will be deleted if filters are applied. + + ```ts + searchSourceReturnFields.index = reference.id; + delete searchSourceReturnFields.indexRefName; + ``` - ```ts - searchSourceReturnFields.index = reference.id; - delete searchSourceReturnFields.indexRefName; - ``` +### Creating a new saved object type -### Others - -If a saved object type wishes to have additional custom functionalities when extracting/injecting references, or after OpenSearch's response, it can define functions in the class constructor when extending the `SavedObjectClass`. For example, visualization plugin's [`SavedVis`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#L91) class has additional `extractReferences`, `injectReferences` and `afterOpenSearchResp` functions defined in [`_saved_vis.ts`](../visualizations/public/saved_visualizations/_saved_vis.ts). +Steps need to be done on both the public/client-side & the server-side for creating a new saved object type. + +Client-side: + +1. Define a class that extends `SavedObjectClass`. This is where custom functionalities, such as extracting/injecting references, or overriding `afterOpenSearchResp` can be set in the constructor. For example, visualization plugin's [`SavedVis`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/4a06f5a6fe404a65b11775d292afaff4b8677c33/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#L91) class has additional `extractReferences`, `injectReferences` and `afterOpenSearchResp` functions defined in [`_saved_vis.ts`](../visualizations/public/saved_visualizations/_saved_vis.ts), and set in the `SavedVis` constructor. ```ts class SavedVis extends SavedObjectClass { @@ -85,14 +86,71 @@ class SavedVis extends SavedObjectClass { afterOpenSearchResp: async (savedObject: SavedObject) => { const savedVis = (savedObject as any) as ISavedVis; ... ... - + return (savedVis as any) as SavedObject; }, ``` +2. Optionally create a loader class that extends `SavedObjectLoader`. This can be useful for performing default CRUD operations on this particular saved object type, as well as overriding default utility functions like `find`. For example, the `visualization` saved object overrides `mapHitSource` (used in `find` & `findAll`) to do additional checking on the returned source object, such as if the returned type is valid: + +```ts +class SavedObjectLoaderVisualize extends SavedObjectLoader { + mapHitSource = (source: Record<string, any>, id: string) => { + const visTypes = visualizationTypes; + ... ... + let typeName = source.typeName; + if (source.visState) { + try { + typeName = JSON.parse(String(source.visState)).type; + } catch (e) { + /* missing typename handled below */ + } + } + + if (!typeName || !visTypes.get(typeName)) { + source.error = 'Unknown visualization type'; + return source; + } + ... ... + return source; + }; +``` + +The loader can then be instantiated once and referenced when needed. For example, the `visualizations` plugin creates and sets it in its `services` in the plugin's start lifecycle: + +```ts +public start( + core: CoreStart, + { data, expressions, uiActions, embeddable, dashboard }: VisualizationsStartDeps +): VisualizationsStart { + ... ... + const savedVisualizationsLoader = createSavedVisLoader({ + savedObjectsClient: core.savedObjects.client, + indexPatterns: data.indexPatterns, + search: data.search, + chrome: core.chrome, + overlays: core.overlays, + visualizationTypes: types, + }); + setSavedVisualizationsLoader(savedVisualizationsLoader); + ... ... +} +``` + +Server-side: + +1. Define the new type that is of type `SavedObjectsType`, which is where various settings can be configured, including the index mappings when the object is stored in the system index. To see an example type definition, you can refer to the [visualization saved object type](src/plugins/visualizations/server/saved_objects/visualization.ts). +2. Register the new type in the respective plugin's setup lifecycle function. For example, the `visualizations` plugin registers the `visualization` saved object type like below: + +```ts +core.savedObjects.registerType(visualizationSavedObjectType); +``` + +To make the new type manageable in the `saved_objects_management` plugin, refer to the [plugin README](src/plugins/saved_objects_management/README.md) + ## Migration -When a saved object is created using a previous version, the migration will trigger if there is a new way of saving the saved object and the migration functions alter the structure of the old saved object to follow the new structure. Migrations can be defined in the specific saved object type in the plugin's server folder. For example, +When a saved object is created using a previous version, the migration will trigger if there is a new way of saving the saved object and the migration functions alter the structure of the old saved object to follow the new structure. Migrations can be defined in the specific saved object type in the plugin's server folder. For example, ```ts export const visualizationSavedObjectType: SavedObjectsType = { @@ -116,4 +174,4 @@ The migraton version will be saved as a `migrationVersion` attribute in the save }, ``` -For a more detailed explanation on the migration, refer to [`saved objects management`](src/core/server/saved_objects/migrations/README.md). \ No newline at end of file +For a more detailed explanation on the migration, refer to [`saved objects management`](src/core/server/saved_objects/migrations/README.md). diff --git a/src/plugins/saved_objects_management/opensearch_dashboards.json b/src/plugins/saved_objects_management/opensearch_dashboards.json index 6d02893311e3..1de1260afceb 100644 --- a/src/plugins/saved_objects_management/opensearch_dashboards.json +++ b/src/plugins/saved_objects_management/opensearch_dashboards.json @@ -4,7 +4,14 @@ "server": true, "ui": true, "requiredPlugins": ["management", "data"], - "optionalPlugins": ["dashboard", "visualizations", "discover", "home", "visBuilder"], + "optionalPlugins": [ + "dashboard", + "visualizations", + "discover", + "home", + "visBuilder", + "visAugmenter" + ], "extraPublicDirs": ["public/lib"], "requiredBundles": ["opensearchDashboardsReact", "home"] } diff --git a/src/plugins/saved_objects_management/public/lib/in_app_url.test.ts b/src/plugins/saved_objects_management/public/lib/in_app_url.test.ts index ab524bb5d993..f537bac45522 100644 --- a/src/plugins/saved_objects_management/public/lib/in_app_url.test.ts +++ b/src/plugins/saved_objects_management/public/lib/in_app_url.test.ts @@ -77,6 +77,22 @@ describe('canViewInApp', () => { expect(canViewInApp(uiCapabilities, 'visualizations')).toEqual(false); }); + it('should handle augment-vis', () => { + let uiCapabilities = createCapabilities({ + visAugmenter: { + show: true, + }, + }); + expect(canViewInApp(uiCapabilities, 'augment-vis')).toEqual(true); + + uiCapabilities = createCapabilities({ + visAugmenter: { + show: false, + }, + }); + expect(canViewInApp(uiCapabilities, 'augment-vis')).toEqual(false); + }); + it('should handle index patterns', () => { let uiCapabilities = createCapabilities({ management: { diff --git a/src/plugins/saved_objects_management/public/lib/in_app_url.ts b/src/plugins/saved_objects_management/public/lib/in_app_url.ts index e55eaa858f4a..ea8a373bca8b 100644 --- a/src/plugins/saved_objects_management/public/lib/in_app_url.ts +++ b/src/plugins/saved_objects_management/public/lib/in_app_url.ts @@ -38,6 +38,8 @@ export function canViewInApp(uiCapabilities: Capabilities, type: string): boolea case 'visualization': case 'visualizations': return uiCapabilities.visualize.show as boolean; + case 'augment-vis': + return uiCapabilities.visAugmenter.show as boolean; case 'index-pattern': case 'index-patterns': case 'indexPatterns': diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index ce1a12fdbaef..8197fd0a2b51 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -169,7 +169,7 @@ export class SavedObjectEdition extends Component< ); if (confirmed) { await savedObjectsClient.delete(type, id); - notifications.toasts.addSuccess(`Deleted '${object!.attributes.title}' ${type} object`); + notifications.toasts.addSuccess(`Deleted ${this.formatTitle(object)} ${type} object`); this.redirectToListing(); } } @@ -179,10 +179,14 @@ export class SavedObjectEdition extends Component< const { object, type } = this.state; await savedObjectsClient.update(object!.type, object!.id, attributes, { references }); - notifications.toasts.addSuccess(`Updated '${attributes.title}' ${type} object`); + notifications.toasts.addSuccess(`Updated ${this.formatTitle(object)} ${type} object`); this.redirectToListing(); }; + formatTitle = (object: SimpleSavedObject<any> | undefined) => { + return object?.attributes?.title ? `'${object.attributes.title}'` : ''; + }; + redirectToListing() { this.props.history.push('/'); } diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap index b4842f289136..e7e57c341930 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap @@ -690,6 +690,151 @@ exports[`Relationships from legacy app should render visualizations normally 1`] </EuiFlyout> `; +exports[`Relationships should render augment-vis objects normally 1`] = ` +<EuiFlyout + onClose={[MockFunction]} +> + <EuiFlyoutHeader + hasBorder={true} + > + <EuiTitle + size="m" + > + <h2> + <EuiToolTip + content="augment-vis" + delay="regular" + position="top" + > + <EuiIcon + aria-label="augment-vis" + size="m" + type="savedObject" + /> + </EuiToolTip> +    + MyAugmentVisObject + </h2> + </EuiTitle> + </EuiFlyoutHeader> + <EuiFlyoutBody> + <div> + <EuiCallOut> + <p> + Here are the saved objects related to MyAugmentVisObject. Deleting this augment-vis affects its parent objects, but not its children. + </p> + </EuiCallOut> + <EuiSpacer /> + <EuiInMemoryTable + columns={ + Array [ + Object { + "align": "center", + "description": "Type of the saved object", + "field": "type", + "name": "Type", + "render": [Function], + "sortable": false, + "width": "50px", + }, + Object { + "data-test-subj": "directRelationship", + "dataType": "string", + "field": "relationship", + "name": "Direct relationship", + "render": [Function], + "sortable": false, + "width": "125px", + }, + Object { + "dataType": "string", + "description": "Title of the saved object", + "field": "meta.title", + "name": "Title", + "render": [Function], + "sortable": false, + }, + Object { + "actions": Array [ + Object { + "available": [Function], + "data-test-subj": "relationshipsTableAction-inspect", + "description": "Inspect this saved object", + "icon": "inspect", + "name": "Inspect", + "onClick": [Function], + "type": "icon", + }, + ], + "name": "Actions", + }, + ] + } + items={ + Array [ + Object { + "id": "1", + "meta": Object { + "editUrl": "/management/opensearch-dashboards/objects/savedVisualizations/1", + "icon": "visualizeApp", + "inAppUrl": Object { + "path": "/edit/1", + "uiCapabilitiesPath": "visualize.show", + }, + "title": "MyViz", + }, + "relationship": "child", + "type": "visualization", + }, + ] + } + pagination={true} + responsive={true} + rowProps={[Function]} + search={ + Object { + "filters": Array [ + Object { + "field": "relationship", + "multiSelect": "or", + "name": "Direct relationship", + "options": Array [ + Object { + "name": "parent", + "value": "parent", + "view": "Parent", + }, + Object { + "name": "child", + "value": "child", + "view": "Child", + }, + ], + "type": "field_value_selection", + }, + Object { + "field": "type", + "multiSelect": "or", + "name": "Type", + "options": Array [ + Object { + "name": "visualization", + "value": "visualization", + "view": "visualization", + }, + ], + "type": "field_value_selection", + }, + ], + } + } + tableLayout="fixed" + /> + </div> + </EuiFlyoutBody> +</EuiFlyout> +`; + exports[`Relationships should render dashboards normally 1`] = ` <EuiFlyout onClose={[MockFunction]} diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx index d78b98bdc6f0..1f21e5990c74 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx @@ -243,6 +243,55 @@ describe('Relationships', () => { expect(component).toMatchSnapshot(); }); + it('should render augment-vis objects normally', async () => { + const props: RelationshipsProps = { + goInspectObject: () => {}, + canGoInApp: () => true, + basePath: httpServiceMock.createSetupContract().basePath, + getRelationships: jest.fn().mockImplementation(() => [ + { + type: 'visualization', + id: '1', + relationship: 'child', + meta: { + title: 'MyViz', + icon: 'visualizeApp', + editUrl: '/management/opensearch-dashboards/objects/savedVisualizations/1', + inAppUrl: { + path: '/edit/1', + uiCapabilitiesPath: 'visualize.show', + }, + }, + }, + ]), + savedObject: { + id: '1', + type: 'augment-vis', + attributes: {}, + references: [], + meta: { + title: 'MyAugmentVisObject', + icon: 'savedObject', + editUrl: '/management/opensearch-dashboards/objects/savedAugmentVis/1', + }, + }, + close: jest.fn(), + }; + + const component = shallowWithI18nProvider(<Relationships {...props} />); + + // Make sure we are showing loading + expect(component.find('EuiLoadingSpinner').length).toBe(1); + + // Ensure all promises resolve + await new Promise((resolve) => process.nextTick(resolve)); + // Ensure the state changes are reflected + component.update(); + + expect(props.getRelationships).toHaveBeenCalled(); + expect(component).toMatchSnapshot(); + }); + it('should render dashboards normally', async () => { const props: RelationshipsProps = { goInspectObject: () => {}, diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index ec7d64ed700c..b2bcb614c50a 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -38,6 +38,7 @@ import { DashboardStart } from '../../dashboard/public'; import { DiscoverStart } from '../../discover/public'; import { HomePublicPluginSetup, FeatureCatalogueCategory } from '../../home/public'; import { VisualizationsStart } from '../../visualizations/public'; +import { VisAugmenterStart } from '../../vis_augmenter/public'; import { SavedObjectsManagementActionService, SavedObjectsManagementActionServiceSetup, @@ -75,6 +76,7 @@ export interface StartDependencies { data: DataPublicPluginStart; dashboard?: DashboardStart; visualizations?: VisualizationsStart; + visAugmenter?: VisAugmenterStart; discover?: DiscoverStart; visBuilder?: VisBuilderStart; } diff --git a/src/plugins/saved_objects_management/public/register_services.ts b/src/plugins/saved_objects_management/public/register_services.ts index 514ab66a4595..1b11eb578547 100644 --- a/src/plugins/saved_objects_management/public/register_services.ts +++ b/src/plugins/saved_objects_management/public/register_services.ts @@ -36,7 +36,10 @@ export const registerServices = async ( registry: ISavedObjectsManagementServiceRegistry, getStartServices: StartServicesAccessor<StartDependencies, SavedObjectsManagementPluginStart> ) => { - const [, { dashboard, visualizations, discover, visBuilder }] = await getStartServices(); + const [ + , + { dashboard, visualizations, visAugmenter, discover, visBuilder }, + ] = await getStartServices(); if (dashboard) { registry.register({ @@ -54,6 +57,14 @@ export const registerServices = async ( }); } + if (visAugmenter) { + registry.register({ + id: 'savedAugmentVis', + title: 'augmentVis', + service: visAugmenter.savedAugmentVisLoader, + }); + } + if (discover) { registry.register({ id: 'savedSearches', diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index e931e2ac2e03..cf736bf6d3e6 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -10,3 +10,12 @@ export function plugin(initializerContext: PluginInitializerContext) { return new VisAugmenterPlugin(initializerContext); } export { VisAugmenterSetup, VisAugmenterStart }; + +export { + createSavedAugmentVisLoader, + createAugmentVisSavedObject, + SavedAugmentVisLoader, + SavedObjectOpenSearchDashboardsServicesWithAugmentVis, +} from './saved_augment_vis'; + +export { ISavedAugmentVis, VisLayerExpressionFn, AugmentVisSavedObject } from './types'; diff --git a/src/plugins/vis_augmenter/public/plugin.ts b/src/plugins/vis_augmenter/public/plugin.ts index d53116bdd12d..1c064a1cee10 100644 --- a/src/plugins/vis_augmenter/public/plugin.ts +++ b/src/plugins/vis_augmenter/public/plugin.ts @@ -7,12 +7,15 @@ import { ExpressionsSetup } from '../../expressions/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; import { visLayers } from './expressions'; +import { setSavedAugmentVisLoader } from './services'; +import { createSavedAugmentVisLoader, SavedAugmentVisLoader } from './saved_augment_vis'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface VisAugmenterSetup {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface VisAugmenterStart {} +export interface VisAugmenterStart { + savedAugmentVisLoader: SavedAugmentVisLoader; +} export interface VisAugmenterSetupDeps { data: DataPublicPluginSetup; @@ -37,7 +40,15 @@ export class VisAugmenterPlugin } public start(core: CoreStart, { data }: VisAugmenterStartDeps): VisAugmenterStart { - return {}; + const savedAugmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: core.savedObjects.client, + indexPatterns: data.indexPatterns, + search: data.search, + chrome: core.chrome, + overlays: core.overlays, + }); + setSavedAugmentVisLoader(savedAugmentVisLoader); + return { savedAugmentVisLoader }; } public stop() {} diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts new file mode 100644 index 000000000000..ffaa64e92304 --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts @@ -0,0 +1,59 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @name SavedAugmentVis + * + * @extends SavedObject. + */ +import { get } from 'lodash'; +import { + createSavedObjectClass, + SavedObject, + SavedObjectOpenSearchDashboardsServices, +} from '../../../saved_objects/public'; +import { IIndexPattern } from '../../../data/public'; +import { extractReferences, injectReferences } from './saved_augment_vis_references'; + +const name = 'augment-vis'; + +export function createSavedAugmentVisClass(services: SavedObjectOpenSearchDashboardsServices) { + const SavedObjectClass = createSavedObjectClass(services); + + class SavedAugmentVis extends SavedObjectClass { + public static type: string = name; + public static mapping: Record<string, string> = { + description: 'text', + pluginResourceId: 'text', + visId: 'keyword', + visLayerExpressionFn: 'text', + version: 'integer', + }; + + constructor(opts: Record<string, unknown> | string = {}) { + if (typeof opts !== 'object') { + opts = { id: opts }; + } + super({ + type: SavedAugmentVis.type, + mapping: SavedAugmentVis.mapping, + extractReferences, + injectReferences, + id: (opts.id as string) || '', + indexPattern: opts.indexPattern as IIndexPattern, + defaults: { + description: get(opts, 'description', ''), + pluginResourceId: get(opts, 'pluginResourceId', ''), + visId: get(opts, 'visId', ''), + visLayerExpressionFn: get(opts, 'visLayerExpressionFn', {}), + version: 1, + }, + }); + this.showInRecentlyAccessed = false; + } + } + + return SavedAugmentVis as new (opts: Record<string, unknown> | string) => SavedObject; +} diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/index.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/index.ts new file mode 100644 index 000000000000..5ac3a159132e --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './saved_augment_vis'; +export * from './utils'; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts new file mode 100644 index 000000000000..bc44a0ed6dc2 --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts @@ -0,0 +1,101 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisLayerExpressionFn } from '../types'; +import { VisLayerTypes } from '../../common'; +import { + createSavedAugmentVisLoader, + SavedObjectOpenSearchDashboardsServicesWithAugmentVis, +} from './saved_augment_vis'; +import { generateAugmentVisSavedObject, getMockAugmentVisSavedObjectClient } from './utils'; + +describe('SavedObjectLoaderAugmentVis', () => { + const fn = { + type: VisLayerTypes.PointInTimeEvents, + name: 'test-fn', + args: { + testArg: 'test-value', + }, + } as VisLayerExpressionFn; + const validObj1 = generateAugmentVisSavedObject('valid-obj-id-1', fn); + const validObj2 = generateAugmentVisSavedObject('valid-obj-id-2', fn); + const invalidFnTypeObj = generateAugmentVisSavedObject('invalid-fn-obj-id-1', { + ...fn, + // @ts-ignore + type: 'invalid-type', + }); + // @ts-ignore + const missingFnObj = generateAugmentVisSavedObject('missing-fn-obj-id-1', {}); + + it('find returns single saved obj', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([validObj1]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.find(); + expect(resp.hits.length).toEqual(1); + expect(resp.hits[0].id).toEqual('valid-obj-id-1'); + expect(resp.hits[0].error).toEqual(undefined); + }); + + it('find returns multiple saved objs', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([validObj1, validObj2]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.find(); + expect(resp.hits.length).toEqual(2); + expect(resp.hits[0].id).toEqual('valid-obj-id-1'); + expect(resp.hits[1].id).toEqual('valid-obj-id-2'); + expect(resp.hits[0].error).toEqual(undefined); + expect(resp.hits[1].error).toEqual(undefined); + }); + + it('find returns empty response', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.find(); + expect(resp.hits.length).toEqual(0); + }); + + it('find does not return objs with errors', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([invalidFnTypeObj]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.find(); + expect(resp.hits.length).toEqual(0); + }); + + it('findAll returns obj with invalid VisLayer fn', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([invalidFnTypeObj]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.findAll(); + expect(resp.hits.length).toEqual(1); + expect(resp.hits[0].id).toEqual('invalid-fn-obj-id-1'); + expect(resp.hits[0].error).toEqual('Unknown VisLayer expression function type'); + }); + + it('findAll returns obj with missing VisLayer fn', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([missingFnObj]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.findAll(); + expect(resp.hits.length).toEqual(1); + expect(resp.hits[0].id).toEqual('missing-fn-obj-id-1'); + expect(resp.hits[0].error).toEqual( + 'visLayerExpressionFn is missing in augment-vis saved object' + ); + }); + + it('findAll returns obj with missing reference', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([validObj1], false), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.findAll(); + expect(resp.hits.length).toEqual(1); + expect(resp.hits[0].id).toEqual('valid-obj-id-1'); + expect(resp.hits[0].error).toEqual('visReference is missing in augment-vis saved object'); + }); +}); diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts new file mode 100644 index 000000000000..82e6e24a7e3e --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts @@ -0,0 +1,62 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get, isEmpty } from 'lodash'; +import { + SavedObjectLoader, + SavedObjectOpenSearchDashboardsServices, +} from '../../../saved_objects/public'; +import { createSavedAugmentVisClass } from './_saved_augment_vis'; +import { VisLayerTypes } from '../../common'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SavedObjectOpenSearchDashboardsServicesWithAugmentVis + extends SavedObjectOpenSearchDashboardsServices {} +export type SavedAugmentVisLoader = ReturnType<typeof createSavedAugmentVisLoader>; +export function createSavedAugmentVisLoader( + services: SavedObjectOpenSearchDashboardsServicesWithAugmentVis +) { + const { savedObjectsClient } = services; + + class SavedObjectLoaderAugmentVis extends SavedObjectLoader { + mapHitSource = (source: Record<string, any>, id: string) => { + source.id = id; + source.visId = get(source, 'visReference.id', ''); + + if (isEmpty(source.visReference)) { + source.error = 'visReference is missing in augment-vis saved object'; + return source; + } + if (isEmpty(source.visLayerExpressionFn)) { + source.error = 'visLayerExpressionFn is missing in augment-vis saved object'; + return source; + } + if (!(get(source, 'visLayerExpressionFn.type', '') in VisLayerTypes)) { + source.error = 'Unknown VisLayer expression function type'; + return source; + } + return source; + }; + + /** + * Updates hit.attributes to contain an id related to the referenced visualization + * (visId) and returns the updated attributes object. + * @param hit + * @returns {hit.attributes} The modified hit.attributes object, with an id and url field. + */ + mapSavedObjectApiHits(hit: { + references: any[]; + attributes: Record<string, unknown>; + id: string; + }) { + // For now we are assuming only one vis reference per saved object. + // If we change to multiple, we will need to dynamically handle that + const visReference = hit.references[0]; + return this.mapHitSource({ ...hit.attributes, visReference }, hit.id); + } + } + const SavedAugmentVis = createSavedAugmentVisClass(services); + return new SavedObjectLoaderAugmentVis(SavedAugmentVis, savedObjectsClient) as SavedObjectLoader; +} diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts new file mode 100644 index 000000000000..4a19b84dc40e --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts @@ -0,0 +1,111 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { extractReferences, injectReferences } from './saved_augment_vis_references'; +import { AugmentVisSavedObject } from '../types'; +import { VIS_REFERENCE_NAME } from './saved_augment_vis_references'; + +describe('extractReferences()', () => { + test('extracts nothing if visId is null', () => { + const doc = { + id: '1', + attributes: { + foo: true, + }, + references: [], + }; + const updatedDoc = extractReferences(doc); + expect(updatedDoc).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "foo": true, + }, + "references": Array [], + } + `); + }); + + test('extracts references from visId', () => { + const doc = { + id: '1', + attributes: { + foo: true, + visId: 'test-id', + }, + references: [], + }; + const updatedDoc = extractReferences(doc); + expect(updatedDoc).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "foo": true, + "visName": "visualization_0", + }, + "references": Array [ + Object { + "id": "test-id", + "name": "visualization_0", + "type": "visualization", + }, + ], + } + `); + }); +}); + +describe('injectReferences()', () => { + test('injects nothing when visName is null', () => { + const context = ({ + id: '1', + pluginResourceId: 'test-resource-id', + visLayerExpressionFn: 'test-fn', + } as unknown) as AugmentVisSavedObject; + injectReferences(context, []); + expect(context).toMatchInlineSnapshot(` + Object { + "id": "1", + "pluginResourceId": "test-resource-id", + "visLayerExpressionFn": "test-fn", + } + `); + }); + + test('injects references into context', () => { + const context = ({ + id: '1', + pluginResourceId: 'test-resource-id', + visLayerExpressionFn: 'test-fn', + visName: VIS_REFERENCE_NAME, + } as unknown) as AugmentVisSavedObject; + const references = [ + { + name: VIS_REFERENCE_NAME, + type: 'visualization', + id: 'test-id', + }, + ]; + injectReferences(context, references); + expect(context).toMatchInlineSnapshot(` + Object { + "id": "1", + "pluginResourceId": "test-resource-id", + "visId": "test-id", + "visLayerExpressionFn": "test-fn", + } + `); + }); + + test(`fails when it can't find the saved object reference in the array`, () => { + const context = ({ + id: '1', + pluginResourceId: 'test-resource-id', + visLayerExpressionFn: 'test-fn', + visName: VIS_REFERENCE_NAME, + } as unknown) as AugmentVisSavedObject; + expect(() => injectReferences(context, [])).toThrowErrorMatchingInlineSnapshot( + `"Could not find visualization reference \\"${VIS_REFERENCE_NAME}\\""` + ); + }); +}); diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts new file mode 100644 index 000000000000..5b2cc3f3d0e5 --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts @@ -0,0 +1,74 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectAttributes, SavedObjectReference } from '../../../../core/public'; +import { AugmentVisSavedObject } from '../types'; + +/** + * Note that references aren't stored in the object's client-side interface (AugmentVisSavedObject). + * Rather, just the ID/type is. The concept of references is a server-side definition used to define + * relationships between saved objects. They are visible in the saved objs management page or + * when making direct saved obj API calls. + * + * So, we need helper fns to construct & deconstruct references when creating and reading the + * indexed/stored saved objects, respectively. + */ + +/** + * Using a constant value for the visualization name to easily extact/inject + * the reference. Setting as "_0" which could be expanded and incremented upon + * in the future if we decide to persist multiple visualizations per + * AugmentVisSavedObject. + */ +export const VIS_REFERENCE_NAME = 'visualization_0'; + +/** + * Used during creation. Converting from AugmentVisSavedObject to the actual indexed saved object + * with references. + */ +export function extractReferences({ + attributes, + references = [], +}: { + attributes: SavedObjectAttributes; + references: SavedObjectReference[]; +}) { + const updatedAttributes = { ...attributes }; + const updatedReferences = [...references]; + + // Extract saved object + if (updatedAttributes.visId) { + updatedReferences.push({ + name: VIS_REFERENCE_NAME, + type: 'visualization', + id: String(updatedAttributes.visId), + }); + delete updatedAttributes.visId; + + updatedAttributes.visName = VIS_REFERENCE_NAME; + } + return { + references: updatedReferences, + attributes: updatedAttributes, + }; +} + +/** + * Used during reading. Converting from the indexed saved object with references + * to a AugmentVisSavedObject + */ +export function injectReferences( + savedObject: AugmentVisSavedObject, + references: SavedObjectReference[] +) { + if (savedObject.visName) { + const visReference = references.find((reference) => reference.name === savedObject.visName); + if (!visReference) { + throw new Error(`Could not find visualization reference "${savedObject.visName}"`); + } + savedObject.visId = visReference.id; + delete savedObject.visName; + } +} diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts new file mode 100644 index 000000000000..c3a54a377317 --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getSavedAugmentVisLoader } from '../../services'; +import { ISavedAugmentVis } from '../../types'; + +/** + * Create an augment vis saved object given an object that + * implements the ISavedAugmentVis interface + */ +export const createAugmentVisSavedObject = async (AugmentVis: ISavedAugmentVis): Promise<any> => { + const loader = getSavedAugmentVisLoader(); + return await loader.get((AugmentVis as any) as Record<string, unknown>); +}; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/index.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/index.ts new file mode 100644 index 000000000000..aa4d6dbf3e35 --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './helpers'; +export * from './test_helpers'; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts new file mode 100644 index 000000000000..c237fa7551c3 --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { cloneDeep } from 'lodash'; +import { VisLayerExpressionFn, ISavedAugmentVis } from '../../types'; +import { VIS_REFERENCE_NAME } from '../saved_augment_vis_references'; + +const pluginResourceId = 'test-plugin-resource-id'; +const visId = 'test-vis-id'; +const version = 1; + +export const generateAugmentVisSavedObject = (idArg: string, exprFnArg: VisLayerExpressionFn) => { + return { + id: idArg, + pluginResourceId, + visLayerExpressionFn: exprFnArg, + VIS_REFERENCE_NAME, + visId, + version, + } as ISavedAugmentVis; +}; + +export const getMockAugmentVisSavedObjectClient = ( + augmentVisSavedObjs: ISavedAugmentVis[], + keepReferences: boolean = true +): any => { + const savedObjs = (augmentVisSavedObjs = cloneDeep(augmentVisSavedObjs)); + + const client = { + find: jest.fn(() => + Promise.resolve({ + total: savedObjs.length, + savedObjects: savedObjs.map((savedObj) => { + const objVisId = savedObj.visId; + const objId = savedObj.id; + delete savedObj.visId; + delete savedObj.id; + return { + id: objId, + attributes: savedObj as Record<string, any>, + references: keepReferences + ? [ + { + name: savedObj.visName, + type: 'visualization', + id: objVisId, + }, + ] + : [], + }; + }), + }) + ), + } as any; + return client; +}; diff --git a/src/plugins/vis_augmenter/public/services.ts b/src/plugins/vis_augmenter/public/services.ts new file mode 100644 index 000000000000..00fa45374980 --- /dev/null +++ b/src/plugins/vis_augmenter/public/services.ts @@ -0,0 +1,11 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createGetterSetter } from '../../opensearch_dashboards_utils/common'; +import { SavedObjectLoader } from '../../saved_objects/public'; + +export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< + SavedObjectLoader +>('savedAugmentVisLoader'); diff --git a/src/plugins/vis_augmenter/public/types.ts b/src/plugins/vis_augmenter/public/types.ts new file mode 100644 index 000000000000..5ddd191cace5 --- /dev/null +++ b/src/plugins/vis_augmenter/public/types.ts @@ -0,0 +1,26 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObject } from '../../saved_objects/public'; +import { VisLayerTypes } from '../common'; + +export interface ISavedAugmentVis { + id?: string; + description?: string; + pluginResourceId: string; + visName?: string; + visId?: string; + visLayerExpressionFn: VisLayerExpressionFn; + version?: number; +} + +export interface VisLayerExpressionFn { + type: keyof typeof VisLayerTypes; + name: string; + // plugin expression fns can freely set custom arguments + args: { [key: string]: any }; +} + +export interface AugmentVisSavedObject extends SavedObject, ISavedAugmentVis {} diff --git a/src/plugins/vis_augmenter/server/capabilities_provider.ts b/src/plugins/vis_augmenter/server/capabilities_provider.ts new file mode 100644 index 000000000000..f9c899d3a0de --- /dev/null +++ b/src/plugins/vis_augmenter/server/capabilities_provider.ts @@ -0,0 +1,13 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const capabilitiesProvider = () => ({ + visAugmenter: { + show: true, + delete: true, + save: true, + saveQuery: true, + }, +}); diff --git a/src/plugins/vis_augmenter/server/plugin.ts b/src/plugins/vis_augmenter/server/plugin.ts index d921126a1f66..f30cf6c974fe 100644 --- a/src/plugins/vis_augmenter/server/plugin.ts +++ b/src/plugins/vis_augmenter/server/plugin.ts @@ -10,6 +10,8 @@ import { Plugin, Logger, } from '../../../core/server'; +import { augmentVisSavedObjectType } from './saved_objects'; +import { capabilitiesProvider } from './capabilities_provider'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface VisAugmenterPluginSetup {} @@ -26,6 +28,8 @@ export class VisAugmenterPlugin public setup(core: CoreSetup) { this.logger.debug('VisAugmenter: Setup'); + core.savedObjects.registerType(augmentVisSavedObjectType); + core.capabilities.registerProvider(capabilitiesProvider); return {}; } diff --git a/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts b/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts new file mode 100644 index 000000000000..0efe98fc14ce --- /dev/null +++ b/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectsType } from 'opensearch-dashboards/server'; + +export const augmentVisSavedObjectType: SavedObjectsType = { + name: 'augment-vis', + hidden: false, + namespaceType: 'single', + management: { + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/opensearch-dashboards/objects/savedAugmentVis/${encodeURIComponent( + obj.id + )}`; + }, + }, + mappings: { + properties: { + title: { type: 'text' }, + description: { type: 'text' }, + pluginResourceId: { type: 'text' }, + visName: { type: 'keyword', index: false, doc_values: false }, + visLayerExpressionFn: { + properties: { + type: { type: 'text' }, + name: { type: 'text' }, + // keeping generic to not limit what users may pass as args to their fns + // users may not have this field at all, if no args are needed + args: { type: 'object', dynamic: true }, + }, + }, + version: { type: 'integer' }, + }, + }, +}; diff --git a/src/plugins/vis_augmenter/server/saved_objects/index.ts b/src/plugins/vis_augmenter/server/saved_objects/index.ts new file mode 100644 index 000000000000..b96dbd4b2a58 --- /dev/null +++ b/src/plugins/vis_augmenter/server/saved_objects/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { augmentVisSavedObjectType } from './augment_vis'; From d63ef1fbd52649fb5cc3fd1e0124bf4769e2284c Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Tue, 21 Feb 2023 14:30:43 -0800 Subject: [PATCH 06/37] Create switch to render line charts using vega-lite (#3106) * initial commit for vega migration Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * Cleanup vega implementation Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * fix minor if statement Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * remove log Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * minor enhancements for robustness Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * Update fixes and more robust Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * fix yarn.lock conflicts Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * Clean up code Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * add additional comments Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * add unit tests Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * add fixes and update tests Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * fix test name Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --------- Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --- .../public/components/vega_vis_editor.tsx | 2 +- .../expressions/line_vega_spec_fn.test.js | 200 ++++++++++++ .../public/expressions/line_vega_spec_fn.ts | 299 ++++++++++++++++++ .../public/{ => expressions}/vega_fn.ts | 30 +- src/plugins/vis_type_vega/public/index.ts | 3 + src/plugins/vis_type_vega/public/plugin.ts | 4 +- .../public/vega_request_handler.ts | 2 +- .../public/vega_view/vega_base_view.js | 6 +- .../opensearch_dashboards.json | 2 +- src/plugins/vis_type_vislib/public/line.ts | 2 + .../public/line_to_expression.ts | 60 ++++ src/plugins/visualizations/public/index.ts | 6 +- .../public/legacy/build_pipeline.ts | 15 +- 13 files changed, 610 insertions(+), 21 deletions(-) create mode 100644 src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js create mode 100644 src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts rename src/plugins/vis_type_vega/public/{ => expressions}/vega_fn.ts (83%) create mode 100644 src/plugins/vis_type_vislib/public/line_to_expression.ts diff --git a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx index 86343ae10b0d..5fe1cbcfa6cd 100644 --- a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx +++ b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx @@ -37,7 +37,7 @@ import { i18n } from '@osd/i18n'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { getNotifications } from '../services'; -import { VisParams } from '../vega_fn'; +import { VisParams } from '../expressions/vega_fn'; import { VegaHelpMenu } from './vega_help_menu'; import { VegaActionsMenu } from './vega_actions_menu'; diff --git a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js new file mode 100644 index 000000000000..2260c46b841a --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js @@ -0,0 +1,200 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + buildLayerMark, + buildXAxis, + buildYAxis, + cleanString, + createSpecFromDatatable, + formatDataTable, + setupConfig, +} from './line_vega_spec_fn'; + +describe('cleanString()', function () { + it('string should not contain "', function () { + const dirtyString = '"someString"'; + expect(cleanString(dirtyString)).toBe('someString'); + }); +}); + +describe('setupConfig()', function () { + it('check all legend positions', function () { + const baseConfig = { + view: { + stroke: null, + }, + concat: { + spacing: 0, + }, + legend: { + orient: null, + }, + }; + const positions = ['top', 'right', 'left', 'bottom']; + positions.forEach((position) => { + const visParams = { legendPosition: position }; + baseConfig.legend.orient = position; + expect(setupConfig(visParams)).toStrictEqual(baseConfig); + }); + }); +}); + +describe('buildLayerMark()', function () { + const types = ['line', 'area', 'histogram']; + const interpolates = ['linear', 'cardinal', 'step-after']; + const strokeWidths = [-1, 0, 1, 2, 3, 4]; + const showCircles = [false, true]; + + it('check each mark possible value', function () { + const mark = { + type: null, + interpolate: null, + strokeWidth: null, + point: null, + }; + types.forEach((type) => { + mark.type = type; + interpolates.forEach((interpolate) => { + mark.interpolate = interpolate; + strokeWidths.forEach((strokeWidth) => { + mark.strokeWidth = strokeWidth; + showCircles.forEach((showCircle) => { + mark.point = showCircle; + const param = { + type: type, + interpolate: interpolate, + lineWidth: strokeWidth, + showCircles: showCircle, + }; + expect(buildLayerMark(param)).toStrictEqual(mark); + }); + }); + }); + }); + }); +}); + +describe('buildXAxis()', function () { + it('build different XAxis', function () { + const xAxisTitle = 'someTitle'; + const xAxisId = 'someId'; + const startTime = 1676596400; + const endTime = 1676796400; + [true, false].forEach((enableGrid) => { + const visParams = { grid: { categoryLines: enableGrid } }; + const vegaXAxis = { + axis: { + title: xAxisTitle, + grid: enableGrid, + }, + field: xAxisId, + type: 'temporal', + scale: { + domain: [startTime, endTime], + }, + }; + expect(buildXAxis(xAxisTitle, xAxisId, startTime, endTime, visParams)).toStrictEqual( + vegaXAxis + ); + }); + }); +}); + +describe('buildYAxis()', function () { + it('build different YAxis', function () { + const valueAxis = { + id: 'someId', + labels: { + rotate: 75, + show: false, + }, + position: 'left', + title: { + text: 'someText', + }, + }; + const column = { name: 'columnName', id: 'columnId' }; + const visParams = { grid: { valueAxis: true } }; + const vegaYAxis = { + axis: { + title: 'someText', + grid: true, + orient: 'left', + labels: false, + labelAngle: 75, + }, + field: 'columnId', + type: 'quantitative', + }; + expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); + + valueAxis.title.text = '""'; + vegaYAxis.axis.title = 'columnName'; + expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); + }); +}); + +describe('createSpecFromDatatable()', function () { + it('build simple line chart"', function () { + const datatable = + '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-2":1672214400000,"col-1-1":44},{"col-0-2":1672300800000,"col-1-1":150},{"col-0-2":1672387200000,"col-1-1":154},{"col-0-2":1672473600000,"col-1-1":144},{"col-0-2":1672560000000,"col-1-1":133},{"col-0-2":1672646400000,"col-1-1":149},{"col-0-2":1672732800000,"col-1-1":152},{"col-0-2":1672819200000,"col-1-1":144},{"col-0-2":1672905600000,"col-1-1":166},{"col-0-2":1672992000000,"col-1-1":151},{"col-0-2":1673078400000,"col-1-1":143},{"col-0-2":1673164800000,"col-1-1":148},{"col-0-2":1673251200000,"col-1-1":146},{"col-0-2":1673337600000,"col-1-1":137},{"col-0-2":1673424000000,"col-1-1":152},{"col-0-2":1673510400000,"col-1-1":152},{"col-0-2":1673596800000,"col-1-1":151},{"col-0-2":1673683200000,"col-1-1":157},{"col-0-2":1673769600000,"col-1-1":151},{"col-0-2":1673856000000,"col-1-1":152},{"col-0-2":1673942400000,"col-1-1":142},{"col-0-2":1674028800000,"col-1-1":151},{"col-0-2":1674115200000,"col-1-1":163},{"col-0-2":1674201600000,"col-1-1":156},{"col-0-2":1674288000000,"col-1-1":153},{"col-0-2":1674374400000,"col-1-1":162},{"col-0-2":1674460800000,"col-1-1":152},{"col-0-2":1674547200000,"col-1-1":159},{"col-0-2":1674633600000,"col-1-1":165},{"col-0-2":1674720000000,"col-1-1":153},{"col-0-2":1674806400000,"col-1-1":149},{"col-0-2":1674892800000,"col-1-1":94}],"columns":[{"id":"col-0-2","name":"order_date per day","meta":{"type":"date_histogram","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"order_date","timeRange":{"from":"now-90d","to":"now"},"useNormalizedOpenSearchInterval":true,"scaleMetricValues":false,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{}}}},{"id":"col-1-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}}]}'; + const visParams = + '{"addLegend":true,"addTimeMarker":false,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"grid":{"categoryLines":false},"labels":{},"legendPosition":"right","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#E7664C","show":false,"style":"full","value":10,"width":1},"times":[],"type":"line","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":0,"show":true,"truncate":100},"name":"LeftAxis-1","position":"left","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]}'; + const dimensions = + '{"x":{"accessor":0,"format":{"id":"date","params":{"pattern":"YYYY-MM-DD"}},"params":{"date":true,"interval":"P1D","intervalESValue":1,"intervalESUnit":"d","format":"YYYY-MM-DD","bounds":{"min":"2022-11-18T00:14:09.617Z","max":"2023-02-16T00:14:09.617Z"}},"label":"order_date per day","aggType":"date_histogram"},"y":[{"accessor":1,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"}]}'; + const spec = + '{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"col-0-2":1672214400000,"col-1-1":44},{"col-0-2":1672300800000,"col-1-1":150},{"col-0-2":1672387200000,"col-1-1":154},{"col-0-2":1672473600000,"col-1-1":144},{"col-0-2":1672560000000,"col-1-1":133},{"col-0-2":1672646400000,"col-1-1":149},{"col-0-2":1672732800000,"col-1-1":152},{"col-0-2":1672819200000,"col-1-1":144},{"col-0-2":1672905600000,"col-1-1":166},{"col-0-2":1672992000000,"col-1-1":151},{"col-0-2":1673078400000,"col-1-1":143},{"col-0-2":1673164800000,"col-1-1":148},{"col-0-2":1673251200000,"col-1-1":146},{"col-0-2":1673337600000,"col-1-1":137},{"col-0-2":1673424000000,"col-1-1":152},{"col-0-2":1673510400000,"col-1-1":152},{"col-0-2":1673596800000,"col-1-1":151},{"col-0-2":1673683200000,"col-1-1":157},{"col-0-2":1673769600000,"col-1-1":151},{"col-0-2":1673856000000,"col-1-1":152},{"col-0-2":1673942400000,"col-1-1":142},{"col-0-2":1674028800000,"col-1-1":151},{"col-0-2":1674115200000,"col-1-1":163},{"col-0-2":1674201600000,"col-1-1":156},{"col-0-2":1674288000000,"col-1-1":153},{"col-0-2":1674374400000,"col-1-1":162},{"col-0-2":1674460800000,"col-1-1":152},{"col-0-2":1674547200000,"col-1-1":159},{"col-0-2":1674633600000,"col-1-1":165},{"col-0-2":1674720000000,"col-1-1":153},{"col-0-2":1674806400000,"col-1-1":149},{"col-0-2":1674892800000,"col-1-1":94}]},"config":{"view":{"stroke":null},"concat":{"spacing":0},"legend":{"orient":"right"}},"layer":[{"mark":{"type":"line","interpolate":"linear","strokeWidth":2,"point":true},"encoding":{"x":{"axis":{"title":"order_date per day","grid":false},"field":"col-0-2","type":"temporal","scale":{"domain":[1668730449617,1676506449617]}},"y":{"axis":{"title":"Count","orient":"left","labels":true,"labelAngle":0},"field":"col-1-1","type":"quantitative"},"tooltip":[{"field":"col-0-2","type":"temporal","title":"order_date per day"},{"field":"col-1-1","type":"quantitative","title":"Count"}],"color":{"datum":"Count"}}}]}'; + expect( + JSON.stringify( + createSpecFromDatatable( + formatDataTable(JSON.parse(datatable)), + JSON.parse(visParams), + JSON.parse(dimensions) + ) + ) + ).toBe(spec); + }); + + it('build empty chart if no x-axis is defined"', function () { + const datatable = + '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-1":4675}],"columns":[{"id":"col-0-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}}]}'; + const visParams = + '{"type":"line","grid":{"categoryLines":false},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"filter":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":true,"type":"line","mode":"normal","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"lineWidth":2,"interpolate":"linear","showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"labels":{},"thresholdLine":{"show":false,"value":10,"width":1,"style":"full","color":"#E7664C"}}'; + const dimensions = + '{"x":null,"y":[{"accessor":0,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"}]}'; + const spec = + '{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"col-0-1":4675}]},"config":{"view":{"stroke":null},"concat":{"spacing":0},"legend":{"orient":"right"}},"layer":[]}'; + expect( + JSON.stringify( + createSpecFromDatatable( + formatDataTable(JSON.parse(datatable)), + JSON.parse(visParams), + JSON.parse(dimensions) + ) + ) + ).toBe(spec); + }); + + it('build complicated line chart"', function () { + const datatable = + '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-2":1672214400000,"col-1-1":44,"col-2-3":60.9375},{"col-0-2":1672300800000,"col-1-1":150,"col-2-3":82.5},{"col-0-2":1672387200000,"col-1-1":154,"col-2-3":79.5},{"col-0-2":1672473600000,"col-1-1":144,"col-2-3":75.875},{"col-0-2":1672560000000,"col-1-1":133,"col-2-3":259.25},{"col-0-2":1672646400000,"col-1-1":149,"col-2-3":90},{"col-0-2":1672732800000,"col-1-1":152,"col-2-3":79.0625},{"col-0-2":1672819200000,"col-1-1":144,"col-2-3":82.5},{"col-0-2":1672905600000,"col-1-1":166,"col-2-3":85.25},{"col-0-2":1672992000000,"col-1-1":151,"col-2-3":92},{"col-0-2":1673078400000,"col-1-1":143,"col-2-3":90.75},{"col-0-2":1673164800000,"col-1-1":148,"col-2-3":92},{"col-0-2":1673251200000,"col-1-1":146,"col-2-3":83.25},{"col-0-2":1673337600000,"col-1-1":137,"col-2-3":98},{"col-0-2":1673424000000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673510400000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673596800000,"col-1-1":151,"col-2-3":87.4375},{"col-0-2":1673683200000,"col-1-1":157,"col-2-3":63.75},{"col-0-2":1673769600000,"col-1-1":151,"col-2-3":81.5625},{"col-0-2":1673856000000,"col-1-1":152,"col-2-3":100.6875},{"col-0-2":1673942400000,"col-1-1":142,"col-2-3":98},{"col-0-2":1674028800000,"col-1-1":151,"col-2-3":100.8125},{"col-0-2":1674115200000,"col-1-1":163,"col-2-3":83.6875},{"col-0-2":1674201600000,"col-1-1":156,"col-2-3":85.8125},{"col-0-2":1674288000000,"col-1-1":153,"col-2-3":98},{"col-0-2":1674374400000,"col-1-1":162,"col-2-3":75.9375},{"col-0-2":1674460800000,"col-1-1":152,"col-2-3":113.375},{"col-0-2":1674547200000,"col-1-1":159,"col-2-3":73.625},{"col-0-2":1674633600000,"col-1-1":165,"col-2-3":72.8125},{"col-0-2":1674720000000,"col-1-1":153,"col-2-3":113.375},{"col-0-2":1674806400000,"col-1-1":149,"col-2-3":82.5},{"col-0-2":1674892800000,"col-1-1":94,"col-2-3":54}],"columns":[{"id":"col-0-2","name":"order_date per day","meta":{"type":"date_histogram","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"order_date","timeRange":{"from":"now-90d","to":"now"},"useNormalizedOpenSearchInterval":true,"scaleMetricValues":false,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{}}}},{"id":"col-1-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}},{"id":"col-2-3","name":"Max products.min_price","meta":{"type":"max","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"products.min_price"}}}]}'; + const visParams = + '{"addLegend":true,"addTimeMarker":true,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"grid":{"categoryLines":false,"valueAxis":"ValueAxis-1"},"labels":{},"legendPosition":"bottom","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"},{"data":{"id":"3","label":"Max products.min_price"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#E7664C","show":true,"style":"dashed","value":100,"width":1},"times":[],"type":"line","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":75,"show":true,"truncate":100},"name":"RightAxis-1","position":"right","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]}'; + const dimensions = + '{"x":{"accessor":0,"format":{"id":"date","params":{"pattern":"YYYY-MM-DD"}},"params":{"date":true,"interval":"P1D","intervalESValue":1,"intervalESUnit":"d","format":"YYYY-MM-DD","bounds":{"min":"2022-11-19T03:26:04.730Z","max":"2023-02-17T03:26:04.730Z"}},"label":"order_date per day","aggType":"date_histogram"},"y":[{"accessor":1,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"},{"accessor":2,"format":{"id":"number","params":{"parsedUrl":{"origin":"http://localhost:5603","pathname":"/rao/app/visualize","basePath":"/rao"}}},"params":{},"label":"Max products.min_price","aggType":"max"}]}'; + const spec = + '{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"col-0-2":1672214400000,"col-1-1":44,"col-2-3":60.9375},{"col-0-2":1672300800000,"col-1-1":150,"col-2-3":82.5},{"col-0-2":1672387200000,"col-1-1":154,"col-2-3":79.5},{"col-0-2":1672473600000,"col-1-1":144,"col-2-3":75.875},{"col-0-2":1672560000000,"col-1-1":133,"col-2-3":259.25},{"col-0-2":1672646400000,"col-1-1":149,"col-2-3":90},{"col-0-2":1672732800000,"col-1-1":152,"col-2-3":79.0625},{"col-0-2":1672819200000,"col-1-1":144,"col-2-3":82.5},{"col-0-2":1672905600000,"col-1-1":166,"col-2-3":85.25},{"col-0-2":1672992000000,"col-1-1":151,"col-2-3":92},{"col-0-2":1673078400000,"col-1-1":143,"col-2-3":90.75},{"col-0-2":1673164800000,"col-1-1":148,"col-2-3":92},{"col-0-2":1673251200000,"col-1-1":146,"col-2-3":83.25},{"col-0-2":1673337600000,"col-1-1":137,"col-2-3":98},{"col-0-2":1673424000000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673510400000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673596800000,"col-1-1":151,"col-2-3":87.4375},{"col-0-2":1673683200000,"col-1-1":157,"col-2-3":63.75},{"col-0-2":1673769600000,"col-1-1":151,"col-2-3":81.5625},{"col-0-2":1673856000000,"col-1-1":152,"col-2-3":100.6875},{"col-0-2":1673942400000,"col-1-1":142,"col-2-3":98},{"col-0-2":1674028800000,"col-1-1":151,"col-2-3":100.8125},{"col-0-2":1674115200000,"col-1-1":163,"col-2-3":83.6875},{"col-0-2":1674201600000,"col-1-1":156,"col-2-3":85.8125},{"col-0-2":1674288000000,"col-1-1":153,"col-2-3":98},{"col-0-2":1674374400000,"col-1-1":162,"col-2-3":75.9375},{"col-0-2":1674460800000,"col-1-1":152,"col-2-3":113.375},{"col-0-2":1674547200000,"col-1-1":159,"col-2-3":73.625},{"col-0-2":1674633600000,"col-1-1":165,"col-2-3":72.8125},{"col-0-2":1674720000000,"col-1-1":153,"col-2-3":113.375},{"col-0-2":1674806400000,"col-1-1":149,"col-2-3":82.5},{"col-0-2":1674892800000,"col-1-1":94,"col-2-3":54}]},"config":{"view":{"stroke":null},"concat":{"spacing":0},"legend":{"orient":"bottom"}},"layer":[{"mark":{"type":"line","interpolate":"linear","strokeWidth":2,"point":true},"encoding":{"x":{"axis":{"title":"order_date per day","grid":false},"field":"col-0-2","type":"temporal","scale":{"domain":[1668828364730,1676604364730]}},"y":{"axis":{"title":"Count","grid":"ValueAxis-1","orient":"right","labels":true,"labelAngle":75},"field":"col-1-1","type":"quantitative"},"tooltip":[{"field":"col-0-2","type":"temporal","title":"order_date per day"},{"field":"col-1-1","type":"quantitative","title":"Count"}],"color":{"datum":"Count"}}},{"mark":{"type":"line","interpolate":"linear","strokeWidth":2,"point":true},"encoding":{"x":{"axis":{"title":"order_date per day","grid":false},"field":"col-0-2","type":"temporal","scale":{"domain":[1668828364730,1676604364730]}},"y":{"axis":{"title":"Count","grid":"ValueAxis-1","orient":"right","labels":true,"labelAngle":75},"field":"col-2-3","type":"quantitative"},"tooltip":[{"field":"col-0-2","type":"temporal","title":"order_date per day"},{"field":"col-2-3","type":"quantitative","title":"Max products.min_price"}],"color":{"datum":"Max products.min_price"}}},{"mark":"rule","encoding":{"x":{"type":"temporal","field":"now_field"},"color":{"value":"red"},"size":{"value":1}}},{"mark":{"type":"rule","color":"#E7664C","strokeDash":[8,8]},"encoding":{"y":{"datum":100}}}],"transform":[{"calculate":"now()","as":"now_field"}]}'; + expect( + JSON.stringify( + createSpecFromDatatable( + formatDataTable(JSON.parse(datatable)), + JSON.parse(visParams), + JSON.parse(dimensions) + ) + ) + ).toBe(spec); + }); +}); diff --git a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts new file mode 100644 index 000000000000..b0f440e59647 --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts @@ -0,0 +1,299 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { cloneDeep } from 'lodash'; +import { i18n } from '@osd/i18n'; +import { + ExpressionFunctionDefinition, + OpenSearchDashboardsDatatable, + OpenSearchDashboardsDatatableColumn, +} from '../../../expressions/public'; +import { VegaVisualizationDependencies } from '../plugin'; +import { VislibDimensions, VisParams } from '../../../visualizations/public'; + +type Input = OpenSearchDashboardsDatatable; +type Output = Promise<string>; + +interface Arguments { + visLayers: string | null; + visParams: string; + dimensions: string; +} + +export type LineVegaSpecExpressionFunctionDefinition = ExpressionFunctionDefinition< + 'line_vega_spec', + Input, + Arguments, + Output +>; + +// TODO: move this to the visualization plugin that has VisParams once all of these parameters have been better defined +interface ValueAxis { + id: string; + labels: { + filter: boolean; + rotate: number; + show: boolean; + truncate: number; + }; + name: string; + position: string; + scale: { + mode: string; + type: string; + }; + show: true; + style: any; + title: { + text: string; + }; + type: string; +} + +// Get the first xaxis field as only 1 setup of X Axis will be supported and +// there won't be support for split series and split chart +const getXAxisId = (dimensions: any, columns: OpenSearchDashboardsDatatableColumn[]): string => { + return columns.filter((column) => column.name === dimensions.x.label)[0].id; +}; + +export const cleanString = (rawString: string): string => { + return rawString.replaceAll('"', ''); +}; + +export const formatDataTable = ( + datatable: OpenSearchDashboardsDatatable +): OpenSearchDashboardsDatatable => { + datatable.columns.forEach((column) => { + // clean quotation marks from names in columns + column.name = cleanString(column.name); + }); + return datatable; +}; + +export const setupConfig = (visParams: VisParams) => { + const legendPosition = visParams.legendPosition; + return { + view: { + stroke: null, + }, + concat: { + spacing: 0, + }, + legend: { + orient: legendPosition, + }, + }; +}; + +export const buildLayerMark = (seriesParams: { + type: string; + interpolate: string; + lineWidth: number; + showCircles: boolean; +}) => { + return { + // Possible types are: line, area, histogram. The eligibility checker will + // prevent area and histogram (though area works in vega-lite) + type: seriesParams.type, + // Possible types: linear, cardinal, step-after. All of these types work in vega-lite + interpolate: seriesParams.interpolate, + // The possible values is any number, which matches what vega-lite supports + strokeWidth: seriesParams.lineWidth, + // this corresponds to showing the dots in the visbuilder for each data point + point: seriesParams.showCircles, + }; +}; + +export const buildXAxis = ( + xAxisTitle: string, + xAxisId: string, + startTime: number, + endTime: number, + visParams: VisParams +) => { + return { + axis: { + title: xAxisTitle, + grid: visParams.grid.categoryLines, + }, + field: xAxisId, + // Right now, the line charts can only set the x-axis value to be a date attribute, so + // this should always be of type temporal + type: 'temporal', + scale: { + domain: [startTime, endTime], + }, + }; +}; + +export const buildYAxis = ( + column: OpenSearchDashboardsDatatableColumn, + valueAxis: ValueAxis, + visParams: VisParams +) => { + return { + axis: { + title: cleanString(valueAxis.title.text) || column.name, + grid: visParams.grid.valueAxis, + orient: valueAxis.position, + labels: valueAxis.labels.show, + labelAngle: valueAxis.labels.rotate, + }, + field: column.id, + type: 'quantitative', + }; +}; + +export const createSpecFromDatatable = ( + datatable: OpenSearchDashboardsDatatable, + visParams: VisParams, + dimensions: VislibDimensions +): object => { + // TODO: we can try to use VegaSpec type but it is currently very outdated, where many + // of the fields and sub-fields don't have other optional params that we want for customizing. + // For now, we make this more loosely-typed by just specifying it as a generic object. + const spec = {} as any; + + spec.$schema = 'https://vega.github.io/schema/vega-lite/v5.json'; + spec.data = { + values: datatable.rows, + }; + spec.config = setupConfig(visParams); + + // Get the valueAxes data and generate a map to easily fetch the different valueAxes data + const valueAxis = new Map(); + visParams?.valueAxes?.forEach((yAxis: ValueAxis) => { + valueAxis.set(yAxis.id, yAxis); + }); + + spec.layer = [] as any[]; + + if (datatable.rows.length > 0 && dimensions.x !== null) { + const xAxisId = getXAxisId(dimensions, datatable.columns); + const xAxisTitle = cleanString(dimensions.x.label); + // get x-axis bounds for the chart + const startTime = new Date(dimensions.x.params.bounds.min).valueOf(); + const endTime = new Date(dimensions.x.params.bounds.max).valueOf(); + let skip = 0; + datatable.columns.forEach((column, index) => { + // Check if it's not xAxis column data + if (column.meta?.aggConfigParams?.interval !== undefined) { + skip++; + } else { + const currentSeriesParams = visParams.seriesParams[index - skip]; + const currentValueAxis = valueAxis.get(currentSeriesParams.valueAxis.toString()); + let tooltip: Array<{ field: string; type: string; title: string }> = []; + if (visParams.addTooltip) { + tooltip = [ + { field: xAxisId, type: 'temporal', title: xAxisTitle }, + { field: column.id, type: 'quantitative', title: column.name }, + ]; + } + spec.layer.push({ + mark: buildLayerMark(currentSeriesParams), + encoding: { + x: buildXAxis(xAxisTitle, xAxisId, startTime, endTime, visParams), + y: buildYAxis(column, currentValueAxis, visParams), + tooltip, + color: { + // This ensures all the different metrics have their own distinct and unique color + datum: column.name, + }, + }, + }); + } + }); + } + + if (visParams.addTimeMarker) { + spec.transform = [ + { + calculate: 'now()', + as: 'now_field', + }, + ]; + + spec.layer.push({ + mark: 'rule', + encoding: { + x: { + type: 'temporal', + field: 'now_field', + }, + // The time marker on vislib is red, so keeping this consistent + color: { + value: 'red', + }, + size: { + value: 1, + }, + }, + }); + } + + if (visParams.thresholdLine.show as boolean) { + const layer = { + mark: { + type: 'rule', + color: visParams.thresholdLine.color, + strokeDash: [1, 0], + }, + encoding: { + y: { + datum: visParams.thresholdLine.value, + }, + }, + }; + + // Can only support making a threshold line with full or dashed style, but not dot-dashed + // due to vega-lite limitations + if (visParams.thresholdLine.style !== 'full') { + layer.mark.strokeDash = [8, 8]; + } + + spec.layer.push(layer); + } + + return spec; +}; + +export const createLineVegaSpecFn = ( + dependencies: VegaVisualizationDependencies +): LineVegaSpecExpressionFunctionDefinition => ({ + name: 'line_vega_spec', + type: 'string', + inputTypes: ['opensearch_dashboards_datatable'], + help: i18n.translate('visTypeVega.function.help', { + defaultMessage: 'Construct line vega spec', + }), + args: { + visLayers: { + types: ['string', 'null'], + default: '', + help: '', + }, + visParams: { + types: ['string'], + default: '""', + help: '', + }, + dimensions: { + types: ['string'], + default: '""', + help: '', + }, + }, + async fn(input, args, context) { + const table = cloneDeep(input); + + // creating initial vega spec from table + const spec = createSpecFromDatatable( + formatDataTable(table), + JSON.parse(args.visParams), + JSON.parse(args.dimensions) + ); + return JSON.stringify(spec); + }, +}); diff --git a/src/plugins/vis_type_vega/public/vega_fn.ts b/src/plugins/vis_type_vega/public/expressions/vega_fn.ts similarity index 83% rename from src/plugins/vis_type_vega/public/vega_fn.ts rename to src/plugins/vis_type_vega/public/expressions/vega_fn.ts index abe2d3665ed3..f5ab178cbd74 100644 --- a/src/plugins/vis_type_vega/public/vega_fn.ts +++ b/src/plugins/vis_type_vega/public/expressions/vega_fn.ts @@ -35,13 +35,13 @@ import { ExpressionFunctionDefinition, OpenSearchDashboardsContext, Render, -} from '../../expressions/public'; -import { VegaVisualizationDependencies } from './plugin'; -import { createVegaRequestHandler } from './vega_request_handler'; -import { VegaInspectorAdapters } from './vega_inspector/index'; -import { TimeRange, Query } from '../../data/public'; -import { VisRenderValue } from '../../visualizations/public'; -import { VegaParser } from './data_model/vega_parser'; +} from '../../../expressions/public'; +import { VegaVisualizationDependencies } from '../plugin'; +import { createVegaRequestHandler } from '../vega_request_handler'; +import { VegaInspectorAdapters } from '../vega_inspector'; +import { TimeRange, Query } from '../../../data/public'; +import { VisRenderValue } from '../../../visualizations/public'; +import { VegaParser } from '../data_model/vega_parser'; type Input = OpenSearchDashboardsContext | null; type Output = Promise<Render<RenderValue>>; @@ -52,6 +52,14 @@ interface Arguments { export type VisParams = Required<Arguments>; +export type VegaExpressionFunctionDefinition = ExpressionFunctionDefinition< + 'vega', + Input, + Arguments, + Output, + ExecutionContext<unknown, VegaInspectorAdapters> +>; + interface RenderValue extends VisRenderValue { visData: VegaParser; visType: 'vega'; @@ -60,13 +68,7 @@ interface RenderValue extends VisRenderValue { export const createVegaFn = ( dependencies: VegaVisualizationDependencies -): ExpressionFunctionDefinition< - 'vega', - Input, - Arguments, - Output, - ExecutionContext<unknown, VegaInspectorAdapters> -> => ({ +): VegaExpressionFunctionDefinition => ({ name: 'vega', type: 'render', inputTypes: ['opensearch_dashboards_context', 'null'], diff --git a/src/plugins/vis_type_vega/public/index.ts b/src/plugins/vis_type_vega/public/index.ts index c41add63d681..ed0c794837dd 100644 --- a/src/plugins/vis_type_vega/public/index.ts +++ b/src/plugins/vis_type_vega/public/index.ts @@ -35,3 +35,6 @@ import { VegaPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext<ConfigSchema>) { return new Plugin(initializerContext); } + +export { VegaExpressionFunctionDefinition } from './expressions/vega_fn'; +export { LineVegaSpecExpressionFunctionDefinition } from './expressions/line_vega_spec_fn'; diff --git a/src/plugins/vis_type_vega/public/plugin.ts b/src/plugins/vis_type_vega/public/plugin.ts index 64ab81eedfd2..9751a73ccf91 100644 --- a/src/plugins/vis_type_vega/public/plugin.ts +++ b/src/plugins/vis_type_vega/public/plugin.ts @@ -43,13 +43,14 @@ import { setInjectedMetadata, } from './services'; -import { createVegaFn } from './vega_fn'; +import { createVegaFn } from './expressions/vega_fn'; import { createVegaTypeDefinition } from './vega_type'; import { IServiceSettings } from '../../maps_legacy/public'; import './index.scss'; import { ConfigSchema } from '../config'; import { getVegaInspectorView } from './vega_inspector'; +import { createLineVegaSpecFn } from './expressions/line_vega_spec_fn'; /** @internal */ export interface VegaVisualizationDependencies { @@ -104,6 +105,7 @@ export class VegaPlugin implements Plugin<Promise<void>, void> { inspector.registerView(getVegaInspectorView({ uiSettings: core.uiSettings })); expressions.registerFunction(() => createVegaFn(visualizationDependencies)); + expressions.registerFunction(() => createLineVegaSpecFn(visualizationDependencies)); visualizations.createBaseVisualization(createVegaTypeDefinition(visualizationDependencies)); } diff --git a/src/plugins/vis_type_vega/public/vega_request_handler.ts b/src/plugins/vis_type_vega/public/vega_request_handler.ts index 7878e97d5889..7711f5d0f497 100644 --- a/src/plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/plugins/vis_type_vega/public/vega_request_handler.ts @@ -34,7 +34,7 @@ import { SearchAPI } from './data_model/search_api'; import { TimeCache } from './data_model/time_cache'; import { VegaVisualizationDependencies } from './plugin'; -import { VisParams } from './vega_fn'; +import { VisParams } from './expressions/vega_fn'; import { getData, getInjectedMetadata } from './services'; import { VegaInspectorAdapters } from './vega_inspector'; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 1286495af901..32dcfe2c026a 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -437,7 +437,11 @@ export class VegaBaseView { * Set global debug variable to simplify vega debugging in console. Show info message first time */ setDebugValues(view, spec, vlspec) { - this._parser.searchAPI.inspectorAdapters?.vega.bindInspectValues({ + // The vega inspector can now be null when rendering line charts using vega for the overlay visualization feature. + // This is because the inspectors get added at bootstrap to the different chart types and visualize embeddable + // thinks the line chart is vislib line chart and uses that inspector adapter and has no way of knowing it's + // actually a vega-lite chart and needs to use the vega inspector adapter without hacky code. + this._parser.searchAPI.inspectorAdapters?.vega?.bindInspectValues({ view, spec: vlspec || spec, }); diff --git a/src/plugins/vis_type_vislib/opensearch_dashboards.json b/src/plugins/vis_type_vislib/opensearch_dashboards.json index b0d9627bb10f..0cef84e463f6 100644 --- a/src/plugins/vis_type_vislib/opensearch_dashboards.json +++ b/src/plugins/vis_type_vislib/opensearch_dashboards.json @@ -3,7 +3,7 @@ "version": "opensearchDashboards", "server": true, "ui": true, - "requiredPlugins": ["charts", "data", "expressions", "visualizations", "opensearchDashboardsLegacy"], + "requiredPlugins": ["charts", "data", "expressions", "visualizations", "opensearchDashboardsLegacy", "visTypeVega"], "optionalPlugins": ["visTypeXy"], "requiredBundles": ["opensearchDashboardsUtils", "visDefaultEditor"] } diff --git a/src/plugins/vis_type_vislib/public/line.ts b/src/plugins/vis_type_vislib/public/line.ts index 06a5be4fe414..04ae732e2903 100644 --- a/src/plugins/vis_type_vislib/public/line.ts +++ b/src/plugins/vis_type_vislib/public/line.ts @@ -50,6 +50,7 @@ import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; import { Rotates } from '../../charts/public'; import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; +import { toExpressionAst } from './line_to_expression'; export const createLineVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({ name: 'line', @@ -58,6 +59,7 @@ export const createLineVisTypeDefinition = (deps: VisTypeVislibDependencies) => description: i18n.translate('visTypeVislib.line.lineDescription', { defaultMessage: 'Emphasize trends', }), + toExpressionAst, visualization: createVislibVisController(deps), getSupportedTriggers: () => { return [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush]; diff --git a/src/plugins/vis_type_vislib/public/line_to_expression.ts b/src/plugins/vis_type_vislib/public/line_to_expression.ts new file mode 100644 index 000000000000..26797d8348a0 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/line_to_expression.ts @@ -0,0 +1,60 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { buildVislibDimensions, Vis } from '../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { OpenSearchaggsExpressionFunctionDefinition } from '../../data/common/search/expressions'; +import { + VegaExpressionFunctionDefinition, + LineVegaSpecExpressionFunctionDefinition, +} from '../../vis_type_vega/public'; + +export const toExpressionAst = async (vis: Vis, params: any) => { + // Construct the existing expr fns that are ran for vislib line chart, up until the render fn. + // That way we get the exact same data table of results as if it was a vislib chart. + const opensearchaggsFn = buildExpressionFunction<OpenSearchaggsExpressionFunctionDefinition>( + 'opensearchaggs', + { + index: vis.data.indexPattern!.id!, + metricsAtAllLevels: vis.isHierarchical(), + partialRows: vis.params.showPartialRows || false, + aggConfigs: JSON.stringify(vis.data.aggs!.aggs), + includeFormatHints: false, + } + ); + + // Checks if there are vislayers to overlay. If not, default to the vislib implementation. + if (params.visLayers == null || Object.keys(params.visLayers).length === 0) { + // This wont work but is needed so then it will default to the original vis lib renderer + const dimensions = await buildVislibDimensions(vis, params); + const visConfig = { ...vis.params, dimensions }; + const vislib = buildExpressionFunction<any>('vislib', { + type: 'line', + visConfig: JSON.stringify(visConfig), + }); + const ast = buildExpression([opensearchaggsFn, vislib]); + return ast.toAst(); + } else { + const dimensions = await buildVislibDimensions(vis, params); + // adding the new expr fn here that takes the datatable and converts to a vega spec + const vegaSpecFn = buildExpressionFunction<LineVegaSpecExpressionFunctionDefinition>( + 'line_vega_spec', + { + visLayers: JSON.stringify([]), + visParams: JSON.stringify(vis.params), + dimensions: JSON.stringify(dimensions), + } + ); + const vegaSpecFnExpressionBuilder = buildExpression([vegaSpecFn]); + + // build vega expr fn. use nested expression fn syntax to first construct the + // spec via 'line_vega_spec' fn, then set as the arg for the final 'vega' fn + const vegaFn = buildExpressionFunction<VegaExpressionFunctionDefinition>('vega', { + spec: vegaSpecFnExpressionBuilder, + }); + const ast = buildExpression([opensearchaggsFn, vegaFn]); + return ast.toAst(); + } +}; diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 46d3b3dd7d03..ffc24a81b381 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -43,7 +43,11 @@ export { Vis } from './vis'; export { TypesService } from './vis_types/types_service'; export { VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER } from './embeddable'; export { VisualizationContainer, VisualizationNoResults } from './components'; -export { getSchemas as getVisSchemas, buildVislibDimensions } from './legacy/build_pipeline'; +export { + getSchemas as getVisSchemas, + buildVislibDimensions, + VislibDimensions, +} from './legacy/build_pipeline'; /** @public types */ export { VisualizationsSetup, VisualizationsStart }; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index 1cbb3bc38879..a2b95afe6756 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -331,7 +331,20 @@ const buildVisConfig: BuildVisConfigFunction = { }, }; -export const buildVislibDimensions = async (vis: any, params: BuildPipelineParams) => { +export interface VislibDimensions { + x: any; + y: SchemaConfig[]; + z?: any[]; + width?: any[]; + series?: any[]; + splitRow?: any[]; + splitColumn?: any[]; +} + +export const buildVislibDimensions = async ( + vis: any, + params: BuildPipelineParams +): Promise<VislibDimensions> => { const schemas = getSchemas(vis, { timeRange: params.timeRange, timefilter: params.timefilter, From 236f49f9e64d98594ade8546ea2d5299b93a96bf Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 8 Mar 2023 16:49:07 -0800 Subject: [PATCH 07/37] Add logic to collect `VisLayer`s in line chart render flow (#3131) * Collect VisLayers in VisualizeEmbeddable render flow Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Refactor types; address comments Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Simplify fetchVisLayers() and pipeline helper fns Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Update src/plugins/visualizations/public/embeddable/visualize_embeddable.ts Signed-off-by: Josh Romero <rmerqg@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> Signed-off-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> --- src/plugins/vis_augmenter/common/types.ts | 60 -------- .../vis_augmenter/public/expressions/index.ts | 2 +- .../expressions/{vis_layers.ts => types.ts} | 18 ++- src/plugins/vis_augmenter/public/index.ts | 6 +- .../public/saved_augment_vis/index.ts | 1 + .../saved_augment_vis.test.ts | 29 ++-- .../saved_augment_vis/saved_augment_vis.ts | 2 +- .../saved_augment_vis_references.test.ts | 7 +- .../public/saved_augment_vis/types.ts | 20 +++ .../saved_augment_vis/utils/test_helpers.ts | 11 +- .../{common => public}/types.test.ts | 0 src/plugins/vis_augmenter/public/types.ts | 51 +++++-- .../{common => public/utils}/index.ts | 2 +- .../vis_augmenter/public/utils/utils.test.ts | 132 ++++++++++++++++++ .../vis_augmenter/public/utils/utils.ts | 60 ++++++++ .../visualizations/opensearch_dashboards.json | 2 +- .../create_vis_embeddable_from_object.ts | 4 + .../public/embeddable/visualize_embeddable.ts | 60 +++++++- .../public/legacy/build_pipeline.ts | 3 + src/plugins/visualizations/public/services.ts | 5 + 20 files changed, 375 insertions(+), 100 deletions(-) delete mode 100644 src/plugins/vis_augmenter/common/types.ts rename src/plugins/vis_augmenter/public/expressions/{vis_layers.ts => types.ts} (56%) create mode 100644 src/plugins/vis_augmenter/public/saved_augment_vis/types.ts rename src/plugins/vis_augmenter/{common => public}/types.test.ts (100%) rename src/plugins/vis_augmenter/{common => public/utils}/index.ts (77%) create mode 100644 src/plugins/vis_augmenter/public/utils/utils.test.ts create mode 100644 src/plugins/vis_augmenter/public/utils/utils.ts diff --git a/src/plugins/vis_augmenter/common/types.ts b/src/plugins/vis_augmenter/common/types.ts deleted file mode 100644 index ceb8b1973ba7..000000000000 --- a/src/plugins/vis_augmenter/common/types.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ExpressionFunctionDefinition } from '../../expressions'; - -export enum VisLayerTypes { - PointInTimeEvents = 'PointInTimeEvents', -} - -export type PluginResourceType = string; - -export interface PluginResource { - type: PluginResourceType; - id: string; - name: string; - urlPath: string; -} - -export interface VisLayer { - type: keyof typeof VisLayerTypes; - originPlugin: string; - pluginResource: PluginResource; -} - -export type VisLayers = VisLayer[]; - -export interface EventMetadata { - pluginResourceId: string; - tooltip?: string; -} - -export interface PointInTimeEvent { - timestamp: number; - metadata: EventMetadata; -} - -export interface PointInTimeEventsVisLayer extends VisLayer { - events: PointInTimeEvent[]; -} - -export const isPointInTimeEventsVisLayer = (obj: any) => { - return obj?.type === VisLayerTypes.PointInTimeEvents; -}; - -export const isValidVisLayer = (obj: any) => { - return obj?.type in VisLayerTypes; -}; - -export interface VisLayerResponseValue { - visLayers: object; -} - -export type VisLayerFunctionDefinition = ExpressionFunctionDefinition< - string, - VisLayerResponseValue, - any, - Promise<VisLayerResponseValue> ->; diff --git a/src/plugins/vis_augmenter/public/expressions/index.ts b/src/plugins/vis_augmenter/public/expressions/index.ts index f7bcfbd083fe..9f269633f307 100644 --- a/src/plugins/vis_augmenter/public/expressions/index.ts +++ b/src/plugins/vis_augmenter/public/expressions/index.ts @@ -3,4 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -export * from './vis_layers'; +export * from './types'; diff --git a/src/plugins/vis_augmenter/public/expressions/vis_layers.ts b/src/plugins/vis_augmenter/public/expressions/types.ts similarity index 56% rename from src/plugins/vis_augmenter/public/expressions/vis_layers.ts rename to src/plugins/vis_augmenter/public/expressions/types.ts index f99ead0407eb..b907e570e108 100644 --- a/src/plugins/vis_augmenter/public/expressions/vis_layers.ts +++ b/src/plugins/vis_augmenter/public/expressions/types.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ExpressionTypeDefinition } from '../../../expressions'; -import { VisLayers } from '../../common'; +import { ExpressionTypeDefinition, ExpressionFunctionDefinition } from '../../../expressions'; +import { VisLayers, VisLayerTypes } from '../'; const name = 'vis_layers'; @@ -31,3 +31,17 @@ export const visLayers: ExpressionTypeDefinition<typeof name, ExprVisLayers> = { }, }, }; + +export type VisLayerFunctionDefinition = ExpressionFunctionDefinition< + string, + ExprVisLayers, + any, + Promise<ExprVisLayers> +>; + +export interface VisLayerExpressionFn { + type: keyof typeof VisLayerTypes; + name: string; + // plugin expression fns can freely set custom arguments + args: { [key: string]: any }; +} diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index cf736bf6d3e6..e374cbe8d2cf 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -18,4 +18,8 @@ export { SavedObjectOpenSearchDashboardsServicesWithAugmentVis, } from './saved_augment_vis'; -export { ISavedAugmentVis, VisLayerExpressionFn, AugmentVisSavedObject } from './types'; +export { VisLayer, VisLayers, VisLayerTypes } from './types'; + +export * from './expressions'; +export * from './utils'; +export * from './saved_augment_vis'; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/index.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/index.ts index 5ac3a159132e..ce1680204953 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/index.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/index.ts @@ -5,3 +5,4 @@ export * from './saved_augment_vis'; export * from './utils'; +export * from './types'; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts index bc44a0ed6dc2..51360f72c331 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts @@ -3,8 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { VisLayerExpressionFn } from '../types'; -import { VisLayerTypes } from '../../common'; +import { VisLayerExpressionFn, VisLayerTypes } from '../types'; import { createSavedAugmentVisLoader, SavedObjectOpenSearchDashboardsServicesWithAugmentVis, @@ -19,15 +18,23 @@ describe('SavedObjectLoaderAugmentVis', () => { testArg: 'test-value', }, } as VisLayerExpressionFn; - const validObj1 = generateAugmentVisSavedObject('valid-obj-id-1', fn); - const validObj2 = generateAugmentVisSavedObject('valid-obj-id-2', fn); - const invalidFnTypeObj = generateAugmentVisSavedObject('invalid-fn-obj-id-1', { - ...fn, - // @ts-ignore - type: 'invalid-type', - }); - // @ts-ignore - const missingFnObj = generateAugmentVisSavedObject('missing-fn-obj-id-1', {}); + const validObj1 = generateAugmentVisSavedObject('valid-obj-id-1', fn, 'test-vis-id'); + const validObj2 = generateAugmentVisSavedObject('valid-obj-id-2', fn, 'test-vis-id'); + const invalidFnTypeObj = generateAugmentVisSavedObject( + 'invalid-fn-obj-id-1', + { + ...fn, + // @ts-ignore + type: 'invalid-type', + }, + 'test-vis-id' + ); + + const missingFnObj = generateAugmentVisSavedObject( + 'missing-fn-obj-id-1', + {} as VisLayerExpressionFn, + 'test-vis-id' + ); it('find returns single saved obj', async () => { const loader = createSavedAugmentVisLoader({ diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts index 82e6e24a7e3e..910ef0b9ea75 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts @@ -9,7 +9,7 @@ import { SavedObjectOpenSearchDashboardsServices, } from '../../../saved_objects/public'; import { createSavedAugmentVisClass } from './_saved_augment_vis'; -import { VisLayerTypes } from '../../common'; +import { VisLayerTypes } from '../types'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SavedObjectOpenSearchDashboardsServicesWithAugmentVis diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts index 4a19b84dc40e..1b5a0ad6cd98 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts @@ -3,9 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { extractReferences, injectReferences } from './saved_augment_vis_references'; +import { + extractReferences, + injectReferences, + VIS_REFERENCE_NAME, +} from './saved_augment_vis_references'; import { AugmentVisSavedObject } from '../types'; -import { VIS_REFERENCE_NAME } from './saved_augment_vis_references'; describe('extractReferences()', () => { test('extracts nothing if visId is null', () => { diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts new file mode 100644 index 000000000000..dee349cb9001 --- /dev/null +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts @@ -0,0 +1,20 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObject } from '../../../saved_objects/public'; +import { VisLayerExpressionFn } from '../expressions'; + +export interface ISavedAugmentVis { + id?: string; + title: string; + description?: string; + pluginResourceId: string; + visName?: string; + visId?: string; + visLayerExpressionFn: VisLayerExpressionFn; + version?: number; +} + +export interface AugmentVisSavedObject extends SavedObject, ISavedAugmentVis {} diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts index c237fa7551c3..9bbfb8940b27 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts @@ -8,16 +8,21 @@ import { VisLayerExpressionFn, ISavedAugmentVis } from '../../types'; import { VIS_REFERENCE_NAME } from '../saved_augment_vis_references'; const pluginResourceId = 'test-plugin-resource-id'; -const visId = 'test-vis-id'; +const title = 'test-title'; const version = 1; -export const generateAugmentVisSavedObject = (idArg: string, exprFnArg: VisLayerExpressionFn) => { +export const generateAugmentVisSavedObject = ( + idArg: string, + exprFnArg: VisLayerExpressionFn, + visIdArg: string +) => { return { id: idArg, + title, pluginResourceId, visLayerExpressionFn: exprFnArg, VIS_REFERENCE_NAME, - visId, + visId: visIdArg, version, } as ISavedAugmentVis; }; diff --git a/src/plugins/vis_augmenter/common/types.test.ts b/src/plugins/vis_augmenter/public/types.test.ts similarity index 100% rename from src/plugins/vis_augmenter/common/types.test.ts rename to src/plugins/vis_augmenter/public/types.test.ts diff --git a/src/plugins/vis_augmenter/public/types.ts b/src/plugins/vis_augmenter/public/types.ts index 5ddd191cace5..920a8db02843 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -3,24 +3,45 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { SavedObject } from '../../saved_objects/public'; -import { VisLayerTypes } from '../common'; +export enum VisLayerTypes { + PointInTimeEvents = 'PointInTimeEvents', +} -export interface ISavedAugmentVis { - id?: string; - description?: string; - pluginResourceId: string; - visName?: string; - visId?: string; - visLayerExpressionFn: VisLayerExpressionFn; - version?: number; +export type PluginResourceType = string; + +export interface PluginResource { + type: PluginResourceType; + id: string; + name: string; + urlPath: string; } -export interface VisLayerExpressionFn { +export interface VisLayer { type: keyof typeof VisLayerTypes; - name: string; - // plugin expression fns can freely set custom arguments - args: { [key: string]: any }; + originPlugin: string; + pluginResource: PluginResource; +} + +export type VisLayers = VisLayer[]; + +export interface EventMetadata { + pluginResourceId: string; + tooltip?: string; +} + +export interface PointInTimeEvent { + timestamp: number; + metadata: EventMetadata; } -export interface AugmentVisSavedObject extends SavedObject, ISavedAugmentVis {} +export interface PointInTimeEventsVisLayer extends VisLayer { + events: PointInTimeEvent[]; +} + +export const isPointInTimeEventsVisLayer = (obj: any) => { + return obj?.type === VisLayerTypes.PointInTimeEvents; +}; + +export const isValidVisLayer = (obj: any) => { + return obj?.type in VisLayerTypes; +}; diff --git a/src/plugins/vis_augmenter/common/index.ts b/src/plugins/vis_augmenter/public/utils/index.ts similarity index 77% rename from src/plugins/vis_augmenter/common/index.ts rename to src/plugins/vis_augmenter/public/utils/index.ts index 9f269633f307..079132ce99d2 100644 --- a/src/plugins/vis_augmenter/common/index.ts +++ b/src/plugins/vis_augmenter/public/utils/index.ts @@ -3,4 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -export * from './types'; +export * from './utils'; diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts new file mode 100644 index 000000000000..3c770b1db8ba --- /dev/null +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -0,0 +1,132 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Vis } from '../../../visualizations/public'; +import { + buildPipelineFromAugmentVisSavedObjs, + getAugmentVisSavedObjs, + isEligibleForVisLayers, +} from './utils'; +import { VisLayerTypes, ISavedAugmentVis, VisLayerExpressionFn } from '../types'; +import { + createSavedAugmentVisLoader, + SavedObjectOpenSearchDashboardsServicesWithAugmentVis, + getMockAugmentVisSavedObjectClient, + generateAugmentVisSavedObject, +} from '../saved_augment_vis'; + +describe('utils', () => { + // TODO: redo / update this test suite when eligibility is finalized. + // Tracked in https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 + describe('isEligibleForVisLayers', () => { + it('vis is ineligible with invalid type', async () => { + const vis = ({ + params: { + type: 'not-line', + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is eligible with valid type', async () => { + const vis = ({ + params: { + type: 'line', + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(true); + }); + }); + + describe('getAugmentVisSavedObjs', () => { + const fn = { + type: VisLayerTypes.PointInTimeEvents, + name: 'test-fn', + args: { + testArg: 'test-value', + }, + } as VisLayerExpressionFn; + const visId1 = 'test-vis-id-1'; + const visId2 = 'test-vis-id-2'; + const visId3 = 'test-vis-id-3'; + const obj1 = generateAugmentVisSavedObject('valid-obj-id-1', fn, visId1); + const obj2 = generateAugmentVisSavedObject('valid-obj-id-2', fn, visId1); + const obj3 = generateAugmentVisSavedObject('valid-obj-id-3', fn, visId2); + + it('returns no matching saved objs with filtering', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([obj1, obj2, obj3]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + expect((await getAugmentVisSavedObjs(visId3, loader)).length).toEqual(0); + }); + it('returns no matching saved objs when client returns empty list', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + expect((await getAugmentVisSavedObjs(visId1, loader)).length).toEqual(0); + }); + it('returns one matching saved obj', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([obj1]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + expect((await getAugmentVisSavedObjs(visId1, loader)).length).toEqual(1); + }); + it('returns multiple matching saved objs without filtering', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([obj1, obj2]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + expect((await getAugmentVisSavedObjs(visId1, loader)).length).toEqual(2); + }); + it('returns multiple matching saved objs with filtering', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([obj1, obj2, obj3]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + expect((await getAugmentVisSavedObjs(visId1, loader)).length).toEqual(2); + }); + }); + + describe('buildPipelineFromAugmentVisSavedObjs', () => { + const obj1 = { + title: 'obj1', + pluginResourceId: 'obj-1-resource-id', + visLayerExpressionFn: { + type: VisLayerTypes.PointInTimeEvents, + name: 'fn-1', + args: { + arg1: 'value-1', + }, + }, + } as ISavedAugmentVis; + const obj2 = { + title: 'obj2', + pluginResourceId: 'obj-2-resource-id', + visLayerExpressionFn: { + type: VisLayerTypes.PointInTimeEvents, + name: 'fn-2', + args: { + arg2: 'value-2', + }, + }, + } as ISavedAugmentVis; + it('catches error with empty array', async () => { + try { + buildPipelineFromAugmentVisSavedObjs([]); + } catch (e: any) { + expect( + e.message.includes( + 'Expression function from augment-vis saved objects could not be generated' + ) + ); + } + }); + it('builds with one saved obj', async () => { + const str = buildPipelineFromAugmentVisSavedObjs([obj1]); + expect(str).toEqual('fn-1 arg1="value-1"'); + }); + it('builds with multiple saved objs', async () => { + const str = buildPipelineFromAugmentVisSavedObjs([obj1, obj2]); + expect(str).toEqual(`fn-1 arg1="value-1"\n| fn-2 arg2="value-2"`); + }); + }); +}); diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts new file mode 100644 index 000000000000..c0d357d517e9 --- /dev/null +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -0,0 +1,60 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get } from 'lodash'; +import { Vis } from '../../../../plugins/visualizations/public'; +import { + formatExpression, + buildExpressionFunction, + buildExpression, + ExpressionAstFunctionBuilder, +} from '../../../../plugins/expressions/public'; +import { ISavedAugmentVis, SavedAugmentVisLoader, VisLayerFunctionDefinition } from '../'; + +// TODO: provide a deeper eligibility check. +// Tracked in https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 +export const isEligibleForVisLayers = (vis: Vis): boolean => { + return vis.params.type === 'line'; +}; + +/** + * Using a SavedAugmentVisLoader, fetch all saved objects that are of 'augment-vis' type + * and filter out to return the ones associated to the particular vis via + * matching vis ID. + */ +export const getAugmentVisSavedObjs = async ( + visId: string | undefined, + loader: SavedAugmentVisLoader | undefined +): Promise<ISavedAugmentVis[]> => { + try { + const resp = await loader?.findAll(); + const allSavedObjects = (get(resp, 'hits', []) as any[]) as ISavedAugmentVis[]; + return allSavedObjects.filter((hit: ISavedAugmentVis) => hit.visId === visId); + } catch (e) { + return [] as ISavedAugmentVis[]; + } +}; + +/** + * Given an array of augment-vis saved objects that contain expression function details, + * construct a pipeline that will execute each of these expression functions. + * Note that the order does not matter; each expression function should be taking + * in the current output and appending its results to it, such that the end result + * contains the results from each expression function that was ran. + */ +export const buildPipelineFromAugmentVisSavedObjs = (objs: ISavedAugmentVis[]): string => { + try { + const visLayerExpressionFns = objs.map((obj: ISavedAugmentVis) => + buildExpressionFunction<VisLayerFunctionDefinition>( + obj.visLayerExpressionFn.name, + obj.visLayerExpressionFn.args + ) + ) as Array<ExpressionAstFunctionBuilder<VisLayerFunctionDefinition>>; + const ast = buildExpression(visLayerExpressionFns).toAst(); + return formatExpression(ast); + } catch (e) { + throw new Error('Expression function from augment-vis saved objects could not be generated'); + } +}; diff --git a/src/plugins/visualizations/opensearch_dashboards.json b/src/plugins/visualizations/opensearch_dashboards.json index 6223ffce3808..b7c5e4ab9b4e 100644 --- a/src/plugins/visualizations/opensearch_dashboards.json +++ b/src/plugins/visualizations/opensearch_dashboards.json @@ -5,5 +5,5 @@ "ui": true, "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "inspector", "dashboard"], "optionalPlugins": ["usageCollection"], - "requiredBundles": ["opensearchDashboardsUtils", "discover", "savedObjects"] + "requiredBundles": ["opensearchDashboardsUtils", "discover", "savedObjects", "visAugmenter"] } diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts index 03666a199dca..e09f789f9a68 100644 --- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts +++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts @@ -40,6 +40,7 @@ import { IContainer, ErrorEmbeddable } from '../../../embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { getSavedVisualizationsLoader, + getSavedAugmentVisLoader, getUISettings, getHttp, getTimeFilter, @@ -88,6 +89,8 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe const editable = getCapabilities().visualize.save as boolean; + const savedAugmentVisLoader = getSavedAugmentVisLoader(); + return new VisualizeEmbeddable( getTimeFilter(), { @@ -101,6 +104,7 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe input, attributeService, savedVisualizationsLoader, + savedAugmentVisLoader, parent ); } catch (e) { diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index f3808951d519..c0d43cd521da 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -28,7 +28,7 @@ * under the License. */ -import _, { get } from 'lodash'; +import _, { get, isEmpty } from 'lodash'; import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; import { i18n } from '@osd/i18n'; @@ -64,6 +64,14 @@ import { TriggerId } from '../../../ui_actions/public'; import { SavedObjectAttributes } from '../../../../core/types'; import { AttributeService } from '../../../dashboard/public'; import { SavedVisualizationsLoader } from '../saved_visualizations'; +import { + SavedAugmentVisLoader, + ExprVisLayers, + VisLayers, + isEligibleForVisLayers, + getAugmentVisSavedObjs, + buildPipelineFromAugmentVisSavedObjs, +} from '../../../vis_augmenter/public'; import { VisSavedObject } from '../types'; const getKeys = <T extends {}>(o: T): Array<keyof T> => Object.keys(o) as Array<keyof T>; @@ -127,6 +135,7 @@ export class VisualizeEmbeddable VisualizeByReferenceInput >; private savedVisualizationsLoader?: SavedVisualizationsLoader; + private savedAugmentVisLoader?: SavedAugmentVisLoader; constructor( timefilter: TimefilterContract, @@ -138,6 +147,7 @@ export class VisualizeEmbeddable VisualizeByReferenceInput >, savedVisualizationsLoader?: SavedVisualizationsLoader, + savedAugmentVisLoader?: SavedAugmentVisLoader, parent?: IContainer ) { super( @@ -160,7 +170,7 @@ export class VisualizeEmbeddable this.vis.uiState.on('reload', this.reload); this.attributeService = attributeService; this.savedVisualizationsLoader = savedVisualizationsLoader; - + this.savedAugmentVisLoader = savedAugmentVisLoader; this.autoRefreshFetchSubscription = timefilter .getAutoRefreshFetch$() .subscribe(this.updateHandler.bind(this)); @@ -393,10 +403,14 @@ export class VisualizeEmbeddable } this.abortController = new AbortController(); const abortController = this.abortController; + + const visLayers = await this.fetchVisLayers(expressionParams, abortController); + this.expression = await buildPipeline(this.vis, { timefilter: this.timefilter, timeRange: this.timeRange, abortSignal: this.abortController!.signal, + visLayers, }); if (this.handler && !abortController.signal.aborted) { @@ -465,4 +479,46 @@ export class VisualizeEmbeddable { showSaveModal: true, saveModalTitle } ); }; + + /** + * Collects any VisLayers from plugin expressions functions + * by fetching all AugmentVisSavedObjects that match the vis + * saved object ID. + * + * TODO: final eligibility will be defined as part of a separate effort. + * Right now we have a placeholder function isEligibleForVisLayers() which + * is used below. For more details, see + * https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 + */ + fetchVisLayers = async ( + expressionParams: IExpressionLoaderParams, + abortController: AbortController + ): Promise<VisLayers> => { + const augmentVisSavedObjs = await getAugmentVisSavedObjs( + this.vis.id, + this.savedAugmentVisLoader + ); + if ( + !isEmpty(augmentVisSavedObjs) && + !abortController.signal.aborted && + isEligibleForVisLayers(this.vis) + ) { + const visLayersPipeline = buildPipelineFromAugmentVisSavedObjs(augmentVisSavedObjs); + // The initial input for the pipeline will just be an empty arr of VisLayers. As plugin + // expression functions are ran, they will incrementally append their generated VisLayers to it. + const visLayersPipelineInput = { + type: 'vis_layers', + layers: [] as VisLayers, + }; + // We cannot use this.handler in this case, since it does not support the run() cmd + // we need here. So, we consume the expressions service to run this instead. + const exprVisLayers = (await getExpressions().run( + visLayersPipeline, + visLayersPipelineInput, + expressionParams as Record<string, unknown> + )) as ExprVisLayers; + return exprVisLayers.layers; + } + return [] as VisLayers; + }; } diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index a2b95afe6756..d751e088c99d 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -33,6 +33,8 @@ import moment from 'moment'; import { formatExpression, SerializedFieldFormat } from '../../../../plugins/expressions/public'; import { IAggConfig, search, TimefilterContract } from '../../../../plugins/data/public'; import { Vis, VisParams } from '../types'; +import { VisLayers } from '../../../../plugins/vis_augmenter/public'; + const { isDateHistogramBucketAggConfig } = search.aggs; interface SchemaConfigParams { @@ -85,6 +87,7 @@ export interface BuildPipelineParams { timefilter: TimefilterContract; timeRange?: any; abortSignal?: AbortSignal; + visLayers?: VisLayers; } const vislibCharts: string[] = [ diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index e3f3ba56f6b1..1ad0b37d76a4 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -52,6 +52,7 @@ import { UiActionsStart } from '../../ui_actions/public'; import { SavedVisualizationsLoader } from './saved_visualizations'; import { SavedObjectLoader } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; +import { SavedAugmentVisLoader } from '../../vis_augmenter/public'; export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); @@ -106,3 +107,7 @@ export const [getChrome, setChrome] = createGetterSetter<ChromeStart>('Chrome'); export const [getSavedSearchLoader, setSavedSearchLoader] = createGetterSetter<SavedObjectLoader>( 'savedSearchLoader' ); + +export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< + SavedAugmentVisLoader +>('SavedAugmentVisLoader'); From 6f9f2c7be127d5707ef3f54c70dd8f7b33e180b7 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 22 Mar 2023 13:32:01 -0700 Subject: [PATCH 08/37] Add error types to VisLayers (#3646) --- src/plugins/vis_augmenter/public/index.ts | 2 +- src/plugins/vis_augmenter/public/types.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index e374cbe8d2cf..79ccbc183bd0 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -18,7 +18,7 @@ export { SavedObjectOpenSearchDashboardsServicesWithAugmentVis, } from './saved_augment_vis'; -export { VisLayer, VisLayers, VisLayerTypes } from './types'; +export { VisLayer, VisLayers, VisLayerTypes, VisLayerErrorTypes, VisLayerError } from './types'; export * from './expressions'; export * from './utils'; diff --git a/src/plugins/vis_augmenter/public/types.ts b/src/plugins/vis_augmenter/public/types.ts index 920a8db02843..a45ce4f20df3 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -7,6 +7,16 @@ export enum VisLayerTypes { PointInTimeEvents = 'PointInTimeEvents', } +export enum VisLayerErrorTypes { + PERMISSIONS_FAILURE = 'PERMISSIONS_FAILURE', + FETCH_FAILURE = 'FETCH_FAILURE', +} + +export interface VisLayerError { + type: keyof typeof VisLayerErrorTypes; + message?: string; +} + export type PluginResourceType = string; export interface PluginResource { @@ -20,6 +30,7 @@ export interface VisLayer { type: keyof typeof VisLayerTypes; originPlugin: string; pluginResource: PluginResource; + error?: VisLayerError; } export type VisLayers = VisLayer[]; From 79b9f506d997462d4668619c5f4857b0d83e0e36 Mon Sep 17 00:00:00 2001 From: Amit Galitzky <amgalitz@amazon.com> Date: Wed, 22 Mar 2023 13:41:55 -0700 Subject: [PATCH 09/37] Setting savedAugmentVisLoader in Visualizations plugin (#3616) --- NOTICE.txt | 1 + src/plugins/visualizations/public/plugin.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/NOTICE.txt b/NOTICE.txt index 170b48daa5b9..34da5d0a6f44 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -245,3 +245,4 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 682e678ed584..48f1d8306345 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -60,6 +60,7 @@ import { setOverlays, setSavedSearchLoader, setEmbeddable, + setSavedAugmentVisLoader, } from './services'; import { VISUALIZE_EMBEDDABLE_TYPE, @@ -92,6 +93,7 @@ import { } from './saved_visualizations/_saved_vis'; import { createSavedSearchesLoader } from '../../discover/public'; import { DashboardStart } from '../../dashboard/public'; +import { createSavedAugmentVisLoader } from '../../vis_augmenter/public'; /** * Interface for this plugin's returned setup/start contracts. @@ -193,6 +195,14 @@ export class VisualizationsPlugin setAggs(data.search.aggs); setOverlays(core.overlays); setChrome(core.chrome); + const savedAugmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: core.savedObjects.client, + indexPatterns: data.indexPatterns, + search: data.search, + chrome: core.chrome, + overlays: core.overlays, + }); + setSavedAugmentVisLoader(savedAugmentVisLoader); const savedVisualizationsLoader = createSavedVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, From 8a2cc697c786446ef1b5573b6490ad105a81fa09 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 30 Mar 2023 14:14:27 -0700 Subject: [PATCH 10/37] Dynamically update vega spec to show `VisLayer`s (#3145) * Dynamically update vega spec to show VisLayers Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Clean up changes from rebase Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Create and set augment vis loader in visualization start() Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Fix test; add placeholder scheme Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Fix null x axis NPE Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * remove adding timestamps that are out of bounds; add test cases for it Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Remove unnecessary if block Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Export PluginResource in vis_augmenter/public Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Add comments Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Fix conflict from rebasing Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Make vega parser flags more fine-grained Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Clean up few tests; simplify helper fn Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Remove visTypeVega from visAugmenter reqd plugins; other minor cleanup Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- src/plugins/vis_augmenter/README.md | 2 +- src/plugins/vis_augmenter/public/constants.ts | 13 + src/plugins/vis_augmenter/public/index.ts | 19 +- .../vis_augmenter/public/test_constants.ts | 561 ++++++++++++++++++ .../vis_augmenter/public/vega/README.md | 1 + .../vis_augmenter/public/vega/helpers.test.ts | 444 ++++++++++++++ .../vis_augmenter/public/vega/helpers.ts | 342 +++++++++++ .../vis_augmenter/public/vega/index.ts | 6 + .../vis_type_vega/opensearch_dashboards.json | 9 +- .../vega_visualization.test.js.snap | 6 +- .../public/data_model/vega_parser.ts | 21 +- .../public/expressions/__mocks__/helpers.ts | 21 + .../public/expressions/__mocks__/index.ts | 6 + .../__snapshots__/helpers.test.js.snap | 7 + .../public/expressions/helpers.test.js | 183 ++++++ .../public/expressions/helpers.ts | 238 ++++++++ .../vis_type_vega/public/expressions/index.ts | 7 + .../expressions/line_vega_spec_fn.test.js | 200 ------- .../public/expressions/line_vega_spec_fn.ts | 272 +-------- src/plugins/vis_type_vega/public/index.ts | 3 +- .../public/vega_view/vega_base_view.js | 16 + .../public/vega_view/vega_view.js | 4 + .../public/line_to_expression.ts | 2 +- src/plugins/visualizations/public/plugin.ts | 14 +- 24 files changed, 1933 insertions(+), 464 deletions(-) create mode 100644 src/plugins/vis_augmenter/public/constants.ts create mode 100644 src/plugins/vis_augmenter/public/test_constants.ts create mode 100644 src/plugins/vis_augmenter/public/vega/README.md create mode 100644 src/plugins/vis_augmenter/public/vega/helpers.test.ts create mode 100644 src/plugins/vis_augmenter/public/vega/helpers.ts create mode 100644 src/plugins/vis_augmenter/public/vega/index.ts create mode 100644 src/plugins/vis_type_vega/public/expressions/__mocks__/helpers.ts create mode 100644 src/plugins/vis_type_vega/public/expressions/__mocks__/index.ts create mode 100644 src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap create mode 100644 src/plugins/vis_type_vega/public/expressions/helpers.test.js create mode 100644 src/plugins/vis_type_vega/public/expressions/helpers.ts create mode 100644 src/plugins/vis_type_vega/public/expressions/index.ts delete mode 100644 src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js diff --git a/src/plugins/vis_augmenter/README.md b/src/plugins/vis_augmenter/README.md index fb9d4fbdcaae..4ebe2e4b1d4b 100644 --- a/src/plugins/vis_augmenter/README.md +++ b/src/plugins/vis_augmenter/README.md @@ -1 +1 @@ -Contains interfaces and type definitions used for allowing external plugins to augment Visualizations. Registers the relevant saved object types and expression functions. +Contains interfaces, type definitions, helper functions, and services used for allowing external plugins to augment Visualizations. Registers the relevant saved object types and expression functions. diff --git a/src/plugins/vis_augmenter/public/constants.ts b/src/plugins/vis_augmenter/public/constants.ts new file mode 100644 index 000000000000..99b75a8de7ea --- /dev/null +++ b/src/plugins/vis_augmenter/public/constants.ts @@ -0,0 +1,13 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const VIS_LAYER_COLUMN_TYPE = 'vis_layer'; +// TODO: replace with a value imported from OUI +export const EVENT_COLOR = 'red'; +export const HOVER_PARAM = 'HOVER'; +export const EVENT_MARK_SIZE = 100; +export const EVENT_MARK_SIZE_ENLARGED = 140; +export const EVENT_MARK_SHAPE = 'triangle-up'; +export const EVENT_TIMELINE_HEIGHT = 25; diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index 79ccbc183bd0..cab6e6fef286 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -12,14 +12,19 @@ export function plugin(initializerContext: PluginInitializerContext) { export { VisAugmenterSetup, VisAugmenterStart }; export { - createSavedAugmentVisLoader, - createAugmentVisSavedObject, - SavedAugmentVisLoader, - SavedObjectOpenSearchDashboardsServicesWithAugmentVis, -} from './saved_augment_vis'; - -export { VisLayer, VisLayers, VisLayerTypes, VisLayerErrorTypes, VisLayerError } from './types'; + VisLayer, + VisLayers, + VisLayerTypes, + VisLayerErrorTypes, + VisLayerError, + PluginResource, + PointInTimeEvent, + PointInTimeEventsVisLayer, + isPointInTimeEventsVisLayer, +} from './types'; export * from './expressions'; export * from './utils'; +export * from './constants'; +export * from './vega'; export * from './saved_augment_vis'; diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts new file mode 100644 index 000000000000..bc77e8ba803f --- /dev/null +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -0,0 +1,561 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { OpenSearchDashboardsDatatable } from '../../expressions/public'; +import { VIS_LAYER_COLUMN_TYPE, VisLayerTypes, HOVER_PARAM } from './'; + +const TEST_X_AXIS_ID = 'test-x-axis-id'; +const TEST_VALUE_AXIS_ID = 'test-value-axis-id'; +const TEST_X_AXIS_TITLE = 'time'; +const TEST_VALUE_AXIS_TITLE = 'avg value'; +const TEST_PLUGIN = 'test-plugin'; +const TEST_PLUGIN_RESOURCE_TYPE = 'test-resource-type'; +const TEST_PLUGIN_RESOURCE_ID = 'test-resource-id'; +const TEST_PLUGIN_RESOURCE_ID_2 = 'test-resource-id-2'; +const TEST_PLUGIN_RESOURCE_NAME = 'test-resource-name'; +const TEST_PLUGIN_RESOURCE_NAME_2 = 'test-resource-name-2'; +const TEST_PLUGIN_RESOURCE_PATH = `${TEST_PLUGIN}/${TEST_PLUGIN_RESOURCE_TYPE}/${TEST_PLUGIN_RESOURCE_ID}`; +const TEST_PLUGIN_RESOURCE_PATH_2 = `${TEST_PLUGIN}/${TEST_PLUGIN_RESOURCE_TYPE}/${TEST_PLUGIN_RESOURCE_ID_2}`; + +const TEST_VALUES_SINGLE_ROW_NO_VIS_LAYERS = [{ [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }]; + +const TEST_VALUES_SINGLE_ROW_SINGLE_VIS_LAYER = [ + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 3 }, +]; + +const TEST_VALUES_ONLY_VIS_LAYERS = [ + { [TEST_X_AXIS_ID]: 0 }, + { [TEST_X_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 2 }, + { [TEST_X_AXIS_ID]: 10 }, + { [TEST_X_AXIS_ID]: 15 }, + { [TEST_X_AXIS_ID]: 20 }, + { [TEST_X_AXIS_ID]: 25 }, + { [TEST_X_AXIS_ID]: 30 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_RESOURCE_ID]: 1 }, + { [TEST_X_AXIS_ID]: 40 }, + { [TEST_X_AXIS_ID]: 45 }, + { [TEST_X_AXIS_ID]: 50 }, +]; + +const TEST_VALUES_NO_VIS_LAYERS = [ + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10 }, + { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4 }, + { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 25 }, + { [TEST_X_AXIS_ID]: 30 }, + { [TEST_X_AXIS_ID]: 35 }, + { [TEST_X_AXIS_ID]: 40 }, + { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5 }, +]; + +const TEST_VALUES_SINGLE_VIS_LAYER = [ + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_RESOURCE_ID]: 2 }, + { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4 }, + { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 25 }, + { [TEST_X_AXIS_ID]: 30 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_RESOURCE_ID]: 1 }, + { [TEST_X_AXIS_ID]: 40 }, + { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5 }, +]; + +const TEST_VALUES_SINGLE_VIS_LAYER_ON_BOUNDS = [ + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 2 }, + { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10 }, + { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4 }, + { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 25 }, + { [TEST_X_AXIS_ID]: 30 }, + { [TEST_X_AXIS_ID]: 35 }, + { [TEST_X_AXIS_ID]: 40 }, + { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 1 }, +]; + +const TEST_VALUES_MULTIPLE_VIS_LAYERS = [ + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, + { + [TEST_X_AXIS_ID]: 5, + [TEST_VALUE_AXIS_ID]: 10, + [TEST_PLUGIN_RESOURCE_ID]: 2, + [TEST_PLUGIN_RESOURCE_ID_2]: 1, + }, + { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4, [TEST_PLUGIN_RESOURCE_ID_2]: 1 }, + { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 25 }, + { [TEST_X_AXIS_ID]: 30 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_RESOURCE_ID]: 1 }, + { [TEST_X_AXIS_ID]: 40 }, + { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID_2]: 2 }, +]; + +export const TEST_COLUMNS_NO_VIS_LAYERS = [ + { + id: TEST_X_AXIS_ID, + name: TEST_X_AXIS_TITLE, + }, + { + id: TEST_VALUE_AXIS_ID, + name: TEST_VALUE_AXIS_TITLE, + }, +]; + +export const TEST_COLUMNS_SINGLE_VIS_LAYER = [ + ...TEST_COLUMNS_NO_VIS_LAYERS, + { + id: TEST_PLUGIN_RESOURCE_ID, + name: TEST_PLUGIN_RESOURCE_NAME, + meta: { + type: VIS_LAYER_COLUMN_TYPE, + }, + }, +]; + +export const TEST_COLUMNS_MULTIPLE_VIS_LAYERS = [ + ...TEST_COLUMNS_SINGLE_VIS_LAYER, + { + id: TEST_PLUGIN_RESOURCE_ID_2, + name: TEST_PLUGIN_RESOURCE_NAME_2, + meta: { + type: VIS_LAYER_COLUMN_TYPE, + }, + }, +]; + +export const TEST_DATATABLE_SINGLE_ROW_NO_VIS_LAYERS = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_NO_VIS_LAYERS, + rows: TEST_VALUES_SINGLE_ROW_NO_VIS_LAYERS, +} as OpenSearchDashboardsDatatable; + +export const TEST_DATATABLE_SINGLE_ROW_SINGLE_VIS_LAYER = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_SINGLE_VIS_LAYER, + rows: TEST_VALUES_SINGLE_ROW_SINGLE_VIS_LAYER, +} as OpenSearchDashboardsDatatable; + +export const TEST_DATATABLE_ONLY_VIS_LAYERS = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_SINGLE_VIS_LAYER, + rows: TEST_VALUES_ONLY_VIS_LAYERS, +} as OpenSearchDashboardsDatatable; + +export const TEST_DATATABLE_NO_VIS_LAYERS = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_NO_VIS_LAYERS, + rows: TEST_VALUES_NO_VIS_LAYERS, +} as OpenSearchDashboardsDatatable; + +export const TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY = { + ...TEST_DATATABLE_NO_VIS_LAYERS, + columns: TEST_COLUMNS_SINGLE_VIS_LAYER, +} as OpenSearchDashboardsDatatable; + +export const TEST_DATATABLE_SINGLE_VIS_LAYER = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_SINGLE_VIS_LAYER, + rows: TEST_VALUES_SINGLE_VIS_LAYER, +} as OpenSearchDashboardsDatatable; + +export const TEST_DATATABLE_SINGLE_VIS_LAYER_ON_BOUNDS = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_SINGLE_VIS_LAYER, + rows: TEST_VALUES_SINGLE_VIS_LAYER_ON_BOUNDS, +} as OpenSearchDashboardsDatatable; + +export const TEST_DATATABLE_MULTIPLE_VIS_LAYERS = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_MULTIPLE_VIS_LAYERS, + rows: TEST_VALUES_MULTIPLE_VIS_LAYERS, +} as OpenSearchDashboardsDatatable; + +const TEST_BASE_CONFIG = { + view: { stroke: null }, + concat: { spacing: 0 }, + legend: { orient: 'right' }, + kibana: { hideWarnings: true }, +}; + +const TEST_BASE_VIS_LAYER = { + mark: { type: 'line', interpolate: 'linear', strokeWidth: 2, point: true }, + encoding: { + x: { + axis: { title: TEST_X_AXIS_TITLE, grid: false }, + field: TEST_X_AXIS_ID, + type: 'temporal', + }, + y: { + axis: { + title: TEST_VALUE_AXIS_TITLE, + grid: '', + orient: 'left', + labels: true, + labelAngle: 0, + }, + field: TEST_VALUE_AXIS_ID, + type: 'quantitative', + }, + tooltip: [ + { field: TEST_X_AXIS_ID, type: 'temporal', title: TEST_VALUE_AXIS_TITLE }, + { field: TEST_VALUE_AXIS_ID, type: 'quantitative', title: TEST_VALUE_AXIS_TITLE }, + ], + color: { datum: TEST_VALUE_AXIS_TITLE }, + }, +}; + +export const TEST_SPEC_NO_VIS_LAYERS = { + $schema: 'https://vega.github.io/schema/vega-lite/v5.json', + data: { + values: TEST_VALUES_NO_VIS_LAYERS, + }, + config: TEST_BASE_CONFIG, + layer: [TEST_BASE_VIS_LAYER], +}; + +export const TEST_SPEC_SINGLE_VIS_LAYER = { + ...TEST_SPEC_NO_VIS_LAYERS, + data: { + ...TEST_SPEC_NO_VIS_LAYERS.data, + values: TEST_VALUES_SINGLE_VIS_LAYER, + }, +}; + +export const TEST_SPEC_MULTIPLE_VIS_LAYERS = { + ...TEST_SPEC_NO_VIS_LAYERS, + data: { + ...TEST_SPEC_NO_VIS_LAYERS.data, + values: TEST_VALUES_MULTIPLE_VIS_LAYERS, + }, +}; + +export const TEST_DIMENSIONS = { + x: { + params: { + interval: 5, + bounds: { + min: 0, + max: 50, + }, + }, + label: TEST_X_AXIS_TITLE, + }, +}; + +export const TEST_DIMENSIONS_SINGLE_ROW = { + x: { + params: { + interval: 5, + bounds: { + min: 0, + max: 0, + }, + }, + label: TEST_X_AXIS_TITLE, + }, +}; + +export const TEST_DIMENSIONS_INVALID_BOUNDS = { + x: { + params: { + interval: 5, + bounds: { + min: 50, + max: 0, + }, + }, + label: TEST_X_AXIS_TITLE, + }, +}; + +export const TEST_VIS_LAYERS_SINGLE = [ + { + originPlugin: TEST_PLUGIN, + type: VisLayerTypes.PointInTimeEvents, + pluginResource: { + type: TEST_PLUGIN_RESOURCE_TYPE, + id: TEST_PLUGIN_RESOURCE_ID, + name: TEST_PLUGIN_RESOURCE_NAME, + urlPath: TEST_PLUGIN_RESOURCE_PATH, + }, + events: [ + { + timestamp: 4, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + { + timestamp: 6, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + { + timestamp: 35, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + ], + }, +]; + +export const TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS = [ + { + originPlugin: TEST_PLUGIN, + type: VisLayerTypes.PointInTimeEvents, + pluginResource: { + type: TEST_PLUGIN_RESOURCE_TYPE, + id: TEST_PLUGIN_RESOURCE_ID, + name: TEST_PLUGIN_RESOURCE_NAME, + urlPath: TEST_PLUGIN_RESOURCE_PATH, + }, + events: [ + { + timestamp: -5, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + { + timestamp: -100, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + { + timestamp: 75, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + ], + }, +]; + +export const TEST_VIS_LAYERS_SINGLE_ON_BOUNDS = [ + { + originPlugin: TEST_PLUGIN, + type: VisLayerTypes.PointInTimeEvents, + pluginResource: { + type: TEST_PLUGIN_RESOURCE_TYPE, + id: TEST_PLUGIN_RESOURCE_ID, + name: TEST_PLUGIN_RESOURCE_NAME, + urlPath: TEST_PLUGIN_RESOURCE_PATH, + }, + events: [ + { + timestamp: 0, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + { + timestamp: 2, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + { + timestamp: 55, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID, + }, + }, + ], + }, +]; + +export const TEST_VIS_LAYERS_MULTIPLE = [ + ...TEST_VIS_LAYERS_SINGLE, + { + originPlugin: TEST_PLUGIN, + type: VisLayerTypes.PointInTimeEvents, + pluginResource: { + type: TEST_PLUGIN_RESOURCE_TYPE, + id: TEST_PLUGIN_RESOURCE_ID_2, + name: TEST_PLUGIN_RESOURCE_NAME_2, + urlPath: TEST_PLUGIN_RESOURCE_PATH_2, + }, + events: [ + { + timestamp: 5, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID_2, + }, + }, + { + timestamp: 15, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID_2, + }, + }, + { + timestamp: 49, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID_2, + }, + }, + { + timestamp: 50, + metadata: { + pluginResourceId: TEST_PLUGIN_RESOURCE_ID_2, + }, + }, + ], + }, +]; + +const TEST_RULE_LAYER_SINGLE_VIS_LAYER = { + mark: { type: 'rule', color: 'red', opacity: 1 }, + transform: [{ filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0` }], + encoding: { + x: { field: TEST_X_AXIS_ID, type: 'temporal' }, + opacity: { value: 0, condition: { empty: false, param: HOVER_PARAM, value: 1 } }, + }, +}; + +const TEST_RULE_LAYER_MULTIPLE_VIS_LAYERS = { + ...TEST_RULE_LAYER_SINGLE_VIS_LAYER, + transform: [ + { + filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0 || datum['${TEST_PLUGIN_RESOURCE_ID_2}'] > 0`, + }, + ], +}; + +const TEST_EVENTS_LAYER_SINGLE_VIS_LAYER = { + height: 25, + mark: { + type: 'point', + shape: 'triangle-up', + color: 'red', + filled: true, + opacity: 1, + }, + transform: [{ filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0` }], + params: [{ name: HOVER_PARAM, select: { type: 'point', on: 'mouseover' } }], + encoding: { + x: { + axis: { + title: TEST_X_AXIS_TITLE, + grid: false, + ticks: true, + orient: 'bottom', + domain: true, + }, + field: TEST_X_AXIS_ID, + type: 'temporal', + scale: { + domain: [ + { + year: 2022, + month: 'December', + date: 1, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + }, + { + year: 2023, + month: 'March', + date: 2, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + }, + ], + }, + }, + size: { condition: { empty: false, param: HOVER_PARAM, value: 140 }, value: 100 }, + }, +}; + +const TEST_EVENTS_LAYER_MULTIPLE_VIS_LAYERS = { + ...TEST_EVENTS_LAYER_SINGLE_VIS_LAYER, + transform: [ + { + filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0 || datum['${TEST_PLUGIN_RESOURCE_ID_2}'] > 0`, + }, + ], +}; + +export const TEST_RESULT_SPEC_SINGLE_VIS_LAYER = { + $schema: 'https://vega.github.io/schema/vega-lite/v5.json', + data: { + values: TEST_VALUES_SINGLE_VIS_LAYER, + }, + config: TEST_BASE_CONFIG, + vconcat: [ + { + layer: [ + { + ...TEST_BASE_VIS_LAYER, + encoding: { + ...TEST_BASE_VIS_LAYER.encoding, + x: { + ...TEST_BASE_VIS_LAYER.encoding.x, + axis: { + title: null, + grid: false, + labels: false, + }, + }, + }, + }, + TEST_RULE_LAYER_SINGLE_VIS_LAYER, + ], + }, + TEST_EVENTS_LAYER_SINGLE_VIS_LAYER, + ], +}; + +export const TEST_RESULT_SPEC_SINGLE_VIS_LAYER_EMPTY = { + ...TEST_RESULT_SPEC_SINGLE_VIS_LAYER, + data: { + values: TEST_VALUES_NO_VIS_LAYERS, + }, +}; + +export const TEST_RESULT_SPEC_MULTIPLE_VIS_LAYERS = { + $schema: 'https://vega.github.io/schema/vega-lite/v5.json', + data: { + values: TEST_VALUES_MULTIPLE_VIS_LAYERS, + }, + config: TEST_BASE_CONFIG, + vconcat: [ + { + layer: [ + { + ...TEST_BASE_VIS_LAYER, + encoding: { + ...TEST_BASE_VIS_LAYER.encoding, + x: { + ...TEST_BASE_VIS_LAYER.encoding.x, + axis: { + title: null, + grid: false, + labels: false, + }, + }, + }, + }, + TEST_RULE_LAYER_MULTIPLE_VIS_LAYERS, + ], + }, + TEST_EVENTS_LAYER_MULTIPLE_VIS_LAYERS, + ], +}; diff --git a/src/plugins/vis_augmenter/public/vega/README.md b/src/plugins/vis_augmenter/public/vega/README.md new file mode 100644 index 000000000000..fef45af1777a --- /dev/null +++ b/src/plugins/vis_augmenter/public/vega/README.md @@ -0,0 +1 @@ +Contains the helper functions that are optionally used when rendering vega charts that are eligible for rendering with VisLayers. diff --git a/src/plugins/vis_augmenter/public/vega/helpers.test.ts b/src/plugins/vis_augmenter/public/vega/helpers.test.ts new file mode 100644 index 000000000000..3f1fa58a4fc3 --- /dev/null +++ b/src/plugins/vis_augmenter/public/vega/helpers.test.ts @@ -0,0 +1,444 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { cloneDeep } from 'lodash'; +import { + OpenSearchDashboardsDatatable, + OpenSearchDashboardsDatatableColumn, +} from '../../../expressions/public'; +import { + enableVisLayersInSpecConfig, + isVisLayerColumn, + generateVisLayerFilterString, + addMissingRowsToTableBounds, + addPointInTimeEventsLayersToTable, + addPointInTimeEventsLayersToSpec, +} from './helpers'; +import { VIS_LAYER_COLUMN_TYPE, VisLayerTypes, PointInTimeEventsVisLayer, VisLayer } from '../'; +import { + TEST_DATATABLE_MULTIPLE_VIS_LAYERS, + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DATATABLE_ONLY_VIS_LAYERS, + TEST_DATATABLE_SINGLE_ROW_NO_VIS_LAYERS, + TEST_DATATABLE_SINGLE_ROW_SINGLE_VIS_LAYER, + TEST_DATATABLE_SINGLE_VIS_LAYER, + TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY, + TEST_DATATABLE_SINGLE_VIS_LAYER_ON_BOUNDS, + TEST_DIMENSIONS, + TEST_DIMENSIONS_INVALID_BOUNDS, + TEST_DIMENSIONS_SINGLE_ROW, + TEST_RESULT_SPEC_MULTIPLE_VIS_LAYERS, + TEST_RESULT_SPEC_SINGLE_VIS_LAYER, + TEST_RESULT_SPEC_SINGLE_VIS_LAYER_EMPTY, + TEST_SPEC_MULTIPLE_VIS_LAYERS, + TEST_SPEC_NO_VIS_LAYERS, + TEST_SPEC_SINGLE_VIS_LAYER, + TEST_VIS_LAYERS_MULTIPLE, + TEST_VIS_LAYERS_SINGLE, + TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS, + TEST_VIS_LAYERS_SINGLE_ON_BOUNDS, +} from '../test_constants'; + +describe('helpers', function () { + describe('enableVisLayersInSpecConfig()', function () { + const pointInTimeEventsVisLayer = { + type: VisLayerTypes.PointInTimeEvents, + originPlugin: 'test-plugin', + pluginResource: { + type: 'test-resource-type', + id: 'test-resource-id', + name: 'test-resource-name', + urlPath: 'test-resource-url-path', + }, + events: [ + { + timestamp: 1234, + metadata: { + pluginResourceId: 'test-resource-id', + }, + }, + ], + } as PointInTimeEventsVisLayer; + const invalidVisLayer = ({ + type: 'something-invalid', + originPlugin: 'test-plugin', + pluginResource: { + type: 'test-resource-type', + id: 'test-resource-id', + name: 'test-resource-name', + urlPath: 'test-resource-url-path', + }, + } as unknown) as VisLayer; + + it('updates config with just a valid Vislayer', function () { + const baseConfig = { + kibana: { + hideWarnings: true, + }, + }; + const updatedConfig = enableVisLayersInSpecConfig({ config: baseConfig }, [ + pointInTimeEventsVisLayer, + ]); + const expectedMap = new Map<VisLayerTypes, boolean>([ + [VisLayerTypes.PointInTimeEvents, true], + ]); + // @ts-ignore + baseConfig.kibana.visibleVisLayers = expectedMap; + expect(updatedConfig).toStrictEqual(baseConfig); + }); + it('updates config with a valid and invalid VisLayer', function () { + const baseConfig = { + kibana: { + hideWarnings: true, + }, + }; + const updatedConfig = enableVisLayersInSpecConfig({ config: baseConfig }, [ + pointInTimeEventsVisLayer, + invalidVisLayer, + ]); + const expectedMap = new Map<VisLayerTypes, boolean>([ + [VisLayerTypes.PointInTimeEvents, true], + ]); + // @ts-ignore + baseConfig.kibana.visibleVisLayers = expectedMap; + expect(updatedConfig).toStrictEqual(baseConfig); + }); + it('does not update config if no valid VisLayer', function () { + const baseConfig = { + kibana: { + hideWarnings: true, + }, + }; + const updatedConfig = enableVisLayersInSpecConfig({ config: baseConfig }, [invalidVisLayer]); + // @ts-ignore + baseConfig.kibana.visibleVisLayers = new Map<VisLayerTypes, boolean>(); + expect(updatedConfig).toStrictEqual(baseConfig); + }); + it('does not update config if empty VisLayer list', function () { + const baseConfig = { + kibana: { + hideWarnings: true, + }, + }; + const updatedConfig = enableVisLayersInSpecConfig({ config: baseConfig }, []); + // @ts-ignore + baseConfig.kibana.visibleVisLayers = new Map<VisLayerTypes, boolean>(); + expect(updatedConfig).toStrictEqual(baseConfig); + }); + }); + + describe('isVisLayerColumn()', function () { + it('return false for column with invalid type', function () { + const column = { + id: 'test-id', + name: 'test-name', + meta: { + type: 'invalid-type', + }, + } as OpenSearchDashboardsDatatableColumn; + expect(isVisLayerColumn(column)).toBe(false); + }); + it('return false for column with no meta field', function () { + const column = { + id: 'test-id', + name: 'test-name', + } as OpenSearchDashboardsDatatableColumn; + expect(isVisLayerColumn(column)).toBe(false); + }); + it('return true for column with valid type', function () { + const column = { + id: 'test-id', + name: 'test-name', + meta: { + type: VIS_LAYER_COLUMN_TYPE, + }, + } as OpenSearchDashboardsDatatableColumn; + expect(isVisLayerColumn(column)).toBe(true); + }); + }); + + describe('generateVisLayerFilterString()', function () { + it('empty array returns false', function () { + const visLayerColumnIds = [] as string[]; + const filterString = 'false'; + expect(generateVisLayerFilterString(visLayerColumnIds)).toStrictEqual(filterString); + }); + it('array with one value returns correct filter string', function () { + const visLayerColumnIds = ['test-id-1']; + const filterString = `datum['test-id-1'] > 0`; + expect(generateVisLayerFilterString(visLayerColumnIds)).toStrictEqual(filterString); + }); + it('array with multiple values returns correct filter string', function () { + const visLayerColumnIds = ['test-id-1', 'test-id-2']; + const filterString = `datum['test-id-1'] > 0 || datum['test-id-2'] > 0`; + expect(generateVisLayerFilterString(visLayerColumnIds)).toStrictEqual(filterString); + }); + }); + + describe('addMissingRowsToTableBounds()', function () { + const columnId = 'test-id'; + const columnName = 'test-name'; + const allRows = [ + { + [columnId]: 1, + }, + { + [columnId]: 2, + }, + { + [columnId]: 3, + }, + { + [columnId]: 4, + }, + { + [columnId]: 5, + }, + ]; + it('adds single row if start/end times are the same', function () { + const datatable = { + type: 'opensearch_dashboards_datatable', + columns: [ + { + id: columnId, + name: columnName, + }, + ], + rows: [], + } as OpenSearchDashboardsDatatable; + const dimensions = { + x: { + params: { + interval: 1, + bounds: { + min: 1, + max: 1, + }, + }, + label: columnName, + }, + }; + const result = addMissingRowsToTableBounds(datatable, dimensions); + const expectedTable = { + ...datatable, + rows: [allRows[0]], + }; + expect(result).toStrictEqual(expectedTable); + }); + it('adds all rows if there is none to begin with', function () { + const datatable = { + type: 'opensearch_dashboards_datatable', + columns: [ + { + id: columnId, + name: columnName, + }, + ], + rows: [], + } as OpenSearchDashboardsDatatable; + const dimensions = { + x: { + params: { + interval: 1, + bounds: { + min: 1, + max: 5, + }, + }, + label: columnName, + }, + }; + const result = addMissingRowsToTableBounds(datatable, dimensions); + const expectedTable = { + ...datatable, + rows: allRows, + }; + expect(result).toStrictEqual(expectedTable); + }); + it('fill rows at beginning', function () { + const missingRows = cloneDeep(allRows); + missingRows.shift(); + missingRows.shift(); + const datatable = { + type: 'opensearch_dashboards_datatable', + columns: [ + { + id: columnId, + name: columnName, + }, + ], + rows: missingRows, + } as OpenSearchDashboardsDatatable; + const dimensions = { + x: { + params: { + interval: 1, + bounds: { + min: 1, + max: 5, + }, + }, + label: columnName, + }, + }; + const result = addMissingRowsToTableBounds(datatable, dimensions); + const expectedTable = { + ...datatable, + rows: allRows, + }; + expect(result).toStrictEqual(expectedTable); + }); + it('fill rows at end', function () { + const missingRows = cloneDeep(allRows); + missingRows.pop(); + missingRows.pop(); + const datatable = { + type: 'opensearch_dashboards_datatable', + columns: [ + { + id: columnId, + name: columnName, + }, + ], + rows: missingRows, + } as OpenSearchDashboardsDatatable; + const dimensions = { + x: { + params: { + interval: 1, + bounds: { + min: 1, + max: 5, + }, + }, + label: columnName, + }, + }; + const result = addMissingRowsToTableBounds(datatable, dimensions); + const expectedTable = { + ...datatable, + rows: allRows, + }; + expect(result).toStrictEqual(expectedTable); + }); + }); + + describe('addPointInTimeEventsLayersToTable()', function () { + it('single vis layer is added correctly', function () { + expect( + addPointInTimeEventsLayersToTable( + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DIMENSIONS, + TEST_VIS_LAYERS_SINGLE + ) + ).toStrictEqual(TEST_DATATABLE_SINGLE_VIS_LAYER); + }); + it('multiple vis layers are added correctly', function () { + expect( + addPointInTimeEventsLayersToTable( + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DIMENSIONS, + TEST_VIS_LAYERS_MULTIPLE + ) + ).toStrictEqual(TEST_DATATABLE_MULTIPLE_VIS_LAYERS); + }); + it('invalid bounds adds no row data', function () { + expect( + addPointInTimeEventsLayersToTable( + { + ...TEST_DATATABLE_NO_VIS_LAYERS, + rows: [], + }, + TEST_DIMENSIONS_INVALID_BOUNDS, + TEST_VIS_LAYERS_SINGLE + ) + ).toStrictEqual({ + ...TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY, + rows: [], + }); + }); + it('vis layers with single row are added correctly', function () { + expect( + addPointInTimeEventsLayersToTable( + TEST_DATATABLE_SINGLE_ROW_NO_VIS_LAYERS, + TEST_DIMENSIONS_SINGLE_ROW, + TEST_VIS_LAYERS_SINGLE + ) + ).toStrictEqual(TEST_DATATABLE_SINGLE_ROW_SINGLE_VIS_LAYER); + }); + it('vis layers with no existing rows/data are added correctly', function () { + expect( + addPointInTimeEventsLayersToTable( + { + ...TEST_DATATABLE_NO_VIS_LAYERS, + rows: [], + }, + TEST_DIMENSIONS, + TEST_VIS_LAYERS_SINGLE + ) + ).toStrictEqual(TEST_DATATABLE_ONLY_VIS_LAYERS); + }); + it('vis layer with out-of-bounds timestamps are not added', function () { + expect( + addPointInTimeEventsLayersToTable( + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DIMENSIONS, + TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS + ) + ).toStrictEqual(TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY); + }); + it('vis layer with events on edge of bounds are added', function () { + expect( + addPointInTimeEventsLayersToTable( + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DIMENSIONS, + TEST_VIS_LAYERS_SINGLE_ON_BOUNDS + ) + ).toStrictEqual(TEST_DATATABLE_SINGLE_VIS_LAYER_ON_BOUNDS); + }); + }); + + describe('addPointInTimeEventsLayersToSpec()', function () { + it('spec with single time series produces correct spec', function () { + const expectedSpec = TEST_RESULT_SPEC_SINGLE_VIS_LAYER; + const returnSpec = addPointInTimeEventsLayersToSpec( + TEST_DATATABLE_SINGLE_VIS_LAYER, + TEST_DIMENSIONS, + TEST_SPEC_SINGLE_VIS_LAYER + ); + // deleting the scale fields since this contain generated + // fields based on timezone env it is run in + delete expectedSpec.vconcat[1].encoding.x.scale; + delete returnSpec.vconcat[1].encoding.x.scale; + expect(returnSpec).toEqual(expectedSpec); + }); + it('spec with multiple time series produces correct spec', function () { + const expectedSpec = TEST_RESULT_SPEC_MULTIPLE_VIS_LAYERS; + const returnSpec = addPointInTimeEventsLayersToSpec( + TEST_DATATABLE_MULTIPLE_VIS_LAYERS, + TEST_DIMENSIONS, + TEST_SPEC_MULTIPLE_VIS_LAYERS + ); + // deleting the scale fields since this contain generated + // fields based on timezone env it is run in + delete expectedSpec.vconcat[1].encoding.x.scale; + delete returnSpec.vconcat[1].encoding.x.scale; + expect(returnSpec).toEqual(expectedSpec); + }); + it('spec with vis layers with empty data produces correct spec', function () { + const expectedSpec = TEST_RESULT_SPEC_SINGLE_VIS_LAYER_EMPTY; + const returnSpec = addPointInTimeEventsLayersToSpec( + TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY, + TEST_DIMENSIONS, + TEST_SPEC_NO_VIS_LAYERS + ); + // deleting the scale fields since this contain generated + // fields based on timezone env it is run in + delete expectedSpec.vconcat[1].encoding.x.scale; + delete returnSpec.vconcat[1].encoding.x.scale; + expect(returnSpec).toEqual(expectedSpec); + }); + }); +}); diff --git a/src/plugins/vis_augmenter/public/vega/helpers.ts b/src/plugins/vis_augmenter/public/vega/helpers.ts new file mode 100644 index 000000000000..670a9c7590b5 --- /dev/null +++ b/src/plugins/vis_augmenter/public/vega/helpers.ts @@ -0,0 +1,342 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import moment from 'moment'; +import { cloneDeep, isEmpty, get } from 'lodash'; +import { + OpenSearchDashboardsDatatable, + OpenSearchDashboardsDatatableColumn, +} from '../../../expressions/public'; +import { + PointInTimeEvent, + PointInTimeEventsVisLayer, + isPointInTimeEventsVisLayer, + VIS_LAYER_COLUMN_TYPE, + EVENT_COLOR, + EVENT_MARK_SIZE, + EVENT_MARK_SIZE_ENLARGED, + EVENT_MARK_SHAPE, + EVENT_TIMELINE_HEIGHT, + HOVER_PARAM, + VisLayer, + VisLayers, + VisLayerTypes, +} from '../'; + +export const enableVisLayersInSpecConfig = (spec: object, visLayers: VisLayers): {} => { + const config = get(spec, 'config', { kibana: {} }); + const visibleVisLayers = new Map<VisLayerTypes, boolean>(); + + // Currently only support PointInTimeEventsVisLayers. Set the flag to true + // if there are any + const pointInTimeEventsVisLayers = visLayers.filter((visLayer: VisLayer) => + isPointInTimeEventsVisLayer(visLayer) + ) as PointInTimeEventsVisLayer[]; + if (!isEmpty(pointInTimeEventsVisLayers)) { + visibleVisLayers.set(VisLayerTypes.PointInTimeEvents, true); + } + return { + ...config, + kibana: { + ...config.kibana, + visibleVisLayers, + }, + }; +}; + +// Get the first xaxis field as only 1 setup of X Axis will be supported and +// there won't be support for split series and split chart +export const getXAxisId = ( + dimensions: any, + columns: OpenSearchDashboardsDatatableColumn[] +): string => { + return columns.filter((column) => column.name === dimensions.x.label)[0].id; +}; + +export const isVisLayerColumn = (column: OpenSearchDashboardsDatatableColumn): boolean => { + return column.meta?.type === VIS_LAYER_COLUMN_TYPE; +}; + +/** + * For temporal domain ranges, there is a bug when passing timestamps in vega lite + * that is still present in the current libraries we are using when developing in a + * dev env. See https://github.com/vega/vega-lite/issues/6060 for bug details. + * So, we convert to a vega-lite Date Time object and pass that instead. + * See https://vega.github.io/vega-lite/docs/datetime.html for details on Date Time. + */ +const convertToDateTimeObj = (timestamp: number): any => { + const momentObj = moment(timestamp); + return { + year: Number(momentObj.format('YYYY')), + month: momentObj.format('MMMM'), + date: momentObj.date(), + hours: momentObj.hours(), + minutes: momentObj.minutes(), + seconds: momentObj.seconds(), + milliseconds: momentObj.milliseconds(), + }; +}; + +export const generateVisLayerFilterString = (visLayerColumnIds: string[]): string => { + if (!isEmpty(visLayerColumnIds)) { + const filterString = visLayerColumnIds.map( + (visLayerColumnId) => `datum['${visLayerColumnId}'] > 0` + ); + return filterString.join(' || '); + } else { + // if there is no VisLayers to display, then filter out everything by always returning false + return 'false'; + } +}; + +/** + * By default, the source datatable will not include rows with empty data. + * For handling events that may belong in missing buckets that are not yet + * created, we need to create them. For more details, see description in + * https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3145 + * + * Note that this may add buckets with start/end times out of the chart bounds. + * This is the current default behavior of histogram aggregations with intervals, + * in order for the bucket keys to have "clean" timestamp keys (e.g., 1/1 @ 12AM). + * For more details, see + * https://opensearch.org/docs/latest/opensearch/bucket-agg/#histogram-date_histogram + * + * Also note this is only adding empty buckets at the beginning/end of a table. We are + * not taking into account missing buckets within source datapoints. Because of this + * limitation, it is possible that charted events may not be put into the most precise + * bucket based on their raw event timestamps, if there is missing / sparse source data. + */ +export const addMissingRowsToTableBounds = ( + datatable: OpenSearchDashboardsDatatable, + dimensions: any +): OpenSearchDashboardsDatatable => { + const augmentedTable = cloneDeep(datatable); + const intervalMillis = moment.duration(dimensions.x.params.interval).asMilliseconds(); + const xAxisId = getXAxisId(dimensions, augmentedTable.columns); + const chartStartTime = new Date(dimensions.x.params.bounds.min).valueOf(); + const chartEndTime = new Date(dimensions.x.params.bounds.max).valueOf(); + + if (!isEmpty(augmentedTable.rows)) { + const dataStartTime = augmentedTable.rows[0][xAxisId] as number; + const dataEndTime = augmentedTable.rows[augmentedTable.rows.length - 1][xAxisId] as number; + + let curStartTime = dataStartTime; + while (curStartTime > chartStartTime) { + curStartTime -= intervalMillis; + augmentedTable.rows.unshift({ + [xAxisId]: curStartTime, + }); + } + + let curEndTime = dataEndTime; + while (curEndTime < chartEndTime) { + curEndTime += intervalMillis; + augmentedTable.rows.push({ + [xAxisId]: curEndTime, + }); + } + } else { + // if there's no existing rows, create them all + let curTime = chartStartTime; + while (curTime <= chartEndTime) { + augmentedTable.rows.push({ + [xAxisId]: curTime, + }); + curTime += intervalMillis; + } + } + return augmentedTable; +}; + +/** + * Adding events into the correct x-axis key (the time bucket) + * based on the table. As of now only results from + * PointInTimeEventsVisLayers are supported + */ +export const addPointInTimeEventsLayersToTable = ( + datatable: OpenSearchDashboardsDatatable, + dimensions: any, + visLayers: PointInTimeEventsVisLayer[] +): OpenSearchDashboardsDatatable => { + const augmentedTable = addMissingRowsToTableBounds(datatable, dimensions); + const xAxisId = getXAxisId(dimensions, augmentedTable.columns); + + if (isEmpty(visLayers)) return augmentedTable; + + visLayers.every((visLayer: PointInTimeEventsVisLayer) => { + const visLayerColumnId = `${visLayer.pluginResource.id}`; + const visLayerColumnName = `${visLayer.pluginResource.name}`; + augmentedTable.columns.push({ + id: visLayerColumnId, + name: visLayerColumnName, + meta: { + type: VIS_LAYER_COLUMN_TYPE, + }, + }); + + if (augmentedTable.rows.length === 0) { + return false; + } + + // if only one row / one datapoint, put all events into this bucket + if (augmentedTable.rows.length === 1) { + augmentedTable.rows[0] = { + ...augmentedTable.rows[0], + [visLayerColumnId]: visLayer.events.length, + }; + return false; + } + + // Bin the timestamps to the closest x-axis key, adding + // an entry for this vis layer ID. Sorting the timestamps first + // so that we will only search a particular row value once. + // There could be some optimizations, such as binary search + dynamically + // changing the bounds, but performance benefits would be very minimal + // if any, given the upper bounds limit on n already due to chart constraints. + let rowIndex = 0; + const minVal = augmentedTable.rows[0][xAxisId] as number; + const maxVal = + (augmentedTable.rows[augmentedTable.rows.length - 1][xAxisId] as number) + + moment.duration(dimensions.x.params.interval).asMilliseconds(); + const sortedTimestamps = visLayer.events + .map((event: PointInTimeEvent) => event.timestamp) + .filter((timestamp: number) => timestamp >= minVal && timestamp <= maxVal) + .sort((n1: number, n2: number) => n1 - n2) as number[]; + + sortedTimestamps.forEach((timestamp) => { + while (rowIndex < augmentedTable.rows.length - 1) { + const smallerVal = augmentedTable.rows[rowIndex][xAxisId] as number; + const higherVal = augmentedTable.rows[rowIndex + 1][xAxisId] as number; + let rowIndexToInsert: number; + + // timestamp is on the left bounds of the chart + if (timestamp === smallerVal) { + rowIndexToInsert = rowIndex; + + // timestamp is in between the right 2 buckets. determine which one it is closer to + } else if (timestamp <= higherVal) { + const smallerValDiff = Math.abs(timestamp - smallerVal); + const higherValDiff = Math.abs(timestamp - higherVal); + rowIndexToInsert = smallerValDiff <= higherValDiff ? rowIndex : rowIndex + 1; + } + + // timestamp is on the right bounds of the chart + else if (rowIndex + 1 === augmentedTable.rows.length - 1) { + rowIndexToInsert = rowIndex + 1; + // timestamp is still too small; traverse to next bucket + } else { + rowIndex += 1; + continue; + } + + // inserting the value. increment if the mapping/property already exists + augmentedTable.rows[rowIndexToInsert][visLayerColumnId] = + (get(augmentedTable.rows[rowIndexToInsert], visLayerColumnId, 0) as number) + 1; + break; + } + }); + + return true; + }); + return augmentedTable; +}; + +/** + * Updating the vega lite spec to include layers and marks related to + * PointInTimeEventsVisLayers. It is assumed the datatable has already been + * augmented with columns and row data containing the vis layers. + */ +export const addPointInTimeEventsLayersToSpec = ( + datatable: OpenSearchDashboardsDatatable, + dimensions: any, + spec: object +): object => { + const newSpec = cloneDeep(spec) as any; + + const xAxisId = getXAxisId(dimensions, datatable.columns); + const xAxisTitle = dimensions.x.label.replaceAll('"', ''); + const bucketStartTime = convertToDateTimeObj(datatable.rows[0][xAxisId] as number); + const bucketEndTime = convertToDateTimeObj( + datatable.rows[datatable.rows.length - 1][xAxisId] as number + ); + const visLayerColumns = datatable.columns.filter((column: OpenSearchDashboardsDatatableColumn) => + isVisLayerColumn(column) + ); + const visLayerColumnIds = visLayerColumns.map((column) => column.id); + + // Hide x axes text on existing chart so they are only visible on the event chart + newSpec.layer.forEach((dataSeries: any) => { + if (get(dataSeries, 'encoding.x.axis', null) !== null) { + dataSeries.encoding.x.axis = { + ...dataSeries.encoding.x.axis, + labels: false, + title: null, + }; + } + }); + + // Add a rule to the existing layer for showing lines on the chart if a dot is hovered on + newSpec.layer.push({ + mark: { + type: 'rule', + color: EVENT_COLOR, + opacity: 1, + }, + transform: [{ filter: generateVisLayerFilterString(visLayerColumnIds) }], + encoding: { + x: { + field: xAxisId, + type: 'temporal', + }, + opacity: { + value: 0, + condition: { empty: false, param: HOVER_PARAM, value: 1 }, + }, + }, + }); + + // Nesting layer into a vconcat field so we can append event chart. + newSpec.vconcat = [] as any[]; + newSpec.vconcat.push({ + layer: newSpec.layer, + }); + delete newSpec.layer; + + // Adding the event timeline chart + newSpec.vconcat.push({ + height: EVENT_TIMELINE_HEIGHT, + mark: { + type: 'point', + shape: EVENT_MARK_SHAPE, + color: EVENT_COLOR, + filled: true, + opacity: 1, + }, + transform: [{ filter: generateVisLayerFilterString(visLayerColumnIds) }], + params: [{ name: HOVER_PARAM, select: { type: 'point', on: 'mouseover' } }], + encoding: { + x: { + axis: { + title: xAxisTitle, + grid: false, + ticks: true, + orient: 'bottom', + domain: true, + }, + field: xAxisId, + type: 'temporal', + scale: { + domain: [bucketStartTime, bucketEndTime], + }, + }, + size: { + condition: { empty: false, param: HOVER_PARAM, value: EVENT_MARK_SIZE_ENLARGED }, + value: EVENT_MARK_SIZE, + }, + }, + }); + + return newSpec; +}; diff --git a/src/plugins/vis_augmenter/public/vega/index.ts b/src/plugins/vis_augmenter/public/vega/index.ts new file mode 100644 index 000000000000..0e8ad44d2bd7 --- /dev/null +++ b/src/plugins/vis_augmenter/public/vega/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './helpers'; diff --git a/src/plugins/vis_type_vega/opensearch_dashboards.json b/src/plugins/vis_type_vega/opensearch_dashboards.json index ca4d7020c2fa..17aee4a97232 100644 --- a/src/plugins/vis_type_vega/opensearch_dashboards.json +++ b/src/plugins/vis_type_vega/opensearch_dashboards.json @@ -4,6 +4,11 @@ "server": true, "ui": true, "requiredPlugins": ["data", "visualizations", "mapsLegacy", "expressions", "inspector"], - "optionalPlugins": ["home","usageCollection"], - "requiredBundles": ["opensearchDashboardsUtils", "opensearchDashboardsReact", "visDefaultEditor"] + "optionalPlugins": ["home", "usageCollection"], + "requiredBundles": [ + "opensearchDashboardsUtils", + "opensearchDashboardsReact", + "visDefaultEditor", + "visAugmenter" + ] } diff --git a/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap b/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap index 7ba343a02f21..973d539e4751 100644 --- a/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap +++ b/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap @@ -2,8 +2,8 @@ exports[`VegaVisualizations VegaVisualization - basics should show vega blank rectangle on top of a map (vegamap) 1`] = `"<div class=\\"vgaVis__view leaflet-container leaflet-grab leaflet-touch-drag\\" style=\\"height: 100%; position: relative;\\" tabindex=\\"0\\"><div class=\\"leaflet-pane leaflet-map-pane\\" style=\\"left: 0px; top: 0px;\\"><div class=\\"leaflet-pane leaflet-tile-pane\\"></div><div class=\\"leaflet-pane leaflet-shadow-pane\\"></div><div class=\\"leaflet-pane leaflet-overlay-pane\\"><div class=\\"leaflet-vega-container\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\" style=\\"left: 0px; top: 0px; cursor: default;\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"0\\" height=\\"0\\" viewBox=\\"0 0 0 0\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(0,0)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h0v0h0Z\\"></path><g><g class=\\"mark-rect role-mark\\" role=\\"graphics-symbol\\" aria-roledescription=\\"rect mark container\\"><path d=\\"M0,0h0v0h0Z\\" fill=\\"#0f0\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div></div><div class=\\"leaflet-pane leaflet-marker-pane\\"></div><div class=\\"leaflet-pane leaflet-tooltip-pane\\"></div><div class=\\"leaflet-pane leaflet-popup-pane\\"></div></div><div class=\\"leaflet-control-container\\"><div class=\\"leaflet-top leaflet-left\\"><div class=\\"leaflet-control-zoom leaflet-bar leaflet-control\\"><a class=\\"leaflet-control-zoom-in\\" href=\\"#\\" title=\\"Zoom in\\" role=\\"button\\" aria-label=\\"Zoom in\\">+</a><a class=\\"leaflet-control-zoom-out\\" href=\\"#\\" title=\\"Zoom out\\" role=\\"button\\" aria-label=\\"Zoom out\\">−</a></div></div><div class=\\"leaflet-top leaflet-right\\"></div><div class=\\"leaflet-bottom leaflet-left\\"></div><div class=\\"leaflet-bottom leaflet-right\\"><div class=\\"leaflet-control-attribution leaflet-control\\"></div></div></div></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; -exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"<div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"512\\" height=\\"512\\" viewBox=\\"0 0 512 512\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(0,0)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h512v512h-512Z\\"></path><g><g class=\\"mark-group role-scope\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h0v0h0Z\\"></path><g><g class=\\"mark-area role-mark\\" role=\\"graphics-symbol\\" aria-roledescription=\\"area mark container\\"><path d=\\"M0,512C18.963,512,37.926,512,56.889,512C75.852,512,94.815,512,113.778,512C132.741,512,151.704,512,170.667,512C189.63,512,208.593,512,227.556,512C246.519,512,265.481,512,284.444,512C303.407,512,322.37,512,341.333,512C360.296,512,379.259,512,398.222,512C417.185,512,436.148,512,455.111,512C474.074,512,493.037,512,512,512L512,355.2C493.037,324.8,474.074,294.4,455.111,294.4C436.148,294.4,417.185,457.6,398.222,457.6C379.259,457.6,360.296,233.6,341.333,233.6C322.37,233.6,303.407,435.2,284.444,435.2C265.481,435.2,246.519,345.6,227.556,345.6C208.593,345.6,189.63,451.2,170.667,451.2C151.704,451.2,132.741,252.8,113.778,252.8C94.815,252.8,75.852,346.133,56.889,374.4C37.926,402.667,18.963,412.533,0,422.4Z\\" fill=\\"#54B399\\" fill-opacity=\\"1\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h0v0h0Z\\"></path><g><g class=\\"mark-area role-mark\\" role=\\"graphics-symbol\\" aria-roledescription=\\"area mark container\\"><path d=\\"M0,422.4C18.963,412.533,37.926,402.667,56.889,374.4C75.852,346.133,94.815,252.8,113.778,252.8C132.741,252.8,151.704,451.2,170.667,451.2C189.63,451.2,208.593,345.6,227.556,345.6C246.519,345.6,265.481,435.2,284.444,435.2C303.407,435.2,322.37,233.6,341.333,233.6C360.296,233.6,379.259,457.6,398.222,457.6C417.185,457.6,436.148,294.4,455.111,294.4C474.074,294.4,493.037,324.8,512,355.2L512,307.2C493.037,275.2,474.074,243.2,455.111,243.2C436.148,243.2,417.185,371.2,398.222,371.2C379.259,371.2,360.296,22.4,341.333,22.4C322.37,22.4,303.407,278.4,284.444,278.4C265.481,278.4,246.519,204.8,227.556,192C208.593,179.2,189.63,185.6,170.667,172.8C151.704,160,132.741,83.2,113.778,83.2C94.815,83.2,75.852,83.2,56.889,83.2C37.926,83.2,18.963,164.8,0,246.4Z\\" fill=\\"#6092C0\\" fill-opacity=\\"1\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--err\\"><pre class=\\"vgaVis__messageCode\\">Cannot read property 'get' of undefined</pre></li></ul>"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--warn\\"><pre class=\\"vgaVis__messageCode\\">\\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable</pre></li></ul><div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"0\\" height=\\"0\\" viewBox=\\"0 0 0 0\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(7,7)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0.5,0.5h0v0h0Z\\" fill=\\"transparent\\" stroke=\\"#ddd\\"></path><g><g class=\\"mark-line role-mark marks\\" role=\\"graphics-object\\" aria-roledescription=\\"line mark container\\"><path aria-label=\\"key: Dec 11, 2017; doc_count: 0\\" role=\\"graphics-symbol\\" aria-roledescription=\\"line mark\\" d=\\"M0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0\\" stroke=\\"#54B399\\" stroke-width=\\"2\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--err\\"><pre class=\\"vgaVis__messageCode\\">Cannot read property 'get' of undefined</pre></li></ul>"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--warn\\"><pre class=\\"vgaVis__messageCode\\">\\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable</pre></li></ul><div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"256\\" height=\\"250\\" viewBox=\\"0 0 256 250\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(7,5)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0.5,0.5h242v238h-242Z\\" fill=\\"transparent\\" stroke=\\"#ddd\\"></path><g><g class=\\"mark-line role-mark marks\\" role=\\"graphics-object\\" aria-roledescription=\\"line mark container\\"><path aria-label=\\"key: Dec 11, 2017; doc_count: 0\\" role=\\"graphics-symbol\\" aria-roledescription=\\"line mark\\" d=\\"M0,238L26.889,238L53.778,238L80.667,21.658L107.556,15.851L134.444,16.184L161.333,231.669L188.222,238L215.111,238L242,238\\" stroke=\\"#54B399\\" stroke-width=\\"2\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--err\\"><pre class=\\"vgaVis__messageCode\\">Cannot read property 'get' of undefined</pre></li></ul>"`; diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 64ed96f7e3e7..4de808506f34 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -44,6 +44,7 @@ import { UrlParser } from './url_parser'; import { SearchAPI } from './search_api'; import { TimeCache } from './time_cache'; import { IServiceSettings } from '../../../maps_legacy/public'; +import { VisLayerTypes } from '../../../vis_augmenter/public'; import { Bool, Data, @@ -92,6 +93,7 @@ export class VegaParser { getServiceSettings: () => Promise<IServiceSettings>; filters: Bool; timeCache: TimeCache; + visibleVisLayers: Map<VisLayerTypes, boolean>; constructor( spec: VegaSpec | string, @@ -102,6 +104,7 @@ export class VegaParser { ) { this.spec = spec as VegaSpec; this.hideWarnings = false; + this.visibleVisLayers = new Map<VisLayerTypes, boolean>(); this.error = undefined; this.warnings = []; @@ -158,6 +161,7 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never this._config = this._parseConfig(); this.hideWarnings = !!this._config.hideWarnings; + this.visibleVisLayers = this._config.visibleVisLayers; this.useMap = this._config.type === 'map'; this.renderer = this._config.renderer === 'svg' ? 'svg' : 'canvas'; this.tooltips = this._parseTooltips(); @@ -190,6 +194,17 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never contains: 'padding', }; + // If we are showing PointInTimeEventsVisLayers, it means we are showing a base vis + event vis. + // Because this will be using a vconcat spec, we can autosize the width + // via fit-x. Note the regular 'fit' (to autosize width + height) does not work here. + // See limitations: https://vega.github.io/vega-lite/docs/size.html#limitations + const showPointInTimeEvents = + this.visibleVisLayers.get(VisLayerTypes.PointInTimeEvents) === true; + const showPointInTimeEventsAutosize = { + type: 'fit-x', + contains: 'padding', + }; + let autosize = this.spec.autosize; let useResize = true; @@ -224,6 +239,10 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never autosize = defaultAutosize; } + if (showPointInTimeEvents) { + autosize = showPointInTimeEventsAutosize; + } + if ( useResize && ((this.spec.width && this.spec.width !== 'container') || @@ -243,7 +262,7 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never ); } - if (useResize) { + if (useResize && !showPointInTimeEvents) { this.spec.width = 'container'; this.spec.height = 'container'; } diff --git a/src/plugins/vis_type_vega/public/expressions/__mocks__/helpers.ts b/src/plugins/vis_type_vega/public/expressions/__mocks__/helpers.ts new file mode 100644 index 000000000000..0dd50913d5f9 --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/__mocks__/helpers.ts @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const complexDatatable = + '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-2":1672214400000,"col-1-1":44,"col-2-3":60.9375},{"col-0-2":1672300800000,"col-1-1":150,"col-2-3":82.5},{"col-0-2":1672387200000,"col-1-1":154,"col-2-3":79.5},{"col-0-2":1672473600000,"col-1-1":144,"col-2-3":75.875},{"col-0-2":1672560000000,"col-1-1":133,"col-2-3":259.25},{"col-0-2":1672646400000,"col-1-1":149,"col-2-3":90},{"col-0-2":1672732800000,"col-1-1":152,"col-2-3":79.0625},{"col-0-2":1672819200000,"col-1-1":144,"col-2-3":82.5},{"col-0-2":1672905600000,"col-1-1":166,"col-2-3":85.25},{"col-0-2":1672992000000,"col-1-1":151,"col-2-3":92},{"col-0-2":1673078400000,"col-1-1":143,"col-2-3":90.75},{"col-0-2":1673164800000,"col-1-1":148,"col-2-3":92},{"col-0-2":1673251200000,"col-1-1":146,"col-2-3":83.25},{"col-0-2":1673337600000,"col-1-1":137,"col-2-3":98},{"col-0-2":1673424000000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673510400000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673596800000,"col-1-1":151,"col-2-3":87.4375},{"col-0-2":1673683200000,"col-1-1":157,"col-2-3":63.75},{"col-0-2":1673769600000,"col-1-1":151,"col-2-3":81.5625},{"col-0-2":1673856000000,"col-1-1":152,"col-2-3":100.6875},{"col-0-2":1673942400000,"col-1-1":142,"col-2-3":98},{"col-0-2":1674028800000,"col-1-1":151,"col-2-3":100.8125},{"col-0-2":1674115200000,"col-1-1":163,"col-2-3":83.6875},{"col-0-2":1674201600000,"col-1-1":156,"col-2-3":85.8125},{"col-0-2":1674288000000,"col-1-1":153,"col-2-3":98},{"col-0-2":1674374400000,"col-1-1":162,"col-2-3":75.9375},{"col-0-2":1674460800000,"col-1-1":152,"col-2-3":113.375},{"col-0-2":1674547200000,"col-1-1":159,"col-2-3":73.625},{"col-0-2":1674633600000,"col-1-1":165,"col-2-3":72.8125},{"col-0-2":1674720000000,"col-1-1":153,"col-2-3":113.375},{"col-0-2":1674806400000,"col-1-1":149,"col-2-3":82.5},{"col-0-2":1674892800000,"col-1-1":94,"col-2-3":54}],"columns":[{"id":"col-0-2","name":"order_date per day","meta":{"type":"date_histogram","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"order_date","timeRange":{"from":"now-90d","to":"now"},"useNormalizedOpenSearchInterval":true,"scaleMetricValues":false,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{}}}},{"id":"col-1-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}},{"id":"col-2-3","name":"Max products.min_price","meta":{"type":"max","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"products.min_price"}}}]}'; +export const complexVisParams = + '{"addLegend":true,"addTimeMarker":true,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"grid":{"categoryLines":false,"valueAxis":"ValueAxis-1"},"labels":{},"legendPosition":"bottom","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"},{"data":{"id":"3","label":"Max products.min_price"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#E7664C","show":true,"style":"dashed","value":100,"width":1},"times":[],"type":"line","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":75,"show":true,"truncate":100},"name":"RightAxis-1","position":"right","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]}'; +export const complexDimensions = + '{"x":{"accessor":0,"format":{"id":"date","params":{"pattern":"YYYY-MM-DD"}},"params":{"date":true,"interval":"P1D","intervalESValue":1,"intervalESUnit":"d","format":"YYYY-MM-DD","bounds":{"min":"2022-11-19T03:26:04.730Z","max":"2023-02-17T03:26:04.730Z"}},"label":"order_date per day","aggType":"date_histogram"},"y":[{"accessor":1,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"},{"accessor":2,"format":{"id":"number","params":{"parsedUrl":{"origin":"http://localhost:5603","pathname":"/rao/app/visualize","basePath":"/rao"}}},"params":{},"label":"Max products.min_price","aggType":"max"}]}'; + +export const noXAxisDimensions = + '{"x":null,"y":[{"accessor":0,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"}]}'; + +export const simpleDatatable = + '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-2":1672214400000,"col-1-1":44},{"col-0-2":1672300800000,"col-1-1":150},{"col-0-2":1672387200000,"col-1-1":154},{"col-0-2":1672473600000,"col-1-1":144},{"col-0-2":1672560000000,"col-1-1":133},{"col-0-2":1672646400000,"col-1-1":149},{"col-0-2":1672732800000,"col-1-1":152},{"col-0-2":1672819200000,"col-1-1":144},{"col-0-2":1672905600000,"col-1-1":166},{"col-0-2":1672992000000,"col-1-1":151},{"col-0-2":1673078400000,"col-1-1":143},{"col-0-2":1673164800000,"col-1-1":148},{"col-0-2":1673251200000,"col-1-1":146},{"col-0-2":1673337600000,"col-1-1":137},{"col-0-2":1673424000000,"col-1-1":152},{"col-0-2":1673510400000,"col-1-1":152},{"col-0-2":1673596800000,"col-1-1":151},{"col-0-2":1673683200000,"col-1-1":157},{"col-0-2":1673769600000,"col-1-1":151},{"col-0-2":1673856000000,"col-1-1":152},{"col-0-2":1673942400000,"col-1-1":142},{"col-0-2":1674028800000,"col-1-1":151},{"col-0-2":1674115200000,"col-1-1":163},{"col-0-2":1674201600000,"col-1-1":156},{"col-0-2":1674288000000,"col-1-1":153},{"col-0-2":1674374400000,"col-1-1":162},{"col-0-2":1674460800000,"col-1-1":152},{"col-0-2":1674547200000,"col-1-1":159},{"col-0-2":1674633600000,"col-1-1":165},{"col-0-2":1674720000000,"col-1-1":153},{"col-0-2":1674806400000,"col-1-1":149},{"col-0-2":1674892800000,"col-1-1":94}],"columns":[{"id":"col-0-2","name":"order_date per day","meta":{"type":"date_histogram","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"order_date","timeRange":{"from":"now-90d","to":"now"},"useNormalizedOpenSearchInterval":true,"scaleMetricValues":false,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{}}}},{"id":"col-1-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}}]}'; +export const simpleVisParams = + '{"addLegend":true,"addTimeMarker":false,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"grid":{"categoryLines":false},"labels":{},"legendPosition":"right","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#E7664C","show":false,"style":"full","value":10,"width":1},"times":[],"type":"line","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":0,"show":true,"truncate":100},"name":"LeftAxis-1","position":"left","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]}'; +export const simpleDimensions = + '{"x":{"accessor":0,"format":{"id":"date","params":{"pattern":"YYYY-MM-DD"}},"params":{"date":true,"interval":"P1D","intervalESValue":1,"intervalESUnit":"d","format":"YYYY-MM-DD","bounds":{"min":"2022-11-18T00:14:09.617Z","max":"2023-02-16T00:14:09.617Z"}},"label":"order_date per day","aggType":"date_histogram"},"y":[{"accessor":1,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"}]}'; diff --git a/src/plugins/vis_type_vega/public/expressions/__mocks__/index.ts b/src/plugins/vis_type_vega/public/expressions/__mocks__/index.ts new file mode 100644 index 000000000000..0e8ad44d2bd7 --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/__mocks__/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './helpers'; diff --git a/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap new file mode 100644 index 000000000000..69af7eba10b3 --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`helpers createSpecFromDatatable() build complicated line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44,\\"col-2-3\\":60.9375},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154,\\"col-2-3\\":79.5},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144,\\"col-2-3\\":75.875},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133,\\"col-2-3\\":259.25},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149,\\"col-2-3\\":90},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152,\\"col-2-3\\":79.0625},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166,\\"col-2-3\\":85.25},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151,\\"col-2-3\\":92},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143,\\"col-2-3\\":90.75},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148,\\"col-2-3\\":92},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146,\\"col-2-3\\":83.25},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137,\\"col-2-3\\":98},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151,\\"col-2-3\\":87.4375},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157,\\"col-2-3\\":63.75},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151,\\"col-2-3\\":81.5625},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152,\\"col-2-3\\":100.6875},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142,\\"col-2-3\\":98},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151,\\"col-2-3\\":100.8125},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156,\\"col-2-3\\":85.8125},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153,\\"col-2-3\\":98},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162,\\"col-2-3\\":75.9375},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159,\\"col-2-3\\":73.625},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165,\\"col-2-3\\":72.8125},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149,\\"col-2-3\\":82.5},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94,\\"col-2-3\\":54}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"bottom\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":\\"ValueAxis-1\\",\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}},{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":\\"ValueAxis-1\\",\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Max products.min_price\\"}],\\"color\\":{\\"datum\\":\\"Max products.min_price\\"}}},{\\"mark\\":\\"rule\\",\\"encoding\\":{\\"x\\":{\\"type\\":\\"temporal\\",\\"field\\":\\"now_field\\"},\\"color\\":{\\"value\\":\\"red\\"},\\"size\\":{\\"value\\":1}}},{\\"mark\\":{\\"type\\":\\"rule\\",\\"color\\":\\"#E7664C\\",\\"strokeDash\\":[8,8]},\\"encoding\\":{\\"y\\":{\\"datum\\":100}}}],\\"transform\\":[{\\"calculate\\":\\"now()\\",\\"as\\":\\"now_field\\"}]}"`; + +exports[`helpers createSpecFromDatatable() build empty chart if no x-axis is defined" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[]}"`; + +exports[`helpers createSpecFromDatatable() build simple line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"orient\\":\\"left\\",\\"labels\\":true,\\"labelAngle\\":0},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}}]}"`; diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.test.js b/src/plugins/vis_type_vega/public/expressions/helpers.test.js new file mode 100644 index 000000000000..f09b1b8db3d6 --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/helpers.test.js @@ -0,0 +1,183 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + buildLayerMark, + buildXAxis, + buildYAxis, + cleanString, + createSpecFromDatatable, + formatDatatable, + setupConfig, +} from './helpers'; +import { + complexDatatable, + complexVisParams, + complexDimensions, + simpleDatatable, + simpleVisParams, + simpleDimensions, + noXAxisDimensions, +} from './__mocks__'; + +describe('helpers', function () { + describe('cleanString()', function () { + it('string should not contain "', function () { + const dirtyString = '"someString"'; + expect(cleanString(dirtyString)).toBe('someString'); + }); + }); + + describe('setupConfig()', function () { + it('check all legend positions', function () { + const baseConfig = { + view: { + stroke: null, + }, + concat: { + spacing: 0, + }, + legend: { + orient: null, + }, + kibana: { + hideWarnings: true, + }, + }; + const positions = ['top', 'right', 'left', 'bottom']; + positions.forEach((position) => { + const visParams = { legendPosition: position }; + baseConfig.legend.orient = position; + expect(setupConfig(visParams)).toStrictEqual(baseConfig); + }); + }); + }); + + describe('buildLayerMark()', function () { + const types = ['line', 'area', 'histogram']; + const interpolates = ['linear', 'cardinal', 'step-after']; + const strokeWidths = [-1, 0, 1, 2, 3, 4]; + const showCircles = [false, true]; + + it('check each mark possible value', function () { + const mark = { + type: null, + interpolate: null, + strokeWidth: null, + point: null, + }; + types.forEach((type) => { + mark.type = type; + interpolates.forEach((interpolate) => { + mark.interpolate = interpolate; + strokeWidths.forEach((strokeWidth) => { + mark.strokeWidth = strokeWidth; + showCircles.forEach((showCircle) => { + mark.point = showCircle; + const param = { + type: type, + interpolate: interpolate, + lineWidth: strokeWidth, + showCircles: showCircle, + }; + expect(buildLayerMark(param)).toStrictEqual(mark); + }); + }); + }); + }); + }); + }); + + describe('buildXAxis()', function () { + it('build different XAxis', function () { + const xAxisTitle = 'someTitle'; + const xAxisId = 'someId'; + [true, false].forEach((enableGrid) => { + const visParams = { grid: { categoryLines: enableGrid } }; + const vegaXAxis = { + axis: { + title: xAxisTitle, + grid: enableGrid, + }, + field: xAxisId, + type: 'temporal', + }; + expect(buildXAxis(xAxisTitle, xAxisId, visParams)).toStrictEqual(vegaXAxis); + }); + }); + }); + + describe('buildYAxis()', function () { + it('build different YAxis', function () { + const valueAxis = { + id: 'someId', + labels: { + rotate: 75, + show: false, + }, + position: 'left', + title: { + text: 'someText', + }, + }; + const column = { name: 'columnName', id: 'columnId' }; + const visParams = { grid: { valueAxis: true } }; + const vegaYAxis = { + axis: { + title: 'someText', + grid: true, + orient: 'left', + labels: false, + labelAngle: 75, + }, + field: 'columnId', + type: 'quantitative', + }; + expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); + + valueAxis.title.text = '""'; + vegaYAxis.axis.title = 'columnName'; + expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); + }); + }); + + describe('createSpecFromDatatable()', function () { + it('build simple line chart"', function () { + expect( + JSON.stringify( + createSpecFromDatatable( + formatDatatable(JSON.parse(simpleDatatable)), + JSON.parse(simpleVisParams), + JSON.parse(simpleDimensions) + ) + ) + ).toMatchSnapshot(); + }); + + it('build empty chart if no x-axis is defined"', function () { + expect( + JSON.stringify( + createSpecFromDatatable( + formatDatatable(JSON.parse(simpleDatatable)), + JSON.parse(simpleVisParams), + JSON.parse(noXAxisDimensions) + ) + ) + ).toMatchSnapshot(); + }); + + it('build complicated line chart"', function () { + expect( + JSON.stringify( + createSpecFromDatatable( + formatDatatable(JSON.parse(complexDatatable)), + JSON.parse(complexVisParams), + JSON.parse(complexDimensions) + ) + ) + ).toMatchSnapshot(); + }); + }); +}); diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.ts b/src/plugins/vis_type_vega/public/expressions/helpers.ts new file mode 100644 index 000000000000..ca31367bf119 --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/helpers.ts @@ -0,0 +1,238 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + OpenSearchDashboardsDatatable, + OpenSearchDashboardsDatatableColumn, +} from '../../../expressions/public'; +import { VislibDimensions, VisParams } from '../../../visualizations/public'; +import { isVisLayerColumn } from '../../../vis_augmenter/public'; + +// TODO: move this to the visualization plugin that has VisParams once all of these parameters have been better defined +interface ValueAxis { + id: string; + labels: { + filter: boolean; + rotate: number; + show: boolean; + truncate: number; + }; + name: string; + position: string; + scale: { + mode: string; + type: string; + }; + show: true; + style: any; + title: { + text: string; + }; + type: string; +} + +// Get the first xaxis field as only 1 setup of X Axis will be supported and +// there won't be support for split series and split chart +const getXAxisId = (dimensions: any, columns: OpenSearchDashboardsDatatableColumn[]): string => { + return columns.filter((column) => column.name === dimensions.x.label)[0].id; +}; + +export const cleanString = (rawString: string): string => { + return rawString.replaceAll('"', ''); +}; + +export const formatDatatable = ( + datatable: OpenSearchDashboardsDatatable +): OpenSearchDashboardsDatatable => { + datatable.columns.forEach((column) => { + // clean quotation marks from names in columns + column.name = cleanString(column.name); + }); + return datatable; +}; + +export const setupConfig = (visParams: VisParams) => { + const legendPosition = visParams.legendPosition; + return { + view: { + stroke: null, + }, + concat: { + spacing: 0, + }, + legend: { + orient: legendPosition, + }, + // This is parsed in the VegaParser and hides unnecessary warnings. + // For example, 'infinite extent' warnings that cover the chart + // when there is empty data for a time series + kibana: { + hideWarnings: true, + }, + }; +}; + +export const buildLayerMark = (seriesParams: { + type: string; + interpolate: string; + lineWidth: number; + showCircles: boolean; +}) => { + return { + // Possible types are: line, area, histogram. The eligibility checker will + // prevent area and histogram (though area works in vega-lite) + type: seriesParams.type, + // Possible types: linear, cardinal, step-after. All of these types work in vega-lite + interpolate: seriesParams.interpolate, + // The possible values is any number, which matches what vega-lite supports + strokeWidth: seriesParams.lineWidth, + // this corresponds to showing the dots in the visbuilder for each data point + point: seriesParams.showCircles, + }; +}; + +export const buildXAxis = (xAxisTitle: string, xAxisId: string, visParams: VisParams) => { + return { + axis: { + title: xAxisTitle, + grid: visParams.grid.categoryLines, + }, + field: xAxisId, + // Right now, the line charts can only set the x-axis value to be a date attribute, so + // this should always be of type temporal + type: 'temporal', + }; +}; + +export const buildYAxis = ( + column: OpenSearchDashboardsDatatableColumn, + valueAxis: ValueAxis, + visParams: VisParams +) => { + return { + axis: { + title: cleanString(valueAxis.title.text) || column.name, + grid: visParams.grid.valueAxis, + orient: valueAxis.position, + labels: valueAxis.labels.show, + labelAngle: valueAxis.labels.rotate, + }, + field: column.id, + type: 'quantitative', + }; +}; + +const isXAxisColumn = (column: OpenSearchDashboardsDatatableColumn): boolean => { + return column.meta?.aggConfigParams?.interval !== undefined; +}; + +export const createSpecFromDatatable = ( + datatable: OpenSearchDashboardsDatatable, + visParams: VisParams, + dimensions: VislibDimensions +): object => { + // TODO: we can try to use VegaSpec type but it is currently very outdated, where many + // of the fields and sub-fields don't have other optional params that we want for customizing. + // For now, we make this more loosely-typed by just specifying it as a generic object. + const spec = {} as any; + + spec.$schema = 'https://vega.github.io/schema/vega-lite/v5.json'; + spec.data = { + values: datatable.rows, + }; + spec.config = setupConfig(visParams); + + // Get the valueAxes data and generate a map to easily fetch the different valueAxes data + const valueAxis = new Map(); + visParams?.valueAxes?.forEach((yAxis: ValueAxis) => { + valueAxis.set(yAxis.id, yAxis); + }); + + spec.layer = [] as any[]; + + if (datatable.rows.length > 0 && dimensions.x !== null) { + const xAxisId = getXAxisId(dimensions, datatable.columns); + const xAxisTitle = cleanString(dimensions.x.label); + let seriesParamSkipCount = 0; + datatable.columns.forEach((column, index) => { + // Don't add a layer for x axis column + if (isXAxisColumn(column)) { + seriesParamSkipCount++; + // Don't add a layer for vis layer column + } else if (!isVisLayerColumn(column)) { + const currentSeriesParams = visParams.seriesParams[index - seriesParamSkipCount]; + const currentValueAxis = valueAxis.get(currentSeriesParams.valueAxis.toString()); + let tooltip: Array<{ field: string; type: string; title: string }> = []; + if (visParams.addTooltip) { + tooltip = [ + { field: xAxisId, type: 'temporal', title: xAxisTitle }, + { field: column.id, type: 'quantitative', title: column.name }, + ]; + } + spec.layer.push({ + mark: buildLayerMark(currentSeriesParams), + encoding: { + x: buildXAxis(xAxisTitle, xAxisId, visParams), + y: buildYAxis(column, currentValueAxis, visParams), + tooltip, + color: { + // This ensures all the different metrics have their own distinct and unique color + datum: column.name, + }, + }, + }); + } + }); + } + + if (visParams.addTimeMarker) { + spec.transform = [ + { + calculate: 'now()', + as: 'now_field', + }, + ]; + + spec.layer.push({ + mark: 'rule', + encoding: { + x: { + type: 'temporal', + field: 'now_field', + }, + // The time marker on vislib is red, so keeping this consistent + color: { + value: 'red', + }, + size: { + value: 1, + }, + }, + }); + } + + if (visParams.thresholdLine.show as boolean) { + const layer = { + mark: { + type: 'rule', + color: visParams.thresholdLine.color, + strokeDash: [1, 0], + }, + encoding: { + y: { + datum: visParams.thresholdLine.value, + }, + }, + }; + + // Can only support making a threshold line with full or dashed style, but not dot-dashed + // due to vega-lite limitations + if (visParams.thresholdLine.style !== 'full') { + layer.mark.strokeDash = [8, 8]; + } + spec.layer.push(layer); + } + return spec; +}; diff --git a/src/plugins/vis_type_vega/public/expressions/index.ts b/src/plugins/vis_type_vega/public/expressions/index.ts new file mode 100644 index 000000000000..dce44f56c47d --- /dev/null +++ b/src/plugins/vis_type_vega/public/expressions/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { LineVegaSpecExpressionFunctionDefinition } from './line_vega_spec_fn'; +export { VegaExpressionFunctionDefinition } from './vega_fn'; diff --git a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js deleted file mode 100644 index 2260c46b841a..000000000000 --- a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.test.js +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { - buildLayerMark, - buildXAxis, - buildYAxis, - cleanString, - createSpecFromDatatable, - formatDataTable, - setupConfig, -} from './line_vega_spec_fn'; - -describe('cleanString()', function () { - it('string should not contain "', function () { - const dirtyString = '"someString"'; - expect(cleanString(dirtyString)).toBe('someString'); - }); -}); - -describe('setupConfig()', function () { - it('check all legend positions', function () { - const baseConfig = { - view: { - stroke: null, - }, - concat: { - spacing: 0, - }, - legend: { - orient: null, - }, - }; - const positions = ['top', 'right', 'left', 'bottom']; - positions.forEach((position) => { - const visParams = { legendPosition: position }; - baseConfig.legend.orient = position; - expect(setupConfig(visParams)).toStrictEqual(baseConfig); - }); - }); -}); - -describe('buildLayerMark()', function () { - const types = ['line', 'area', 'histogram']; - const interpolates = ['linear', 'cardinal', 'step-after']; - const strokeWidths = [-1, 0, 1, 2, 3, 4]; - const showCircles = [false, true]; - - it('check each mark possible value', function () { - const mark = { - type: null, - interpolate: null, - strokeWidth: null, - point: null, - }; - types.forEach((type) => { - mark.type = type; - interpolates.forEach((interpolate) => { - mark.interpolate = interpolate; - strokeWidths.forEach((strokeWidth) => { - mark.strokeWidth = strokeWidth; - showCircles.forEach((showCircle) => { - mark.point = showCircle; - const param = { - type: type, - interpolate: interpolate, - lineWidth: strokeWidth, - showCircles: showCircle, - }; - expect(buildLayerMark(param)).toStrictEqual(mark); - }); - }); - }); - }); - }); -}); - -describe('buildXAxis()', function () { - it('build different XAxis', function () { - const xAxisTitle = 'someTitle'; - const xAxisId = 'someId'; - const startTime = 1676596400; - const endTime = 1676796400; - [true, false].forEach((enableGrid) => { - const visParams = { grid: { categoryLines: enableGrid } }; - const vegaXAxis = { - axis: { - title: xAxisTitle, - grid: enableGrid, - }, - field: xAxisId, - type: 'temporal', - scale: { - domain: [startTime, endTime], - }, - }; - expect(buildXAxis(xAxisTitle, xAxisId, startTime, endTime, visParams)).toStrictEqual( - vegaXAxis - ); - }); - }); -}); - -describe('buildYAxis()', function () { - it('build different YAxis', function () { - const valueAxis = { - id: 'someId', - labels: { - rotate: 75, - show: false, - }, - position: 'left', - title: { - text: 'someText', - }, - }; - const column = { name: 'columnName', id: 'columnId' }; - const visParams = { grid: { valueAxis: true } }; - const vegaYAxis = { - axis: { - title: 'someText', - grid: true, - orient: 'left', - labels: false, - labelAngle: 75, - }, - field: 'columnId', - type: 'quantitative', - }; - expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); - - valueAxis.title.text = '""'; - vegaYAxis.axis.title = 'columnName'; - expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); - }); -}); - -describe('createSpecFromDatatable()', function () { - it('build simple line chart"', function () { - const datatable = - '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-2":1672214400000,"col-1-1":44},{"col-0-2":1672300800000,"col-1-1":150},{"col-0-2":1672387200000,"col-1-1":154},{"col-0-2":1672473600000,"col-1-1":144},{"col-0-2":1672560000000,"col-1-1":133},{"col-0-2":1672646400000,"col-1-1":149},{"col-0-2":1672732800000,"col-1-1":152},{"col-0-2":1672819200000,"col-1-1":144},{"col-0-2":1672905600000,"col-1-1":166},{"col-0-2":1672992000000,"col-1-1":151},{"col-0-2":1673078400000,"col-1-1":143},{"col-0-2":1673164800000,"col-1-1":148},{"col-0-2":1673251200000,"col-1-1":146},{"col-0-2":1673337600000,"col-1-1":137},{"col-0-2":1673424000000,"col-1-1":152},{"col-0-2":1673510400000,"col-1-1":152},{"col-0-2":1673596800000,"col-1-1":151},{"col-0-2":1673683200000,"col-1-1":157},{"col-0-2":1673769600000,"col-1-1":151},{"col-0-2":1673856000000,"col-1-1":152},{"col-0-2":1673942400000,"col-1-1":142},{"col-0-2":1674028800000,"col-1-1":151},{"col-0-2":1674115200000,"col-1-1":163},{"col-0-2":1674201600000,"col-1-1":156},{"col-0-2":1674288000000,"col-1-1":153},{"col-0-2":1674374400000,"col-1-1":162},{"col-0-2":1674460800000,"col-1-1":152},{"col-0-2":1674547200000,"col-1-1":159},{"col-0-2":1674633600000,"col-1-1":165},{"col-0-2":1674720000000,"col-1-1":153},{"col-0-2":1674806400000,"col-1-1":149},{"col-0-2":1674892800000,"col-1-1":94}],"columns":[{"id":"col-0-2","name":"order_date per day","meta":{"type":"date_histogram","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"order_date","timeRange":{"from":"now-90d","to":"now"},"useNormalizedOpenSearchInterval":true,"scaleMetricValues":false,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{}}}},{"id":"col-1-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}}]}'; - const visParams = - '{"addLegend":true,"addTimeMarker":false,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"grid":{"categoryLines":false},"labels":{},"legendPosition":"right","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#E7664C","show":false,"style":"full","value":10,"width":1},"times":[],"type":"line","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":0,"show":true,"truncate":100},"name":"LeftAxis-1","position":"left","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]}'; - const dimensions = - '{"x":{"accessor":0,"format":{"id":"date","params":{"pattern":"YYYY-MM-DD"}},"params":{"date":true,"interval":"P1D","intervalESValue":1,"intervalESUnit":"d","format":"YYYY-MM-DD","bounds":{"min":"2022-11-18T00:14:09.617Z","max":"2023-02-16T00:14:09.617Z"}},"label":"order_date per day","aggType":"date_histogram"},"y":[{"accessor":1,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"}]}'; - const spec = - '{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"col-0-2":1672214400000,"col-1-1":44},{"col-0-2":1672300800000,"col-1-1":150},{"col-0-2":1672387200000,"col-1-1":154},{"col-0-2":1672473600000,"col-1-1":144},{"col-0-2":1672560000000,"col-1-1":133},{"col-0-2":1672646400000,"col-1-1":149},{"col-0-2":1672732800000,"col-1-1":152},{"col-0-2":1672819200000,"col-1-1":144},{"col-0-2":1672905600000,"col-1-1":166},{"col-0-2":1672992000000,"col-1-1":151},{"col-0-2":1673078400000,"col-1-1":143},{"col-0-2":1673164800000,"col-1-1":148},{"col-0-2":1673251200000,"col-1-1":146},{"col-0-2":1673337600000,"col-1-1":137},{"col-0-2":1673424000000,"col-1-1":152},{"col-0-2":1673510400000,"col-1-1":152},{"col-0-2":1673596800000,"col-1-1":151},{"col-0-2":1673683200000,"col-1-1":157},{"col-0-2":1673769600000,"col-1-1":151},{"col-0-2":1673856000000,"col-1-1":152},{"col-0-2":1673942400000,"col-1-1":142},{"col-0-2":1674028800000,"col-1-1":151},{"col-0-2":1674115200000,"col-1-1":163},{"col-0-2":1674201600000,"col-1-1":156},{"col-0-2":1674288000000,"col-1-1":153},{"col-0-2":1674374400000,"col-1-1":162},{"col-0-2":1674460800000,"col-1-1":152},{"col-0-2":1674547200000,"col-1-1":159},{"col-0-2":1674633600000,"col-1-1":165},{"col-0-2":1674720000000,"col-1-1":153},{"col-0-2":1674806400000,"col-1-1":149},{"col-0-2":1674892800000,"col-1-1":94}]},"config":{"view":{"stroke":null},"concat":{"spacing":0},"legend":{"orient":"right"}},"layer":[{"mark":{"type":"line","interpolate":"linear","strokeWidth":2,"point":true},"encoding":{"x":{"axis":{"title":"order_date per day","grid":false},"field":"col-0-2","type":"temporal","scale":{"domain":[1668730449617,1676506449617]}},"y":{"axis":{"title":"Count","orient":"left","labels":true,"labelAngle":0},"field":"col-1-1","type":"quantitative"},"tooltip":[{"field":"col-0-2","type":"temporal","title":"order_date per day"},{"field":"col-1-1","type":"quantitative","title":"Count"}],"color":{"datum":"Count"}}}]}'; - expect( - JSON.stringify( - createSpecFromDatatable( - formatDataTable(JSON.parse(datatable)), - JSON.parse(visParams), - JSON.parse(dimensions) - ) - ) - ).toBe(spec); - }); - - it('build empty chart if no x-axis is defined"', function () { - const datatable = - '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-1":4675}],"columns":[{"id":"col-0-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}}]}'; - const visParams = - '{"type":"line","grid":{"categoryLines":false},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"filter":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":true,"type":"line","mode":"normal","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"lineWidth":2,"interpolate":"linear","showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"labels":{},"thresholdLine":{"show":false,"value":10,"width":1,"style":"full","color":"#E7664C"}}'; - const dimensions = - '{"x":null,"y":[{"accessor":0,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"}]}'; - const spec = - '{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"col-0-1":4675}]},"config":{"view":{"stroke":null},"concat":{"spacing":0},"legend":{"orient":"right"}},"layer":[]}'; - expect( - JSON.stringify( - createSpecFromDatatable( - formatDataTable(JSON.parse(datatable)), - JSON.parse(visParams), - JSON.parse(dimensions) - ) - ) - ).toBe(spec); - }); - - it('build complicated line chart"', function () { - const datatable = - '{"type":"opensearch_dashboards_datatable","rows":[{"col-0-2":1672214400000,"col-1-1":44,"col-2-3":60.9375},{"col-0-2":1672300800000,"col-1-1":150,"col-2-3":82.5},{"col-0-2":1672387200000,"col-1-1":154,"col-2-3":79.5},{"col-0-2":1672473600000,"col-1-1":144,"col-2-3":75.875},{"col-0-2":1672560000000,"col-1-1":133,"col-2-3":259.25},{"col-0-2":1672646400000,"col-1-1":149,"col-2-3":90},{"col-0-2":1672732800000,"col-1-1":152,"col-2-3":79.0625},{"col-0-2":1672819200000,"col-1-1":144,"col-2-3":82.5},{"col-0-2":1672905600000,"col-1-1":166,"col-2-3":85.25},{"col-0-2":1672992000000,"col-1-1":151,"col-2-3":92},{"col-0-2":1673078400000,"col-1-1":143,"col-2-3":90.75},{"col-0-2":1673164800000,"col-1-1":148,"col-2-3":92},{"col-0-2":1673251200000,"col-1-1":146,"col-2-3":83.25},{"col-0-2":1673337600000,"col-1-1":137,"col-2-3":98},{"col-0-2":1673424000000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673510400000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673596800000,"col-1-1":151,"col-2-3":87.4375},{"col-0-2":1673683200000,"col-1-1":157,"col-2-3":63.75},{"col-0-2":1673769600000,"col-1-1":151,"col-2-3":81.5625},{"col-0-2":1673856000000,"col-1-1":152,"col-2-3":100.6875},{"col-0-2":1673942400000,"col-1-1":142,"col-2-3":98},{"col-0-2":1674028800000,"col-1-1":151,"col-2-3":100.8125},{"col-0-2":1674115200000,"col-1-1":163,"col-2-3":83.6875},{"col-0-2":1674201600000,"col-1-1":156,"col-2-3":85.8125},{"col-0-2":1674288000000,"col-1-1":153,"col-2-3":98},{"col-0-2":1674374400000,"col-1-1":162,"col-2-3":75.9375},{"col-0-2":1674460800000,"col-1-1":152,"col-2-3":113.375},{"col-0-2":1674547200000,"col-1-1":159,"col-2-3":73.625},{"col-0-2":1674633600000,"col-1-1":165,"col-2-3":72.8125},{"col-0-2":1674720000000,"col-1-1":153,"col-2-3":113.375},{"col-0-2":1674806400000,"col-1-1":149,"col-2-3":82.5},{"col-0-2":1674892800000,"col-1-1":94,"col-2-3":54}],"columns":[{"id":"col-0-2","name":"order_date per day","meta":{"type":"date_histogram","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"order_date","timeRange":{"from":"now-90d","to":"now"},"useNormalizedOpenSearchInterval":true,"scaleMetricValues":false,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{}}}},{"id":"col-1-1","name":"Count","meta":{"type":"count","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{}}},{"id":"col-2-3","name":"Max products.min_price","meta":{"type":"max","indexPatternId":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","aggConfigParams":{"field":"products.min_price"}}}]}'; - const visParams = - '{"addLegend":true,"addTimeMarker":true,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"grid":{"categoryLines":false,"valueAxis":"ValueAxis-1"},"labels":{},"legendPosition":"bottom","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"},{"data":{"id":"3","label":"Max products.min_price"},"drawLinesBetweenPoints":true,"interpolate":"linear","lineWidth":2,"mode":"normal","show":true,"showCircles":true,"type":"line","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#E7664C","show":true,"style":"dashed","value":100,"width":1},"times":[],"type":"line","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":75,"show":true,"truncate":100},"name":"RightAxis-1","position":"right","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]}'; - const dimensions = - '{"x":{"accessor":0,"format":{"id":"date","params":{"pattern":"YYYY-MM-DD"}},"params":{"date":true,"interval":"P1D","intervalESValue":1,"intervalESUnit":"d","format":"YYYY-MM-DD","bounds":{"min":"2022-11-19T03:26:04.730Z","max":"2023-02-17T03:26:04.730Z"}},"label":"order_date per day","aggType":"date_histogram"},"y":[{"accessor":1,"format":{"id":"number"},"params":{},"label":"Count","aggType":"count"},{"accessor":2,"format":{"id":"number","params":{"parsedUrl":{"origin":"http://localhost:5603","pathname":"/rao/app/visualize","basePath":"/rao"}}},"params":{},"label":"Max products.min_price","aggType":"max"}]}'; - const spec = - '{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"col-0-2":1672214400000,"col-1-1":44,"col-2-3":60.9375},{"col-0-2":1672300800000,"col-1-1":150,"col-2-3":82.5},{"col-0-2":1672387200000,"col-1-1":154,"col-2-3":79.5},{"col-0-2":1672473600000,"col-1-1":144,"col-2-3":75.875},{"col-0-2":1672560000000,"col-1-1":133,"col-2-3":259.25},{"col-0-2":1672646400000,"col-1-1":149,"col-2-3":90},{"col-0-2":1672732800000,"col-1-1":152,"col-2-3":79.0625},{"col-0-2":1672819200000,"col-1-1":144,"col-2-3":82.5},{"col-0-2":1672905600000,"col-1-1":166,"col-2-3":85.25},{"col-0-2":1672992000000,"col-1-1":151,"col-2-3":92},{"col-0-2":1673078400000,"col-1-1":143,"col-2-3":90.75},{"col-0-2":1673164800000,"col-1-1":148,"col-2-3":92},{"col-0-2":1673251200000,"col-1-1":146,"col-2-3":83.25},{"col-0-2":1673337600000,"col-1-1":137,"col-2-3":98},{"col-0-2":1673424000000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673510400000,"col-1-1":152,"col-2-3":83.6875},{"col-0-2":1673596800000,"col-1-1":151,"col-2-3":87.4375},{"col-0-2":1673683200000,"col-1-1":157,"col-2-3":63.75},{"col-0-2":1673769600000,"col-1-1":151,"col-2-3":81.5625},{"col-0-2":1673856000000,"col-1-1":152,"col-2-3":100.6875},{"col-0-2":1673942400000,"col-1-1":142,"col-2-3":98},{"col-0-2":1674028800000,"col-1-1":151,"col-2-3":100.8125},{"col-0-2":1674115200000,"col-1-1":163,"col-2-3":83.6875},{"col-0-2":1674201600000,"col-1-1":156,"col-2-3":85.8125},{"col-0-2":1674288000000,"col-1-1":153,"col-2-3":98},{"col-0-2":1674374400000,"col-1-1":162,"col-2-3":75.9375},{"col-0-2":1674460800000,"col-1-1":152,"col-2-3":113.375},{"col-0-2":1674547200000,"col-1-1":159,"col-2-3":73.625},{"col-0-2":1674633600000,"col-1-1":165,"col-2-3":72.8125},{"col-0-2":1674720000000,"col-1-1":153,"col-2-3":113.375},{"col-0-2":1674806400000,"col-1-1":149,"col-2-3":82.5},{"col-0-2":1674892800000,"col-1-1":94,"col-2-3":54}]},"config":{"view":{"stroke":null},"concat":{"spacing":0},"legend":{"orient":"bottom"}},"layer":[{"mark":{"type":"line","interpolate":"linear","strokeWidth":2,"point":true},"encoding":{"x":{"axis":{"title":"order_date per day","grid":false},"field":"col-0-2","type":"temporal","scale":{"domain":[1668828364730,1676604364730]}},"y":{"axis":{"title":"Count","grid":"ValueAxis-1","orient":"right","labels":true,"labelAngle":75},"field":"col-1-1","type":"quantitative"},"tooltip":[{"field":"col-0-2","type":"temporal","title":"order_date per day"},{"field":"col-1-1","type":"quantitative","title":"Count"}],"color":{"datum":"Count"}}},{"mark":{"type":"line","interpolate":"linear","strokeWidth":2,"point":true},"encoding":{"x":{"axis":{"title":"order_date per day","grid":false},"field":"col-0-2","type":"temporal","scale":{"domain":[1668828364730,1676604364730]}},"y":{"axis":{"title":"Count","grid":"ValueAxis-1","orient":"right","labels":true,"labelAngle":75},"field":"col-2-3","type":"quantitative"},"tooltip":[{"field":"col-0-2","type":"temporal","title":"order_date per day"},{"field":"col-2-3","type":"quantitative","title":"Max products.min_price"}],"color":{"datum":"Max products.min_price"}}},{"mark":"rule","encoding":{"x":{"type":"temporal","field":"now_field"},"color":{"value":"red"},"size":{"value":1}}},{"mark":{"type":"rule","color":"#E7664C","strokeDash":[8,8]},"encoding":{"y":{"datum":100}}}],"transform":[{"calculate":"now()","as":"now_field"}]}'; - expect( - JSON.stringify( - createSpecFromDatatable( - formatDataTable(JSON.parse(datatable)), - JSON.parse(visParams), - JSON.parse(dimensions) - ) - ) - ).toBe(spec); - }); -}); diff --git a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts index b0f440e59647..499310c106d2 100644 --- a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts +++ b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts @@ -3,15 +3,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { cloneDeep } from 'lodash'; +import { cloneDeep, isEmpty } from 'lodash'; import { i18n } from '@osd/i18n'; import { ExpressionFunctionDefinition, OpenSearchDashboardsDatatable, - OpenSearchDashboardsDatatableColumn, } from '../../../expressions/public'; -import { VegaVisualizationDependencies } from '../plugin'; import { VislibDimensions, VisParams } from '../../../visualizations/public'; +import { + VisLayer, + VisLayers, + PointInTimeEventsVisLayer, + isPointInTimeEventsVisLayer, + addPointInTimeEventsLayersToTable, + addPointInTimeEventsLayersToSpec, + enableVisLayersInSpecConfig, +} from '../../../vis_augmenter/public'; +import { formatDatatable, createSpecFromDatatable } from './helpers'; +import { VegaVisualizationDependencies } from '../plugin'; type Input = OpenSearchDashboardsDatatable; type Output = Promise<string>; @@ -29,236 +38,6 @@ export type LineVegaSpecExpressionFunctionDefinition = ExpressionFunctionDefinit Output >; -// TODO: move this to the visualization plugin that has VisParams once all of these parameters have been better defined -interface ValueAxis { - id: string; - labels: { - filter: boolean; - rotate: number; - show: boolean; - truncate: number; - }; - name: string; - position: string; - scale: { - mode: string; - type: string; - }; - show: true; - style: any; - title: { - text: string; - }; - type: string; -} - -// Get the first xaxis field as only 1 setup of X Axis will be supported and -// there won't be support for split series and split chart -const getXAxisId = (dimensions: any, columns: OpenSearchDashboardsDatatableColumn[]): string => { - return columns.filter((column) => column.name === dimensions.x.label)[0].id; -}; - -export const cleanString = (rawString: string): string => { - return rawString.replaceAll('"', ''); -}; - -export const formatDataTable = ( - datatable: OpenSearchDashboardsDatatable -): OpenSearchDashboardsDatatable => { - datatable.columns.forEach((column) => { - // clean quotation marks from names in columns - column.name = cleanString(column.name); - }); - return datatable; -}; - -export const setupConfig = (visParams: VisParams) => { - const legendPosition = visParams.legendPosition; - return { - view: { - stroke: null, - }, - concat: { - spacing: 0, - }, - legend: { - orient: legendPosition, - }, - }; -}; - -export const buildLayerMark = (seriesParams: { - type: string; - interpolate: string; - lineWidth: number; - showCircles: boolean; -}) => { - return { - // Possible types are: line, area, histogram. The eligibility checker will - // prevent area and histogram (though area works in vega-lite) - type: seriesParams.type, - // Possible types: linear, cardinal, step-after. All of these types work in vega-lite - interpolate: seriesParams.interpolate, - // The possible values is any number, which matches what vega-lite supports - strokeWidth: seriesParams.lineWidth, - // this corresponds to showing the dots in the visbuilder for each data point - point: seriesParams.showCircles, - }; -}; - -export const buildXAxis = ( - xAxisTitle: string, - xAxisId: string, - startTime: number, - endTime: number, - visParams: VisParams -) => { - return { - axis: { - title: xAxisTitle, - grid: visParams.grid.categoryLines, - }, - field: xAxisId, - // Right now, the line charts can only set the x-axis value to be a date attribute, so - // this should always be of type temporal - type: 'temporal', - scale: { - domain: [startTime, endTime], - }, - }; -}; - -export const buildYAxis = ( - column: OpenSearchDashboardsDatatableColumn, - valueAxis: ValueAxis, - visParams: VisParams -) => { - return { - axis: { - title: cleanString(valueAxis.title.text) || column.name, - grid: visParams.grid.valueAxis, - orient: valueAxis.position, - labels: valueAxis.labels.show, - labelAngle: valueAxis.labels.rotate, - }, - field: column.id, - type: 'quantitative', - }; -}; - -export const createSpecFromDatatable = ( - datatable: OpenSearchDashboardsDatatable, - visParams: VisParams, - dimensions: VislibDimensions -): object => { - // TODO: we can try to use VegaSpec type but it is currently very outdated, where many - // of the fields and sub-fields don't have other optional params that we want for customizing. - // For now, we make this more loosely-typed by just specifying it as a generic object. - const spec = {} as any; - - spec.$schema = 'https://vega.github.io/schema/vega-lite/v5.json'; - spec.data = { - values: datatable.rows, - }; - spec.config = setupConfig(visParams); - - // Get the valueAxes data and generate a map to easily fetch the different valueAxes data - const valueAxis = new Map(); - visParams?.valueAxes?.forEach((yAxis: ValueAxis) => { - valueAxis.set(yAxis.id, yAxis); - }); - - spec.layer = [] as any[]; - - if (datatable.rows.length > 0 && dimensions.x !== null) { - const xAxisId = getXAxisId(dimensions, datatable.columns); - const xAxisTitle = cleanString(dimensions.x.label); - // get x-axis bounds for the chart - const startTime = new Date(dimensions.x.params.bounds.min).valueOf(); - const endTime = new Date(dimensions.x.params.bounds.max).valueOf(); - let skip = 0; - datatable.columns.forEach((column, index) => { - // Check if it's not xAxis column data - if (column.meta?.aggConfigParams?.interval !== undefined) { - skip++; - } else { - const currentSeriesParams = visParams.seriesParams[index - skip]; - const currentValueAxis = valueAxis.get(currentSeriesParams.valueAxis.toString()); - let tooltip: Array<{ field: string; type: string; title: string }> = []; - if (visParams.addTooltip) { - tooltip = [ - { field: xAxisId, type: 'temporal', title: xAxisTitle }, - { field: column.id, type: 'quantitative', title: column.name }, - ]; - } - spec.layer.push({ - mark: buildLayerMark(currentSeriesParams), - encoding: { - x: buildXAxis(xAxisTitle, xAxisId, startTime, endTime, visParams), - y: buildYAxis(column, currentValueAxis, visParams), - tooltip, - color: { - // This ensures all the different metrics have their own distinct and unique color - datum: column.name, - }, - }, - }); - } - }); - } - - if (visParams.addTimeMarker) { - spec.transform = [ - { - calculate: 'now()', - as: 'now_field', - }, - ]; - - spec.layer.push({ - mark: 'rule', - encoding: { - x: { - type: 'temporal', - field: 'now_field', - }, - // The time marker on vislib is red, so keeping this consistent - color: { - value: 'red', - }, - size: { - value: 1, - }, - }, - }); - } - - if (visParams.thresholdLine.show as boolean) { - const layer = { - mark: { - type: 'rule', - color: visParams.thresholdLine.color, - strokeDash: [1, 0], - }, - encoding: { - y: { - datum: visParams.thresholdLine.value, - }, - }, - }; - - // Can only support making a threshold line with full or dashed style, but not dot-dashed - // due to vega-lite limitations - if (visParams.thresholdLine.style !== 'full') { - layer.mark.strokeDash = [8, 8]; - } - - spec.layer.push(layer); - } - - return spec; -}; - export const createLineVegaSpecFn = ( dependencies: VegaVisualizationDependencies ): LineVegaSpecExpressionFunctionDefinition => ({ @@ -286,14 +65,27 @@ export const createLineVegaSpecFn = ( }, }, async fn(input, args, context) { - const table = cloneDeep(input); + let table = formatDatatable(cloneDeep(input)); + + const visParams = JSON.parse(args.visParams) as VisParams; + const dimensions = JSON.parse(args.dimensions) as VislibDimensions; + const allVisLayers = (args.visLayers ? JSON.parse(args.visLayers) : []) as VisLayers; + + // currently only supporting PointInTimeEventsVisLayer type + const pointInTimeEventsVisLayers = allVisLayers.filter((visLayer: VisLayer) => + isPointInTimeEventsVisLayer(visLayer) + ) as PointInTimeEventsVisLayer[]; - // creating initial vega spec from table - const spec = createSpecFromDatatable( - formatDataTable(table), - JSON.parse(args.visParams), - JSON.parse(args.dimensions) - ); + if (!isEmpty(pointInTimeEventsVisLayers) && dimensions.x !== null) { + table = addPointInTimeEventsLayersToTable(table, dimensions, pointInTimeEventsVisLayers); + } + + let spec = createSpecFromDatatable(table, visParams, dimensions); + + if (!isEmpty(pointInTimeEventsVisLayers) && dimensions.x !== null) { + spec = addPointInTimeEventsLayersToSpec(table, dimensions, spec); + spec.config = enableVisLayersInSpecConfig(spec, pointInTimeEventsVisLayers); + } return JSON.stringify(spec); }, }); diff --git a/src/plugins/vis_type_vega/public/index.ts b/src/plugins/vis_type_vega/public/index.ts index ed0c794837dd..da9b8b396dba 100644 --- a/src/plugins/vis_type_vega/public/index.ts +++ b/src/plugins/vis_type_vega/public/index.ts @@ -36,5 +36,4 @@ export function plugin(initializerContext: PluginInitializerContext<ConfigSchema return new Plugin(initializerContext); } -export { VegaExpressionFunctionDefinition } from './expressions/vega_fn'; -export { LineVegaSpecExpressionFunctionDefinition } from './expressions/line_vega_spec_fn'; +export * from './expressions'; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index d47de0a4e7a6..65843ff05162 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -259,6 +259,22 @@ export class VegaBaseView { return false; } + // This is only used when the PointInTimeEventsVisLayer type in vega parser's + // visibleVisLayers is true. This VisLayer type is currently only supported + // for time series chart types. It may be updated + // in the future to expand to other chart type use cases. + // Because there is no clean way to use autosize for concatenated views, + // we manually set the value of the top view (the base vis) to fill in + // space and leave enough space to show the bottom view (the events vis). + // Ref: https://vega.github.io/vega-lite/docs/size.html#limitations + addPointInTimeEventPadding(view) { + // TODO: 100 is enough padding for now. May need to adjust once the current scrolling/overflow + // issue is handled. See https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3501 + const eventVisHeight = 100; + const height = Math.max(0, this._$container.height()) - eventVisHeight; + view._signals.concat_0_height.value = height; + } + setView(view) { if (this._view === view) return; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_view.js index 45d15849ed47..4e9a2e53c144 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_view.js @@ -28,6 +28,7 @@ * under the License. */ +import { VisLayerTypes } from '../../../vis_augmenter/public'; import { vega } from '../lib/vega'; import { VegaBaseView } from './vega_base_view'; @@ -44,6 +45,9 @@ export class VegaView extends VegaBaseView { view.warn = this.onWarn.bind(this); view.error = this.onError.bind(this); if (this._parser.useResize) this.updateVegaSize(view); + if (this._parser.visibleVisLayers?.get(VisLayerTypes.PointInTimeEvents) === true) { + this.addPointInTimeEventPadding(view); + } view.initialize(this._$container.get(0), this._$controls.get(0)); if (this._parser.useHover) view.hover(); diff --git a/src/plugins/vis_type_vislib/public/line_to_expression.ts b/src/plugins/vis_type_vislib/public/line_to_expression.ts index 26797d8348a0..2648e70af38d 100644 --- a/src/plugins/vis_type_vislib/public/line_to_expression.ts +++ b/src/plugins/vis_type_vislib/public/line_to_expression.ts @@ -42,7 +42,7 @@ export const toExpressionAst = async (vis: Vis, params: any) => { const vegaSpecFn = buildExpressionFunction<LineVegaSpecExpressionFunctionDefinition>( 'line_vega_spec', { - visLayers: JSON.stringify([]), + visLayers: JSON.stringify(params.visLayers), visParams: JSON.stringify(vis.params), dimensions: JSON.stringify(dimensions), } diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 48f1d8306345..ec0fc098189d 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -54,13 +54,13 @@ import { setExpressions, setUiActions, setSavedVisualizationsLoader, + setSavedAugmentVisLoader, setTimeFilter, setAggs, setChrome, setOverlays, setSavedSearchLoader, setEmbeddable, - setSavedAugmentVisLoader, } from './services'; import { VISUALIZE_EMBEDDABLE_TYPE, @@ -195,30 +195,30 @@ export class VisualizationsPlugin setAggs(data.search.aggs); setOverlays(core.overlays); setChrome(core.chrome); - const savedAugmentVisLoader = createSavedAugmentVisLoader({ + const savedVisualizationsLoader = createSavedVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, search: data.search, chrome: core.chrome, overlays: core.overlays, + visualizationTypes: types, }); - setSavedAugmentVisLoader(savedAugmentVisLoader); - const savedVisualizationsLoader = createSavedVisLoader({ + setSavedVisualizationsLoader(savedVisualizationsLoader); + const savedSearchLoader = createSavedSearchesLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, search: data.search, chrome: core.chrome, overlays: core.overlays, - visualizationTypes: types, }); - setSavedVisualizationsLoader(savedVisualizationsLoader); - const savedSearchLoader = createSavedSearchesLoader({ + const savedAugmentVisLoader = createSavedAugmentVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, search: data.search, chrome: core.chrome, overlays: core.overlays, }); + setSavedAugmentVisLoader(savedAugmentVisLoader); setSavedSearchLoader(savedSearchLoader); return { ...types, From de61732def96c323504a282d466d6c74d33d2d78 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Fri, 31 Mar 2023 12:30:51 -0700 Subject: [PATCH 11/37] [Feature Anywhere] Fix visibleVisLayers ser/deser (#3758) * Fix visibleVisLayers ser/deser * Update wording in comments * Add test for conversion Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../vis_augmenter/public/test_constants.ts | 13 +++++++ .../vis_augmenter/public/vega/helpers.test.ts | 34 +++++++++++++------ .../vis_augmenter/public/vega/helpers.ts | 8 ++--- .../vis_type_vega/public/data_model/types.ts | 2 ++ .../public/data_model/vega_parser.test.js | 20 +++++++++++ .../public/data_model/vega_parser.ts | 4 +++ 6 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index bc77e8ba803f..6bdaa7a66d78 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -344,6 +344,19 @@ export const TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS = [ }, ]; +export const TEST_VIS_LAYERS_SINGLE_EMPTY_EVENTS = [ + { + originPlugin: TEST_PLUGIN, + type: VisLayerTypes.PointInTimeEvents, + pluginResource: { + type: TEST_PLUGIN_RESOURCE_TYPE, + id: TEST_PLUGIN_RESOURCE_ID, + name: TEST_PLUGIN_RESOURCE_NAME, + urlPath: TEST_PLUGIN_RESOURCE_PATH, + }, + }, +]; + export const TEST_VIS_LAYERS_SINGLE_ON_BOUNDS = [ { originPlugin: TEST_PLUGIN, diff --git a/src/plugins/vis_augmenter/public/vega/helpers.test.ts b/src/plugins/vis_augmenter/public/vega/helpers.test.ts index 3f1fa58a4fc3..3f0145dc16d0 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.test.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.test.ts @@ -37,6 +37,7 @@ import { TEST_SPEC_SINGLE_VIS_LAYER, TEST_VIS_LAYERS_MULTIPLE, TEST_VIS_LAYERS_SINGLE, + TEST_VIS_LAYERS_SINGLE_EMPTY_EVENTS, TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS, TEST_VIS_LAYERS_SINGLE_ON_BOUNDS, } from '../test_constants'; @@ -81,11 +82,11 @@ describe('helpers', function () { const updatedConfig = enableVisLayersInSpecConfig({ config: baseConfig }, [ pointInTimeEventsVisLayer, ]); - const expectedMap = new Map<VisLayerTypes, boolean>([ - [VisLayerTypes.PointInTimeEvents, true], - ]); + const expectedArr = [ + ...new Map<VisLayerTypes, boolean>([[VisLayerTypes.PointInTimeEvents, true]]), + ]; // @ts-ignore - baseConfig.kibana.visibleVisLayers = expectedMap; + baseConfig.kibana.visibleVisLayers = expectedArr; expect(updatedConfig).toStrictEqual(baseConfig); }); it('updates config with a valid and invalid VisLayer', function () { @@ -98,11 +99,11 @@ describe('helpers', function () { pointInTimeEventsVisLayer, invalidVisLayer, ]); - const expectedMap = new Map<VisLayerTypes, boolean>([ - [VisLayerTypes.PointInTimeEvents, true], - ]); + const expectedArr = [ + ...new Map<VisLayerTypes, boolean>([[VisLayerTypes.PointInTimeEvents, true]]), + ]; // @ts-ignore - baseConfig.kibana.visibleVisLayers = expectedMap; + baseConfig.kibana.visibleVisLayers = expectedArr; expect(updatedConfig).toStrictEqual(baseConfig); }); it('does not update config if no valid VisLayer', function () { @@ -113,7 +114,7 @@ describe('helpers', function () { }; const updatedConfig = enableVisLayersInSpecConfig({ config: baseConfig }, [invalidVisLayer]); // @ts-ignore - baseConfig.kibana.visibleVisLayers = new Map<VisLayerTypes, boolean>(); + baseConfig.kibana.visibleVisLayers = [...new Map<VisLayerTypes, boolean>()]; expect(updatedConfig).toStrictEqual(baseConfig); }); it('does not update config if empty VisLayer list', function () { @@ -124,7 +125,7 @@ describe('helpers', function () { }; const updatedConfig = enableVisLayersInSpecConfig({ config: baseConfig }, []); // @ts-ignore - baseConfig.kibana.visibleVisLayers = new Map<VisLayerTypes, boolean>(); + baseConfig.kibana.visibleVisLayers = [...new Map<VisLayerTypes, boolean>()]; expect(updatedConfig).toStrictEqual(baseConfig); }); }); @@ -389,6 +390,19 @@ describe('helpers', function () { ) ).toStrictEqual(TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY); }); + // below case should not happen since only VisLayers with a populated + // set of events should be passed from the plugins. but, if it does + // happen, we can handle it more gracefully instead of throwing an error + it('vis layer with empty events adds nothing to datatable', function () { + expect( + addPointInTimeEventsLayersToTable( + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DIMENSIONS, + // @ts-ignore + TEST_VIS_LAYERS_SINGLE_EMPTY_EVENTS + ) + ).toStrictEqual(TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY); + }); it('vis layer with events on edge of bounds are added', function () { expect( addPointInTimeEventsLayersToTable( diff --git a/src/plugins/vis_augmenter/public/vega/helpers.ts b/src/plugins/vis_augmenter/public/vega/helpers.ts index 670a9c7590b5..2132e170bb4d 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.ts @@ -25,6 +25,8 @@ import { VisLayerTypes, } from '../'; +// Given any visLayers, create a map to indicate which VisLayer types are present. +// Convert to an array since ES6 Maps cannot be stringified. export const enableVisLayersInSpecConfig = (spec: object, visLayers: VisLayers): {} => { const config = get(spec, 'config', { kibana: {} }); const visibleVisLayers = new Map<VisLayerTypes, boolean>(); @@ -41,7 +43,7 @@ export const enableVisLayersInSpecConfig = (spec: object, visLayers: VisLayers): ...config, kibana: { ...config.kibana, - visibleVisLayers, + visibleVisLayers: [...visibleVisLayers], }, }; }; @@ -176,9 +178,7 @@ export const addPointInTimeEventsLayersToTable = ( }, }); - if (augmentedTable.rows.length === 0) { - return false; - } + if (augmentedTable.rows.length === 0 || isEmpty(visLayer.events)) return false; // if only one row / one datapoint, put all events into this bucket if (augmentedTable.rows.length === 1) { diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index cd6cbc94bcc7..b5db91558c03 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -32,6 +32,7 @@ import { SearchResponse, SearchParams } from 'elasticsearch'; import { Filter } from 'src/plugins/data/public'; import { DslQuery } from 'src/plugins/data/common'; +import { VisLayerTypes } from 'src/plugins/vis_augmenter/public'; import { OpenSearchQueryParser } from './opensearch_query_parser'; import { EmsFileParser } from './ems_file_parser'; import { UrlParser } from './url_parser'; @@ -113,6 +114,7 @@ export interface OpenSearchDashboards { hideWarnings: boolean; type: string; renderer: Renderer; + visibleVisLayers?: Map<VisLayerTypes, boolean>; } export interface VegaSpec { diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js index 8ceb565fa8c0..421b99790dc1 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js @@ -33,6 +33,7 @@ import { euiThemeVars } from '@osd/ui-shared-deps/theme'; import { euiPaletteColorBlind } from '@elastic/eui'; import { VegaParser } from './vega_parser'; import { bypassExternalUrlCheck } from '../vega_view/vega_base_view'; +import { VisLayerTypes } from '../../../vis_augmenter/public'; jest.mock('../services'); @@ -388,6 +389,25 @@ describe('VegaParser._parseConfig', () => { check({ config: { kibana: { a: 1 } } }, { a: 1 }, { config: {} }) ); test('_hostConfig', check({ _hostConfig: { a: 1 } }, { a: 1 }, {}, 1)); + test( + 'visibleVisLayers conversion to map', + check( + { + config: { + kibana: { + hideWarnings: true, + visibleVisLayers: [[VisLayerTypes.PointInTimeEvents, true]], + }, + }, + }, + { + hideWarnings: true, + visibleVisLayers: new Map([[VisLayerTypes.PointInTimeEvents, true]]), + }, + { config: {} }, + 0 + ) + ); }); describe('VegaParser._compileWithAutosize', () => { diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 4de808506f34..187c4690aad5 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -405,6 +405,10 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never ) ); } + // Converting the visibleVisLayers array back to a map + if (result.visibleVisLayers !== undefined && Array.isArray(result.visibleVisLayers)) { + result.visibleVisLayers = new Map<VisLayerTypes, boolean>(result.visibleVisLayers); + } } } return result || {}; From 03b393f9b39a4f8b34600b45f2aca6681cfe1e9e Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 26 Apr 2023 12:04:40 -0700 Subject: [PATCH 12/37] Add VisLayer error toasts when rendering vis (#3649) * Add VisLayer error toasts when rendering vis * Finalize formatting of err stack details * Add one test case * Make message of VisLayerError required * Move test_helpers; clean up test; add comment * Add id to error toast * Add context to helper fns * Change back to toStrictEqual() for tests Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../notifications/toasts/toasts_api.tsx | 4 ++ src/plugins/vis_augmenter/public/index.ts | 1 + .../saved_augment_vis/utils/test_helpers.ts | 2 +- .../vis_augmenter/public/types.test.ts | 33 +++++---- src/plugins/vis_augmenter/public/types.ts | 11 ++- .../vis_augmenter/public/utils/index.ts | 1 + .../public/utils/test_helpers.ts | 36 ++++++++++ .../vis_augmenter/public/utils/utils.test.ts | 71 ++++++++++++++++++- .../vis_augmenter/public/utils/utils.ts | 45 +++++++++++- .../public/embeddable/visualize_embeddable.ts | 21 +++++- src/plugins/visualizations/public/plugin.ts | 4 ++ src/plugins/visualizations/public/services.ts | 5 ++ 12 files changed, 211 insertions(+), 23 deletions(-) create mode 100644 src/plugins/vis_augmenter/public/utils/test_helpers.ts diff --git a/src/core/public/notifications/toasts/toasts_api.tsx b/src/core/public/notifications/toasts/toasts_api.tsx index 76d0bf9cf9b2..4a30dc6dab9e 100644 --- a/src/core/public/notifications/toasts/toasts_api.tsx +++ b/src/core/public/notifications/toasts/toasts_api.tsx @@ -87,6 +87,10 @@ export interface ErrorToastOptions extends ToastOptions { * message will still be shown in the detailed error modal. */ toastMessage?: string; + /** + * Unique ID for the toast. Can be used to prevent duplicate toasts on re-renders. + */ + id?: string; } const normalizeToast = (toastOrTitle: ToastInput): ToastInputFields => { diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index cab6e6fef286..bef66db8fd02 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -21,6 +21,7 @@ export { PointInTimeEvent, PointInTimeEventsVisLayer, isPointInTimeEventsVisLayer, + isVisLayerWithError, } from './types'; export * from './expressions'; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts index 9bbfb8940b27..38e80646ed15 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts @@ -4,7 +4,7 @@ */ import { cloneDeep } from 'lodash'; -import { VisLayerExpressionFn, ISavedAugmentVis } from '../../types'; +import { VisLayerExpressionFn, ISavedAugmentVis } from '../../'; import { VIS_REFERENCE_NAME } from '../saved_augment_vis_references'; const pluginResourceId = 'test-plugin-resource-id'; diff --git a/src/plugins/vis_augmenter/public/types.test.ts b/src/plugins/vis_augmenter/public/types.test.ts index 9e9b59d41425..732541812b37 100644 --- a/src/plugins/vis_augmenter/public/types.test.ts +++ b/src/plugins/vis_augmenter/public/types.test.ts @@ -3,20 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { VisLayerTypes, VisLayer, isPointInTimeEventsVisLayer, isValidVisLayer } from './types'; - -const generateVisLayer = (type: any): VisLayer => { - return { - type, - originPlugin: 'test-plugin', - pluginResource: { - type: 'test-resource-type', - id: 'test-resource-id', - name: 'test-resource-name', - urlPath: 'test-resource-url-path', - }, - }; -}; +import { + VisLayerTypes, + isPointInTimeEventsVisLayer, + isValidVisLayer, + isVisLayerWithError, +} from './types'; +import { generateVisLayer } from './utils'; describe('isPointInTimeEventsVisLayer()', function () { it('should return false if type does not match', function () { @@ -41,3 +34,15 @@ describe('isValidVisLayer()', function () { expect(isValidVisLayer(visLayer)).toBe(true); }); }); + +describe('isVisLayerWithError()', function () { + it('should return false if no error', function () { + const visLayer = generateVisLayer('unknown-vis-layer-type', false); + expect(isVisLayerWithError(visLayer)).toBe(false); + }); + + it('should return true if error', function () { + const visLayer = generateVisLayer(VisLayerTypes.PointInTimeEvents, true); + expect(isVisLayerWithError(visLayer)).toBe(true); + }); +}); diff --git a/src/plugins/vis_augmenter/public/types.ts b/src/plugins/vis_augmenter/public/types.ts index a45ce4f20df3..d7a75c12d125 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -14,7 +14,7 @@ export enum VisLayerErrorTypes { export interface VisLayerError { type: keyof typeof VisLayerErrorTypes; - message?: string; + message: string; } export type PluginResourceType = string; @@ -53,6 +53,15 @@ export const isPointInTimeEventsVisLayer = (obj: any) => { return obj?.type === VisLayerTypes.PointInTimeEvents; }; +/** + * Used to determine if a given saved obj has a valid type and can + * be converted into a VisLayer + */ export const isValidVisLayer = (obj: any) => { return obj?.type in VisLayerTypes; }; + +/** + * Used for checking if an existing VisLayer has a populated error field or not + */ +export const isVisLayerWithError = (visLayer: VisLayer): boolean => visLayer.error !== undefined; diff --git a/src/plugins/vis_augmenter/public/utils/index.ts b/src/plugins/vis_augmenter/public/utils/index.ts index 079132ce99d2..dc0cbebbe7de 100644 --- a/src/plugins/vis_augmenter/public/utils/index.ts +++ b/src/plugins/vis_augmenter/public/utils/index.ts @@ -4,3 +4,4 @@ */ export * from './utils'; +export * from './test_helpers'; diff --git a/src/plugins/vis_augmenter/public/utils/test_helpers.ts b/src/plugins/vis_augmenter/public/utils/test_helpers.ts new file mode 100644 index 000000000000..7796ddb4f394 --- /dev/null +++ b/src/plugins/vis_augmenter/public/utils/test_helpers.ts @@ -0,0 +1,36 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get } from 'lodash'; +import { VisLayer, VisLayerErrorTypes } from '../types'; + +export const generateVisLayer = ( + type: any, + error: boolean = false, + errorMessage: string = 'some-error-message', + resource?: { + type?: string; + id?: string; + name?: string; + urlPath?: string; + } +): VisLayer => { + return { + type, + originPlugin: 'test-plugin', + pluginResource: { + type: get(resource, 'type', 'test-resource-type'), + id: get(resource, 'id', 'test-resource-id'), + name: get(resource, 'name', 'test-resource-name'), + urlPath: get(resource, 'urlPath', 'test-resource-url-path'), + }, + error: error + ? { + type: VisLayerErrorTypes.FETCH_FAILURE, + message: errorMessage, + } + : undefined, + }; +}; diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index 3c770b1db8ba..0820d6e9c362 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -7,15 +7,19 @@ import { Vis } from '../../../visualizations/public'; import { buildPipelineFromAugmentVisSavedObjs, getAugmentVisSavedObjs, + getAnyErrors, isEligibleForVisLayers, } from './utils'; -import { VisLayerTypes, ISavedAugmentVis, VisLayerExpressionFn } from '../types'; import { createSavedAugmentVisLoader, SavedObjectOpenSearchDashboardsServicesWithAugmentVis, getMockAugmentVisSavedObjectClient, generateAugmentVisSavedObject, -} from '../saved_augment_vis'; + ISavedAugmentVis, + VisLayerExpressionFn, + VisLayerTypes, +} from '../'; +import { generateVisLayer } from './'; describe('utils', () => { // TODO: redo / update this test suite when eligibility is finalized. @@ -129,4 +133,67 @@ describe('utils', () => { expect(str).toEqual(`fn-1 arg1="value-1"\n| fn-2 arg2="value-2"`); }); }); + + describe('getAnyErrors', () => { + const noErrorLayer1 = generateVisLayer(VisLayerTypes.PointInTimeEvents, false); + const noErrorLayer2 = generateVisLayer(VisLayerTypes.PointInTimeEvents, false); + const errorLayer1 = generateVisLayer(VisLayerTypes.PointInTimeEvents, true, 'uh-oh!', { + type: 'resource-type-1', + id: '1234', + name: 'resource-1', + }); + const errorLayer2 = generateVisLayer( + VisLayerTypes.PointInTimeEvents, + true, + 'oh no something terrible has happened :(', + { + type: 'resource-type-2', + id: '5678', + name: 'resource-2', + } + ); + const errorLayer3 = generateVisLayer(VisLayerTypes.PointInTimeEvents, true, 'oops!', { + type: 'resource-type-1', + id: 'abcd', + name: 'resource-3', + }); + + it('empty array - returns undefined', async () => { + const err = getAnyErrors([], 'title-vis-title'); + expect(err).toEqual(undefined); + }); + it('single VisLayer no errors - returns undefined', async () => { + const err = getAnyErrors([noErrorLayer1], 'test-vis-title'); + expect(err).toEqual(undefined); + }); + it('multiple VisLayers no errors - returns undefined', async () => { + const err = getAnyErrors([noErrorLayer1, noErrorLayer2], 'test-vis-title'); + expect(err).toEqual(undefined); + }); + it('single VisLayer with error - returns formatted error', async () => { + const err = getAnyErrors([errorLayer1], 'test-vis-title'); + expect(err).not.toEqual(undefined); + expect(err?.stack).toStrictEqual(`-----resource-type-1-----\nID: 1234\nMessage: "uh-oh!"`); + }); + it('multiple VisLayers with errors - returns formatted error', async () => { + const err = getAnyErrors([errorLayer1, errorLayer2], 'test-vis-title'); + expect(err).not.toEqual(undefined); + expect(err?.stack).toStrictEqual( + `-----resource-type-1-----\nID: 1234\nMessage: "uh-oh!"\n\n\n` + + `-----resource-type-2-----\nID: 5678\nMessage: "oh no something terrible has happened :("` + ); + }); + it('multiple VisLayers with errors of same type - returns formatted error', async () => { + const err = getAnyErrors([errorLayer1, errorLayer3], 'test-vis-title'); + expect(err).not.toEqual(undefined); + expect(err?.stack).toStrictEqual( + `-----resource-type-1-----\nID: 1234\nMessage: "uh-oh!"\n\n` + `ID: abcd\nMessage: "oops!"` + ); + }); + it('VisLayers with and without error - returns formatted error', async () => { + const err = getAnyErrors([noErrorLayer1, errorLayer1], 'test-vis-title'); + expect(err).not.toEqual(undefined); + expect(err?.stack).toStrictEqual(`-----resource-type-1-----\nID: 1234\nMessage: "uh-oh!"`); + }); + }); }); diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index c0d357d517e9..096b5769b20f 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { get } from 'lodash'; +import { get, isEmpty } from 'lodash'; import { Vis } from '../../../../plugins/visualizations/public'; import { formatExpression, @@ -11,7 +11,13 @@ import { buildExpression, ExpressionAstFunctionBuilder, } from '../../../../plugins/expressions/public'; -import { ISavedAugmentVis, SavedAugmentVisLoader, VisLayerFunctionDefinition } from '../'; +import { + ISavedAugmentVis, + SavedAugmentVisLoader, + VisLayerFunctionDefinition, + VisLayer, + isVisLayerWithError, +} from '../'; // TODO: provide a deeper eligibility check. // Tracked in https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 @@ -58,3 +64,38 @@ export const buildPipelineFromAugmentVisSavedObjs = (objs: ISavedAugmentVis[]): throw new Error('Expression function from augment-vis saved objects could not be generated'); } }; + +/** + * Returns an error with an aggregated message about all of the + * errors found in the set of VisLayers. If no errors, returns undefined. + */ +export const getAnyErrors = (visLayers: VisLayer[], visTitle: string): Error | undefined => { + const visLayersWithErrors = visLayers.filter((visLayer) => isVisLayerWithError(visLayer)); + if (!isEmpty(visLayersWithErrors)) { + // Aggregate by unique plugin resource type + const resourceTypes = [ + ...new Set(visLayersWithErrors.map((visLayer) => visLayer.pluginResource.type)), + ]; + + let msgDetails = ''; + resourceTypes.forEach((type, index) => { + const matchingVisLayers = visLayersWithErrors.filter( + (visLayer) => visLayer.pluginResource.type === type + ); + if (index !== 0) msgDetails += '\n\n\n'; + msgDetails += `-----${type}-----`; + matchingVisLayers.forEach((visLayer, idx) => { + if (idx !== 0) msgDetails += '\n'; + msgDetails += `\nID: ${visLayer.pluginResource.id}`; + msgDetails += `\nMessage: "${visLayer.error?.message}"`; + }); + }); + + const err = new Error(`Certain plugin resources failed to load on the ${visTitle} chart`); + // We set as the stack here so it can be parsed and shown cleanly in the details modal coming from the error toast notification. + err.stack = msgDetails; + return err; + } else { + return undefined; + } +}; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index c0d43cd521da..c313fe5ae3b9 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -57,7 +57,7 @@ import { } from '../../../expressions/public'; import { buildPipeline } from '../legacy/build_pipeline'; import { Vis, SerializedVis } from '../vis'; -import { getExpressions, getUiActions } from '../services'; +import { getExpressions, getNotifications, getUiActions } from '../services'; import { VIS_EVENT_TO_TRIGGER } from './events'; import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { TriggerId } from '../../../ui_actions/public'; @@ -71,6 +71,7 @@ import { isEligibleForVisLayers, getAugmentVisSavedObjs, buildPipelineFromAugmentVisSavedObjs, + getAnyErrors, } from '../../../vis_augmenter/public'; import { VisSavedObject } from '../types'; @@ -511,13 +512,27 @@ export class VisualizeEmbeddable layers: [] as VisLayers, }; // We cannot use this.handler in this case, since it does not support the run() cmd - // we need here. So, we consume the expressions service to run this instead. + // we need here. So, we consume the expressions service to run this directly instead. const exprVisLayers = (await getExpressions().run( visLayersPipeline, visLayersPipelineInput, expressionParams as Record<string, unknown> )) as ExprVisLayers; - return exprVisLayers.layers; + const visLayers = exprVisLayers.layers; + const err = getAnyErrors(visLayers, this.vis.title); + // This is only true when one or more VisLayers has an error + if (err !== undefined) { + const { toasts } = getNotifications(); + toasts.addError(err, { + title: i18n.translate('visualizations.renderVisTitle', { + defaultMessage: `Error loading data on the ${this.vis.title} chart`, + }), + toastMessage: ' ', + id: this.id, + }); + } + + return visLayers; } return [] as VisLayers; }; diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index ec0fc098189d..07b40066018c 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -37,6 +37,7 @@ import { Plugin, ApplicationStart, SavedObjectsClientContract, + NotificationsStart, } from '../../../core/public'; import { TypesService, TypesSetup, TypesStart } from './vis_types'; import { @@ -61,6 +62,7 @@ import { setOverlays, setSavedSearchLoader, setEmbeddable, + setNotifications, } from './services'; import { VISUALIZE_EMBEDDABLE_TYPE, @@ -130,6 +132,7 @@ export interface VisualizationsStartDeps { dashboard: DashboardStart; getAttributeService: DashboardStart['getAttributeService']; savedObjectsClient: SavedObjectsClientContract; + notifications: NotificationsStart; } /** @@ -220,6 +223,7 @@ export class VisualizationsPlugin }); setSavedAugmentVisLoader(savedAugmentVisLoader); setSavedSearchLoader(savedSearchLoader); + setNotifications(core.notifications); return { ...types, showNewVisModal, diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index 1ad0b37d76a4..92c01703ca17 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -37,6 +37,7 @@ import { IUiSettingsClient, OverlayStart, SavedObjectsStart, + NotificationsStart, } from '../../../core/public'; import { TypesStart } from './vis_types'; import { createGetterSetter } from '../../opensearch_dashboards_utils/common'; @@ -111,3 +112,7 @@ export const [getSavedSearchLoader, setSavedSearchLoader] = createGetterSetter<S export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< SavedAugmentVisLoader >('SavedAugmentVisLoader'); + +export const [getNotifications, setNotifications] = createGetterSetter<NotificationsStart>( + 'Notifications' +); From 609bcc1882d8105f167482c9467a766242b439d1 Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Wed, 10 May 2023 14:28:42 -0700 Subject: [PATCH 13/37] Finalize eligibility check for augmenting visualizations by vis augmenter (#3687) * Add elibility checker for visualizations that can allow vis_augmenter to augment Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --------- Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --- src/plugins/vis_augmenter/public/index.ts | 1 + .../vis_augmenter/public/test_constants.ts | 33 +++ .../vis_augmenter/public/utils/utils.test.ts | 259 +++++++++++++++++- .../vis_augmenter/public/utils/utils.ts | 22 +- .../__snapshots__/helpers.test.js.snap | 4 +- .../public/expressions/helpers.test.js | 45 ++- .../public/expressions/helpers.ts | 31 ++- .../opensearch_dashboards.json | 2 +- .../public/line_to_expression.ts | 14 +- 9 files changed, 383 insertions(+), 28 deletions(-) diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index bef66db8fd02..78657ba6c224 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -29,3 +29,4 @@ export * from './utils'; export * from './constants'; export * from './vega'; export * from './saved_augment_vis'; +export * from './test_constants'; diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index 6bdaa7a66d78..d8c4386d0f69 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -7,7 +7,9 @@ import { OpenSearchDashboardsDatatable } from '../../expressions/public'; import { VIS_LAYER_COLUMN_TYPE, VisLayerTypes, HOVER_PARAM } from './'; const TEST_X_AXIS_ID = 'test-x-axis-id'; +const TEST_X_AXIS_ID_DIRTY = 'test.x.axis.id'; const TEST_VALUE_AXIS_ID = 'test-value-axis-id'; +const TEST_VALUE_AXIS_ID_DIRTY = 'test.value.axis.id'; const TEST_X_AXIS_TITLE = 'time'; const TEST_VALUE_AXIS_TITLE = 'avg value'; const TEST_PLUGIN = 'test-plugin'; @@ -53,6 +55,20 @@ const TEST_VALUES_NO_VIS_LAYERS = [ { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5 }, ]; +const TEST_VALUES_NO_VIS_LAYERS_DIRTY = [ + { [TEST_X_AXIS_ID_DIRTY]: 0, [TEST_VALUE_AXIS_ID_DIRTY]: 5 }, + { [TEST_X_AXIS_ID_DIRTY]: 5, [TEST_VALUE_AXIS_ID_DIRTY]: 10 }, + { [TEST_X_AXIS_ID_DIRTY]: 10, [TEST_VALUE_AXIS_ID_DIRTY]: 6 }, + { [TEST_X_AXIS_ID_DIRTY]: 15, [TEST_VALUE_AXIS_ID_DIRTY]: 4 }, + { [TEST_X_AXIS_ID_DIRTY]: 20, [TEST_VALUE_AXIS_ID_DIRTY]: 5 }, + { [TEST_X_AXIS_ID_DIRTY]: 25 }, + { [TEST_X_AXIS_ID_DIRTY]: 30 }, + { [TEST_X_AXIS_ID_DIRTY]: 35 }, + { [TEST_X_AXIS_ID_DIRTY]: 40 }, + { [TEST_X_AXIS_ID_DIRTY]: 45, [TEST_VALUE_AXIS_ID_DIRTY]: 3 }, + { [TEST_X_AXIS_ID_DIRTY]: 50, [TEST_VALUE_AXIS_ID_DIRTY]: 5 }, +]; + const TEST_VALUES_SINGLE_VIS_LAYER = [ { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_RESOURCE_ID]: 2 }, @@ -111,6 +127,17 @@ export const TEST_COLUMNS_NO_VIS_LAYERS = [ }, ]; +export const TEST_COLUMNS_NO_VIS_LAYERS_DIRTY = [ + { + id: TEST_X_AXIS_ID_DIRTY, + name: TEST_X_AXIS_TITLE, + }, + { + id: TEST_VALUE_AXIS_ID_DIRTY, + name: TEST_VALUE_AXIS_TITLE, + }, +]; + export const TEST_COLUMNS_SINGLE_VIS_LAYER = [ ...TEST_COLUMNS_NO_VIS_LAYERS, { @@ -157,6 +184,12 @@ export const TEST_DATATABLE_NO_VIS_LAYERS = { rows: TEST_VALUES_NO_VIS_LAYERS, } as OpenSearchDashboardsDatatable; +export const TEST_DATATABLE_NO_VIS_LAYERS_DIRTY = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_NO_VIS_LAYERS_DIRTY, + rows: TEST_VALUES_NO_VIS_LAYERS_DIRTY, +} as OpenSearchDashboardsDatatable; + export const TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY = { ...TEST_DATATABLE_NO_VIS_LAYERS, columns: TEST_COLUMNS_SINGLE_VIS_LAYER, diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index 0820d6e9c362..c37e87194c97 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -9,37 +9,278 @@ import { getAugmentVisSavedObjs, getAnyErrors, isEligibleForVisLayers, -} from './utils'; -import { createSavedAugmentVisLoader, SavedObjectOpenSearchDashboardsServicesWithAugmentVis, getMockAugmentVisSavedObjectClient, generateAugmentVisSavedObject, ISavedAugmentVis, - VisLayerExpressionFn, + generateVisLayer, VisLayerTypes, + VisLayerExpressionFn, } from '../'; -import { generateVisLayer } from './'; +import { AggConfigs, AggTypesRegistryStart, IndexPattern } from '../../../data/common'; +import { mockAggTypesRegistry } from '../../../data/common/search/aggs/test_helpers'; describe('utils', () => { - // TODO: redo / update this test suite when eligibility is finalized. - // Tracked in https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 describe('isEligibleForVisLayers', () => { - it('vis is ineligible with invalid type', async () => { + const validConfigStates = [ + { + enabled: true, + type: 'max', + params: {}, + schema: 'metric', + }, + { + enabled: true, + type: 'date_histogram', + params: {}, + schema: 'segment', + }, + ]; + const stubIndexPatternWithFields = { + id: '1234', + title: 'logstash-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], + }; + const typesRegistry: AggTypesRegistryStart = mockAggTypesRegistry(); + const aggs = new AggConfigs(stubIndexPatternWithFields as IndexPattern, validConfigStates, { + typesRegistry, + }); + const validVis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + it('vis is ineligible with invalid non-line type', async () => { const vis = ({ params: { type: 'not-line', + seriesParams: [], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, }, } as unknown) as Vis; expect(isEligibleForVisLayers(vis)).toEqual(false); }); - it('vis is eligible with valid type', async () => { + it('vis is ineligible with no date_histogram', async () => { + const invalidConfigStates = [ + { + enabled: true, + type: 'histogram', + params: {}, + }, + { + enabled: true, + type: 'metrics', + params: {}, + }, + ]; + const invalidAggs = new AggConfigs( + stubIndexPatternWithFields as IndexPattern, + invalidConfigStates, + { + typesRegistry, + } + ); + const vis = ({ + params: { + type: 'line', + seriesParams: [], + }, + data: { + invalidAggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with invalid aggs counts', async () => { + const invalidConfigStates = [ + ...validConfigStates, + { + enabled: true, + type: 'dot', + params: {}, + schema: 'radius', + }, + ]; + const invalidAggs = new AggConfigs( + stubIndexPatternWithFields as IndexPattern, + invalidConfigStates, + { + typesRegistry, + } + ); const vis = ({ params: { type: 'line', + seriesParams: [], + }, + data: { + invalidAggs, }, } as unknown) as Vis; - expect(isEligibleForVisLayers(vis)).toEqual(true); + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with no metric aggs', async () => { + const invalidConfigStates = [ + { + enabled: true, + type: 'date_histogram', + params: {}, + }, + ]; + const invalidAggs = new AggConfigs( + stubIndexPatternWithFields as IndexPattern, + invalidConfigStates, + { + typesRegistry, + } + ); + const vis = ({ + params: { + type: 'line', + seriesParams: [], + }, + data: { + invalidAggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with series param is not line type', async () => { + const vis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'area', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with series param not all being line type', async () => { + const vis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'area', + }, + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with invalid x-axis due to no segment aggregation', async () => { + const badConfigStates = [ + { + enabled: true, + type: 'max', + params: {}, + schema: 'metric', + }, + { + enabled: true, + type: 'max', + params: {}, + schema: 'metric', + }, + ]; + const badAggs = new AggConfigs(stubIndexPatternWithFields as IndexPattern, badConfigStates, { + typesRegistry, + }); + const invalidVis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + badAggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(invalidVis)).toEqual(false); + }); + it('vis is ineligible with xaxis not on bottom', async () => { + const invalidVis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'top', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(invalidVis)).toEqual(false); + }); + it('vis is eligible with valid type', async () => { + expect(isEligibleForVisLayers(validVis)).toEqual(true); }); }); diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index 096b5769b20f..0ec4ad486383 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -19,10 +19,26 @@ import { isVisLayerWithError, } from '../'; -// TODO: provide a deeper eligibility check. -// Tracked in https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 export const isEligibleForVisLayers = (vis: Vis): boolean => { - return vis.params.type === 'line'; + // Only support date histogram and ensure there is only 1 x-axis and it has to be on the bottom. + // Additionally to have a valid x-axis, there needs to be a segment aggregation + const hasValidXaxis = + vis.data.aggs !== undefined && + vis.data.aggs?.byTypeName('date_histogram').length === 1 && + vis.params.categoryAxes.length === 1 && + vis.params.categoryAxes[0].position === 'bottom' && + vis.data.aggs?.bySchemaName('segment').length > 0; + // Support 1 segment for x axis bucket (that is date_histogram) and support metrics for + // multiple supported yaxis only. If there are other aggregation types, this is not + // valid for augmentation + const hasCorrectAggregationCount = + vis.data.aggs !== undefined && + vis.data.aggs?.bySchemaName('metric').length > 0 && + vis.data.aggs?.bySchemaName('metric').length === vis.data.aggs?.aggs.length - 1; + const hasOnlyLineSeries = + vis.params.seriesParams.every((seriesParam: { type: string }) => seriesParam.type === 'line') && + vis.params.type === 'line'; + return hasValidXaxis && hasCorrectAggregationCount && hasOnlyLineSeries; }; /** diff --git a/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap index 69af7eba10b3..7515b07fb9d2 100644 --- a/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap +++ b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`helpers createSpecFromDatatable() build complicated line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44,\\"col-2-3\\":60.9375},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154,\\"col-2-3\\":79.5},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144,\\"col-2-3\\":75.875},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133,\\"col-2-3\\":259.25},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149,\\"col-2-3\\":90},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152,\\"col-2-3\\":79.0625},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166,\\"col-2-3\\":85.25},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151,\\"col-2-3\\":92},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143,\\"col-2-3\\":90.75},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148,\\"col-2-3\\":92},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146,\\"col-2-3\\":83.25},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137,\\"col-2-3\\":98},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151,\\"col-2-3\\":87.4375},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157,\\"col-2-3\\":63.75},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151,\\"col-2-3\\":81.5625},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152,\\"col-2-3\\":100.6875},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142,\\"col-2-3\\":98},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151,\\"col-2-3\\":100.8125},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156,\\"col-2-3\\":85.8125},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153,\\"col-2-3\\":98},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162,\\"col-2-3\\":75.9375},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159,\\"col-2-3\\":73.625},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165,\\"col-2-3\\":72.8125},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149,\\"col-2-3\\":82.5},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94,\\"col-2-3\\":54}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"bottom\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":\\"ValueAxis-1\\",\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}},{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":\\"ValueAxis-1\\",\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Max products.min_price\\"}],\\"color\\":{\\"datum\\":\\"Max products.min_price\\"}}},{\\"mark\\":\\"rule\\",\\"encoding\\":{\\"x\\":{\\"type\\":\\"temporal\\",\\"field\\":\\"now_field\\"},\\"color\\":{\\"value\\":\\"red\\"},\\"size\\":{\\"value\\":1}}},{\\"mark\\":{\\"type\\":\\"rule\\",\\"color\\":\\"#E7664C\\",\\"strokeDash\\":[8,8]},\\"encoding\\":{\\"y\\":{\\"datum\\":100}}}],\\"transform\\":[{\\"calculate\\":\\"now()\\",\\"as\\":\\"now_field\\"}]}"`; +exports[`helpers createSpecFromDatatable() build complicated line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44,\\"col-2-3\\":60.9375},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154,\\"col-2-3\\":79.5},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144,\\"col-2-3\\":75.875},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133,\\"col-2-3\\":259.25},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149,\\"col-2-3\\":90},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152,\\"col-2-3\\":79.0625},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166,\\"col-2-3\\":85.25},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151,\\"col-2-3\\":92},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143,\\"col-2-3\\":90.75},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148,\\"col-2-3\\":92},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146,\\"col-2-3\\":83.25},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137,\\"col-2-3\\":98},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151,\\"col-2-3\\":87.4375},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157,\\"col-2-3\\":63.75},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151,\\"col-2-3\\":81.5625},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152,\\"col-2-3\\":100.6875},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142,\\"col-2-3\\":98},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151,\\"col-2-3\\":100.8125},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156,\\"col-2-3\\":85.8125},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153,\\"col-2-3\\":98},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162,\\"col-2-3\\":75.9375},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159,\\"col-2-3\\":73.625},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165,\\"col-2-3\\":72.8125},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149,\\"col-2-3\\":82.5},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94,\\"col-2-3\\":54}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"bottom\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}},{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Max products.min_price\\"}],\\"color\\":{\\"datum\\":\\"Max products.min_price\\"}}},{\\"mark\\":\\"rule\\",\\"encoding\\":{\\"x\\":{\\"type\\":\\"temporal\\",\\"field\\":\\"now_field\\"},\\"color\\":{\\"value\\":\\"red\\"},\\"size\\":{\\"value\\":1}}},{\\"mark\\":{\\"type\\":\\"rule\\",\\"color\\":\\"#E7664C\\",\\"strokeDash\\":[8,8]},\\"encoding\\":{\\"y\\":{\\"datum\\":100}}}],\\"transform\\":[{\\"calculate\\":\\"now()\\",\\"as\\":\\"now_field\\"}]}"`; exports[`helpers createSpecFromDatatable() build empty chart if no x-axis is defined" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[]}"`; -exports[`helpers createSpecFromDatatable() build simple line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"orient\\":\\"left\\",\\"labels\\":true,\\"labelAngle\\":0},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}}]}"`; +exports[`helpers createSpecFromDatatable() build simple line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":false,\\"orient\\":\\"left\\",\\"labels\\":true,\\"labelAngle\\":0},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}}]}"`; diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.test.js b/src/plugins/vis_type_vega/public/expressions/helpers.test.js index f09b1b8db3d6..08e347dca175 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.test.js +++ b/src/plugins/vis_type_vega/public/expressions/helpers.test.js @@ -21,10 +21,25 @@ import { simpleDimensions, noXAxisDimensions, } from './__mocks__'; +import { + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DATATABLE_NO_VIS_LAYERS_DIRTY, +} from '../../../vis_augmenter/public'; describe('helpers', function () { + describe('formatDatatable()', function () { + it('formatSimpleDatatable', function () { + expect(formatDatatable(TEST_DATATABLE_NO_VIS_LAYERS)).toBe(TEST_DATATABLE_NO_VIS_LAYERS); + }); + it('formatDirtyDatatable', function () { + expect(formatDatatable(TEST_DATATABLE_NO_VIS_LAYERS_DIRTY)).toStrictEqual( + TEST_DATATABLE_NO_VIS_LAYERS + ); + }); + }); + describe('cleanString()', function () { - it('string should not contain "', function () { + it('string should not contain quotation marks', function () { const dirtyString = '"someString"'; expect(cleanString(dirtyString)).toBe('someString'); }); @@ -141,6 +156,34 @@ describe('helpers', function () { vegaYAxis.axis.title = 'columnName'; expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); }); + it('build YAxis with percentile rank', function () { + const valueAxis = { + id: 'someId', + labels: { + rotate: 75, + show: false, + }, + position: 'left', + title: { + text: 'someText', + }, + }; + const column = { name: 'columnName', id: 'columnId', meta: { type: 'percentile_ranks' } }; + const visParams = { grid: { valueAxis: true } }; + const vegaYAxis = { + axis: { + title: 'someText', + grid: true, + orient: 'left', + labels: false, + labelAngle: 75, + format: '.0%', + }, + field: 'columnId', + type: 'quantitative', + }; + expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); + }); }); describe('createSpecFromDatatable()', function () { diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.ts b/src/plugins/vis_type_vega/public/expressions/helpers.ts index ca31367bf119..e904ccb1af87 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.ts +++ b/src/plugins/vis_type_vega/public/expressions/helpers.ts @@ -6,6 +6,7 @@ import { OpenSearchDashboardsDatatable, OpenSearchDashboardsDatatableColumn, + OpenSearchDashboardsDatatableRow, } from '../../../expressions/public'; import { VislibDimensions, VisParams } from '../../../visualizations/public'; import { isVisLayerColumn } from '../../../vis_augmenter/public'; @@ -49,7 +50,20 @@ export const formatDatatable = ( datatable.columns.forEach((column) => { // clean quotation marks from names in columns column.name = cleanString(column.name); + // clean ids to remove "." as that will cause vega to not process it correctly. + // This happens for different metric types + column.id = column.id.replaceAll('.', '-'); }); + + // clean row keys to remove "." as that will cause vega to not process it correctly + const updatedRows: OpenSearchDashboardsDatatableRow[] = datatable.rows.map((row) => + Object.entries(row).reduce((updatedRow, [key, value]) => { + const cleanKey = key.replaceAll('.', '-'); + return Object.assign(updatedRow, { [cleanKey]: value }); + }, {}) + ); + + datatable.rows = updatedRows; return datatable; }; @@ -111,14 +125,17 @@ export const buildYAxis = ( valueAxis: ValueAxis, visParams: VisParams ) => { + const subAxis = { + title: cleanString(valueAxis.title.text) || column.name, + grid: visParams.grid.valueAxis !== undefined, + orient: valueAxis.position, + labels: valueAxis.labels.show, + labelAngle: valueAxis.labels.rotate, + }; + // Percentile ranks aggregation metric needs percentile formatting. + if (column.meta?.type === 'percentile_ranks') Object.assign(subAxis, { format: '.0%' }); return { - axis: { - title: cleanString(valueAxis.title.text) || column.name, - grid: visParams.grid.valueAxis, - orient: valueAxis.position, - labels: valueAxis.labels.show, - labelAngle: valueAxis.labels.rotate, - }, + axis: subAxis, field: column.id, type: 'quantitative', }; diff --git a/src/plugins/vis_type_vislib/opensearch_dashboards.json b/src/plugins/vis_type_vislib/opensearch_dashboards.json index 0cef84e463f6..c498d121e99d 100644 --- a/src/plugins/vis_type_vislib/opensearch_dashboards.json +++ b/src/plugins/vis_type_vislib/opensearch_dashboards.json @@ -5,5 +5,5 @@ "ui": true, "requiredPlugins": ["charts", "data", "expressions", "visualizations", "opensearchDashboardsLegacy", "visTypeVega"], "optionalPlugins": ["visTypeXy"], - "requiredBundles": ["opensearchDashboardsUtils", "visDefaultEditor"] + "requiredBundles": ["opensearchDashboardsUtils", "visDefaultEditor", "visAugmenter"] } diff --git a/src/plugins/vis_type_vislib/public/line_to_expression.ts b/src/plugins/vis_type_vislib/public/line_to_expression.ts index 2648e70af38d..79a716f2e800 100644 --- a/src/plugins/vis_type_vislib/public/line_to_expression.ts +++ b/src/plugins/vis_type_vislib/public/line_to_expression.ts @@ -3,13 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { buildVislibDimensions, Vis } from '../../visualizations/public'; +import { buildVislibDimensions, Vis, VislibDimensions } from '../../visualizations/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import { OpenSearchaggsExpressionFunctionDefinition } from '../../data/common/search/expressions'; import { VegaExpressionFunctionDefinition, LineVegaSpecExpressionFunctionDefinition, } from '../../vis_type_vega/public'; +import { isEligibleForVisLayers } from '../../vis_augmenter/public'; export const toExpressionAst = async (vis: Vis, params: any) => { // Construct the existing expr fns that are ran for vislib line chart, up until the render fn. @@ -26,9 +27,13 @@ export const toExpressionAst = async (vis: Vis, params: any) => { ); // Checks if there are vislayers to overlay. If not, default to the vislib implementation. - if (params.visLayers == null || Object.keys(params.visLayers).length === 0) { - // This wont work but is needed so then it will default to the original vis lib renderer - const dimensions = await buildVislibDimensions(vis, params); + const dimensions: VislibDimensions = await buildVislibDimensions(vis, params); + if ( + params.visLayers == null || + Object.keys(params.visLayers).length === 0 || + !isEligibleForVisLayers(vis) + ) { + // Render using vislib instead of vega-lite const visConfig = { ...vis.params, dimensions }; const vislib = buildExpressionFunction<any>('vislib', { type: 'line', @@ -37,7 +42,6 @@ export const toExpressionAst = async (vis: Vis, params: any) => { const ast = buildExpression([opensearchaggsFn, vislib]); return ast.toAst(); } else { - const dimensions = await buildVislibDimensions(vis, params); // adding the new expr fn here that takes the datatable and converts to a vega spec const vegaSpecFn = buildExpressionFunction<LineVegaSpecExpressionFunctionDefinition>( 'line_vega_spec', From 2cc88f35827b9d6a4921941b3f7560a499558f6e Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 17 May 2023 15:12:11 -0700 Subject: [PATCH 14/37] Add a `VisAugmenter` stats API (#4006) * Update augment-vis saved obj mappings Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Finish implementation; add tests; update attributes Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * remove log Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../augment_vis_saved_object_attributes.ts | 32 +++ src/plugins/vis_augmenter/common/constants.ts | 12 ++ src/plugins/vis_augmenter/common/index.ts | 7 + .../saved_augment_vis/_saved_augment_vis.ts | 21 +- .../saved_augment_vis.test.ts | 96 ++++++++- .../saved_augment_vis/saved_augment_vis.ts | 21 +- .../saved_augment_vis_references.test.ts | 2 +- .../saved_augment_vis_references.ts | 5 +- .../public/saved_augment_vis/types.ts | 8 +- .../public/saved_augment_vis/utils/helpers.ts | 2 +- .../saved_augment_vis/utils/test_helpers.ts | 10 +- .../vis_augmenter/public/utils/utils.test.ts | 41 +++- src/plugins/vis_augmenter/server/plugin.ts | 6 + .../vis_augmenter/server/routes/stats.ts | 56 +++++ .../server/routes/stats_helpers.test.ts | 203 ++++++++++++++++++ .../server/routes/stats_helpers.ts | 91 ++++++++ .../server/saved_objects/augment_vis.ts | 8 +- 17 files changed, 582 insertions(+), 39 deletions(-) create mode 100644 src/plugins/vis_augmenter/common/augment_vis_saved_object_attributes.ts create mode 100644 src/plugins/vis_augmenter/common/constants.ts create mode 100644 src/plugins/vis_augmenter/common/index.ts create mode 100644 src/plugins/vis_augmenter/server/routes/stats.ts create mode 100644 src/plugins/vis_augmenter/server/routes/stats_helpers.test.ts create mode 100644 src/plugins/vis_augmenter/server/routes/stats_helpers.ts diff --git a/src/plugins/vis_augmenter/common/augment_vis_saved_object_attributes.ts b/src/plugins/vis_augmenter/common/augment_vis_saved_object_attributes.ts new file mode 100644 index 000000000000..824f35c6e112 --- /dev/null +++ b/src/plugins/vis_augmenter/common/augment_vis_saved_object_attributes.ts @@ -0,0 +1,32 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectAttributes } from 'opensearch-dashboards/server'; + +export interface AugmentVisSavedObjectAttributes extends SavedObjectAttributes { + id: string; + title: string; + description?: string; + originPlugin: string; + pluginResource: { + type: string; + id: string; + }; + visLayerExpressionFn: { + type: string; + name: string; + }; + version: number; + // Following fields are optional since they will get set/removed during the extraction/injection + // of the vis reference + visName?: string; + visId?: string; + visReference?: { + id: string; + name: string; + }; + // Error may be populated if there is some issue when parsing the attribute values + error?: string; +} diff --git a/src/plugins/vis_augmenter/common/constants.ts b/src/plugins/vis_augmenter/common/constants.ts new file mode 100644 index 000000000000..7ce517aba77e --- /dev/null +++ b/src/plugins/vis_augmenter/common/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const APP_PATH = { + STATS: '/stats', +}; +export const APP_API = '/api/vis_augmenter'; + +// used for limiting results received from the stats API +export const PER_PAGE_REQUEST_NUMBER = 50; diff --git a/src/plugins/vis_augmenter/common/index.ts b/src/plugins/vis_augmenter/common/index.ts new file mode 100644 index 000000000000..9762ce68770e --- /dev/null +++ b/src/plugins/vis_augmenter/common/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './constants'; +export { AugmentVisSavedObjectAttributes } from './augment_vis_saved_object_attributes'; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts index ffaa64e92304..88178a7a08cd 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts @@ -14,8 +14,8 @@ import { SavedObject, SavedObjectOpenSearchDashboardsServices, } from '../../../saved_objects/public'; -import { IIndexPattern } from '../../../data/public'; import { extractReferences, injectReferences } from './saved_augment_vis_references'; +import { AugmentVisSavedObjectAttributes } from '../../common'; const name = 'augment-vis'; @@ -24,28 +24,19 @@ export function createSavedAugmentVisClass(services: SavedObjectOpenSearchDashbo class SavedAugmentVis extends SavedObjectClass { public static type: string = name; - public static mapping: Record<string, string> = { - description: 'text', - pluginResourceId: 'text', - visId: 'keyword', - visLayerExpressionFn: 'text', - version: 'integer', - }; + public static mapping: AugmentVisSavedObjectAttributes; - constructor(opts: Record<string, unknown> | string = {}) { - if (typeof opts !== 'object') { - opts = { id: opts }; - } + constructor(opts: AugmentVisSavedObjectAttributes) { super({ type: SavedAugmentVis.type, mapping: SavedAugmentVis.mapping, extractReferences, injectReferences, id: (opts.id as string) || '', - indexPattern: opts.indexPattern as IIndexPattern, defaults: { description: get(opts, 'description', ''), - pluginResourceId: get(opts, 'pluginResourceId', ''), + originPlugin: get(opts, 'originPlugin', ''), + pluginResource: get(opts, 'pluginResource', {}), visId: get(opts, 'visId', ''), visLayerExpressionFn: get(opts, 'visLayerExpressionFn', {}), version: 1, @@ -55,5 +46,5 @@ export function createSavedAugmentVisClass(services: SavedObjectOpenSearchDashbo } } - return SavedAugmentVis as new (opts: Record<string, unknown> | string) => SavedObject; + return SavedAugmentVis as new (opts: AugmentVisSavedObjectAttributes) => SavedObject; } diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts index 51360f72c331..7e2aabe25d69 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts @@ -3,12 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { VisLayerExpressionFn, VisLayerTypes } from '../types'; +import { VisLayerTypes } from '../types'; +import { VisLayerExpressionFn } from '../expressions'; import { createSavedAugmentVisLoader, SavedObjectOpenSearchDashboardsServicesWithAugmentVis, } from './saved_augment_vis'; import { generateAugmentVisSavedObject, getMockAugmentVisSavedObjectClient } from './utils'; +import { ISavedPluginResource } from './types'; describe('SavedObjectLoaderAugmentVis', () => { const fn = { @@ -18,8 +20,25 @@ describe('SavedObjectLoaderAugmentVis', () => { testArg: 'test-value', }, } as VisLayerExpressionFn; - const validObj1 = generateAugmentVisSavedObject('valid-obj-id-1', fn, 'test-vis-id'); - const validObj2 = generateAugmentVisSavedObject('valid-obj-id-2', fn, 'test-vis-id'); + const originPlugin = 'test-plugin'; + const pluginResource = { + type: 'test-plugin', + id: 'test-plugin-resource-id', + }; + const validObj1 = generateAugmentVisSavedObject( + 'valid-obj-id-1', + fn, + 'test-vis-id', + originPlugin, + pluginResource + ); + const validObj2 = generateAugmentVisSavedObject( + 'valid-obj-id-2', + fn, + 'test-vis-id', + originPlugin, + pluginResource + ); const invalidFnTypeObj = generateAugmentVisSavedObject( 'invalid-fn-obj-id-1', { @@ -27,13 +46,48 @@ describe('SavedObjectLoaderAugmentVis', () => { // @ts-ignore type: 'invalid-type', }, - 'test-vis-id' + 'test-vis-id', + originPlugin, + pluginResource ); const missingFnObj = generateAugmentVisSavedObject( 'missing-fn-obj-id-1', {} as VisLayerExpressionFn, - 'test-vis-id' + 'test-vis-id', + originPlugin, + pluginResource + ); + + const missingOriginPluginObj = generateAugmentVisSavedObject( + 'missing-origin-plugin-obj-id-1', + fn, + 'test-vis-id', + // @ts-ignore + undefined, + pluginResource + ); + + const missingPluginResourceTypeObj = generateAugmentVisSavedObject( + 'missing-plugin-resource-type-obj-id-1', + fn, + 'test-vis-id', + // @ts-ignore + originPlugin, + { + id: pluginResource.id, + } as ISavedPluginResource + ); + + const missingPluginResourceIdObj = generateAugmentVisSavedObject( + 'missing-plugin-resource-id-obj-id-1', + fn, + 'test-vis-id', + // @ts-ignore + originPlugin, + { + type: pluginResource.type, + } as ISavedPluginResource ); it('find returns single saved obj', async () => { @@ -105,4 +159,36 @@ describe('SavedObjectLoaderAugmentVis', () => { expect(resp.hits[0].id).toEqual('valid-obj-id-1'); expect(resp.hits[0].error).toEqual('visReference is missing in augment-vis saved object'); }); + + it('findAll returns obj with missing originPlugin', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([missingOriginPluginObj]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.findAll(); + expect(resp.hits.length).toEqual(1); + expect(resp.hits[0].id).toEqual('missing-origin-plugin-obj-id-1'); + expect(resp.hits[0].error).toEqual('originPlugin is missing in augment-vis saved object'); + }); + + it('findAll returns obj with missing plugin resource type', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([missingPluginResourceTypeObj]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.findAll(); + expect(resp.hits.length).toEqual(1); + expect(resp.hits[0].id).toEqual('missing-plugin-resource-type-obj-id-1'); + expect(resp.hits[0].error).toEqual( + 'pluginResource.type is missing in augment-vis saved object' + ); + }); + + it('findAll returns obj with missing plugin resource id', async () => { + const loader = createSavedAugmentVisLoader({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([missingPluginResourceIdObj]), + } as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + const resp = await loader.findAll(); + expect(resp.hits.length).toEqual(1); + expect(resp.hits[0].id).toEqual('missing-plugin-resource-id-obj-id-1'); + expect(resp.hits[0].error).toEqual('pluginResource.id is missing in augment-vis saved object'); + }); }); diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts index 910ef0b9ea75..5c76b3cec421 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts @@ -10,6 +10,7 @@ import { } from '../../../saved_objects/public'; import { createSavedAugmentVisClass } from './_saved_augment_vis'; import { VisLayerTypes } from '../types'; +import { AugmentVisSavedObjectAttributes } from '../../common'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SavedObjectOpenSearchDashboardsServicesWithAugmentVis @@ -21,9 +22,9 @@ export function createSavedAugmentVisLoader( const { savedObjectsClient } = services; class SavedObjectLoaderAugmentVis extends SavedObjectLoader { - mapHitSource = (source: Record<string, any>, id: string) => { + mapHitSource = (source: AugmentVisSavedObjectAttributes, id: string) => { source.id = id; - source.visId = get(source, 'visReference.id', ''); + source.visId = get(source, 'visReference.id', '') as string; if (isEmpty(source.visReference)) { source.error = 'visReference is missing in augment-vis saved object'; @@ -33,10 +34,22 @@ export function createSavedAugmentVisLoader( source.error = 'visLayerExpressionFn is missing in augment-vis saved object'; return source; } - if (!(get(source, 'visLayerExpressionFn.type', '') in VisLayerTypes)) { + if (!((get(source, 'visLayerExpressionFn.type', '') as string) in VisLayerTypes)) { source.error = 'Unknown VisLayer expression function type'; return source; } + if (get(source, 'originPlugin', undefined) === undefined) { + source.error = 'originPlugin is missing in augment-vis saved object'; + return source; + } + if (get(source, 'pluginResource.type', undefined) === undefined) { + source.error = 'pluginResource.type is missing in augment-vis saved object'; + return source; + } + if (get(source, 'pluginResource.id', undefined) === undefined) { + source.error = 'pluginResource.id is missing in augment-vis saved object'; + return source; + } return source; }; @@ -48,7 +61,7 @@ export function createSavedAugmentVisLoader( */ mapSavedObjectApiHits(hit: { references: any[]; - attributes: Record<string, unknown>; + attributes: AugmentVisSavedObjectAttributes; id: string; }) { // For now we are assuming only one vis reference per saved object. diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts index 1b5a0ad6cd98..dd7aef79d9ad 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.test.ts @@ -8,7 +8,7 @@ import { injectReferences, VIS_REFERENCE_NAME, } from './saved_augment_vis_references'; -import { AugmentVisSavedObject } from '../types'; +import { AugmentVisSavedObject } from './types'; describe('extractReferences()', () => { test('extracts nothing if visId is null', () => { diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts index 5b2cc3f3d0e5..7a915f93745e 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis_references.ts @@ -4,7 +4,8 @@ */ import { SavedObjectAttributes, SavedObjectReference } from '../../../../core/public'; -import { AugmentVisSavedObject } from '../types'; +import { AugmentVisSavedObjectAttributes } from '../../common'; +import { AugmentVisSavedObject } from './types'; /** * Note that references aren't stored in the object's client-side interface (AugmentVisSavedObject). @@ -35,7 +36,7 @@ export function extractReferences({ attributes: SavedObjectAttributes; references: SavedObjectReference[]; }) { - const updatedAttributes = { ...attributes }; + const updatedAttributes = { ...attributes } as AugmentVisSavedObjectAttributes; const updatedReferences = [...references]; // Extract saved object diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts index dee349cb9001..c2e5b8b19008 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts @@ -6,11 +6,17 @@ import { SavedObject } from '../../../saved_objects/public'; import { VisLayerExpressionFn } from '../expressions'; +export interface ISavedPluginResource { + type: string; + id: string; +} + export interface ISavedAugmentVis { id?: string; title: string; description?: string; - pluginResourceId: string; + originPlugin: string; + pluginResource: ISavedPluginResource; visName?: string; visId?: string; visLayerExpressionFn: VisLayerExpressionFn; diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts index c3a54a377317..6c984e16d4d7 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts @@ -4,7 +4,7 @@ */ import { getSavedAugmentVisLoader } from '../../services'; -import { ISavedAugmentVis } from '../../types'; +import { ISavedAugmentVis } from '../types'; /** * Create an augment vis saved object given an object that diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts index 38e80646ed15..c162fd3e0a6c 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts @@ -4,22 +4,24 @@ */ import { cloneDeep } from 'lodash'; -import { VisLayerExpressionFn, ISavedAugmentVis } from '../../'; +import { VisLayerExpressionFn, ISavedAugmentVis, ISavedPluginResource } from '../../'; import { VIS_REFERENCE_NAME } from '../saved_augment_vis_references'; -const pluginResourceId = 'test-plugin-resource-id'; const title = 'test-title'; const version = 1; export const generateAugmentVisSavedObject = ( idArg: string, exprFnArg: VisLayerExpressionFn, - visIdArg: string + visIdArg: string, + originPluginArg: string, + pluginResourceArg: ISavedPluginResource ) => { return { id: idArg, title, - pluginResourceId, + originPlugin: originPluginArg, + pluginResource: pluginResourceArg, visLayerExpressionFn: exprFnArg, VIS_REFERENCE_NAME, visId: visIdArg, diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index c37e87194c97..6e55fd8a11fd 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -292,12 +292,35 @@ describe('utils', () => { testArg: 'test-value', }, } as VisLayerExpressionFn; + const originPlugin = 'test-plugin'; + const pluginResource = { + type: 'test-plugin', + id: 'test-plugin-resource-id', + }; const visId1 = 'test-vis-id-1'; const visId2 = 'test-vis-id-2'; const visId3 = 'test-vis-id-3'; - const obj1 = generateAugmentVisSavedObject('valid-obj-id-1', fn, visId1); - const obj2 = generateAugmentVisSavedObject('valid-obj-id-2', fn, visId1); - const obj3 = generateAugmentVisSavedObject('valid-obj-id-3', fn, visId2); + const obj1 = generateAugmentVisSavedObject( + 'valid-obj-id-1', + fn, + visId1, + originPlugin, + pluginResource + ); + const obj2 = generateAugmentVisSavedObject( + 'valid-obj-id-2', + fn, + visId1, + originPlugin, + pluginResource + ); + const obj3 = generateAugmentVisSavedObject( + 'valid-obj-id-3', + fn, + visId2, + originPlugin, + pluginResource + ); it('returns no matching saved objs with filtering', async () => { const loader = createSavedAugmentVisLoader({ @@ -334,7 +357,11 @@ describe('utils', () => { describe('buildPipelineFromAugmentVisSavedObjs', () => { const obj1 = { title: 'obj1', - pluginResourceId: 'obj-1-resource-id', + originPlugin: 'test-plugin', + pluginResource: { + type: 'test-resource-type', + id: 'obj-1-resource-id', + }, visLayerExpressionFn: { type: VisLayerTypes.PointInTimeEvents, name: 'fn-1', @@ -345,7 +372,11 @@ describe('utils', () => { } as ISavedAugmentVis; const obj2 = { title: 'obj2', - pluginResourceId: 'obj-2-resource-id', + originPlugin: 'test-plugin', + pluginResource: { + type: 'test-resource-type', + id: 'obj-2-resource-id', + }, visLayerExpressionFn: { type: VisLayerTypes.PointInTimeEvents, name: 'fn-2', diff --git a/src/plugins/vis_augmenter/server/plugin.ts b/src/plugins/vis_augmenter/server/plugin.ts index f30cf6c974fe..f6b41646c536 100644 --- a/src/plugins/vis_augmenter/server/plugin.ts +++ b/src/plugins/vis_augmenter/server/plugin.ts @@ -12,6 +12,7 @@ import { } from '../../../core/server'; import { augmentVisSavedObjectType } from './saved_objects'; import { capabilitiesProvider } from './capabilities_provider'; +import { registerStatsRoute } from './routes/stats'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface VisAugmenterPluginSetup {} @@ -30,6 +31,11 @@ export class VisAugmenterPlugin this.logger.debug('VisAugmenter: Setup'); core.savedObjects.registerType(augmentVisSavedObjectType); core.capabilities.registerProvider(capabilitiesProvider); + + // Register server-side APIs + const router = core.http.createRouter(); + registerStatsRoute(router, this.logger); + return {}; } diff --git a/src/plugins/vis_augmenter/server/routes/stats.ts b/src/plugins/vis_augmenter/server/routes/stats.ts new file mode 100644 index 000000000000..e8c4ae76bf24 --- /dev/null +++ b/src/plugins/vis_augmenter/server/routes/stats.ts @@ -0,0 +1,56 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Logger } from '@osd/logging'; +import { ResponseError } from '@opensearch-project/opensearch/lib/errors'; +import { + IOpenSearchDashboardsResponse, + IRouter, + SavedObjectsFindResponse, +} from '../../../../core/server'; +import { + APP_API, + APP_PATH, + PER_PAGE_REQUEST_NUMBER, + AugmentVisSavedObjectAttributes, +} from '../../common'; +import { getAugmentVisSavedObjects, getStats } from './stats_helpers'; + +export const registerStatsRoute = (router: IRouter, logger: Logger) => { + router.get( + { + path: `${APP_API}${APP_PATH.STATS}`, + validate: {}, + }, + async ( + context, + request, + response + ): Promise<IOpenSearchDashboardsResponse<any | ResponseError>> => { + try { + const savedObjectsClient = context.core.savedObjects.client; + const augmentVisSavedObjects: SavedObjectsFindResponse<AugmentVisSavedObjectAttributes> = await getAugmentVisSavedObjects( + savedObjectsClient, + PER_PAGE_REQUEST_NUMBER + ); + const stats = getStats(augmentVisSavedObjects); + return response.ok({ + body: stats, + }); + } catch (error: any) { + logger.error(error); + return response.customError({ + statusCode: error.statusCode || 500, + body: { + message: error.message, + attributes: { + error: error.body?.error || error.message, + }, + }, + }); + } + } + ); +}; diff --git a/src/plugins/vis_augmenter/server/routes/stats_helpers.test.ts b/src/plugins/vis_augmenter/server/routes/stats_helpers.test.ts new file mode 100644 index 000000000000..96f561c49e05 --- /dev/null +++ b/src/plugins/vis_augmenter/server/routes/stats_helpers.test.ts @@ -0,0 +1,203 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectsFindResult } from '../../../../../src/core/server'; +import { AugmentVisSavedObjectAttributes } from '../../common'; +import { getAugmentVisSavedObjects, getStats } from './stats_helpers'; + +const ORIGIN_PLUGIN_1 = 'origin-plugin-1'; +const ORIGIN_PLUGIN_2 = 'origin-plugin-2'; +const PLUGIN_RESOURCE_TYPE_1 = 'plugin-resource-type-1'; +const PLUGIN_RESOURCE_TYPE_2 = 'plugin-resource-type-2'; +const PLUGIN_RESOURCE_ID_1 = 'plugin-resource-id-1'; +const PLUGIN_RESOURCE_ID_2 = 'plugin-resource-id-2'; +const PLUGIN_RESOURCE_ID_3 = 'plugin-resource-id-3'; +const VIS_ID_1 = 'vis-id-1'; +const VIS_ID_2 = 'vis-id-2'; +const PER_PAGE = 4; + +const SINGLE_SAVED_OBJ = [ + { + attributes: { + originPlugin: ORIGIN_PLUGIN_1, + pluginResource: { + type: PLUGIN_RESOURCE_TYPE_1, + id: PLUGIN_RESOURCE_ID_1, + }, + visId: VIS_ID_1, + }, + }, +] as Array<SavedObjectsFindResult<AugmentVisSavedObjectAttributes>>; + +const MULTIPLE_SAVED_OBJS = [ + { + attributes: { + originPlugin: ORIGIN_PLUGIN_1, + pluginResource: { + type: PLUGIN_RESOURCE_TYPE_1, + id: PLUGIN_RESOURCE_ID_1, + }, + visId: VIS_ID_1, + }, + }, + { + attributes: { + originPlugin: ORIGIN_PLUGIN_2, + pluginResource: { + type: PLUGIN_RESOURCE_TYPE_2, + id: PLUGIN_RESOURCE_ID_2, + }, + visId: VIS_ID_1, + }, + }, + { + attributes: { + originPlugin: ORIGIN_PLUGIN_2, + pluginResource: { + type: PLUGIN_RESOURCE_TYPE_2, + id: PLUGIN_RESOURCE_ID_2, + }, + visId: VIS_ID_2, + }, + }, + { + attributes: { + originPlugin: ORIGIN_PLUGIN_2, + pluginResource: { + type: PLUGIN_RESOURCE_TYPE_2, + id: PLUGIN_RESOURCE_ID_3, + }, + visId: VIS_ID_1, + }, + }, +] as Array<SavedObjectsFindResult<AugmentVisSavedObjectAttributes>>; + +describe('getAugmentVisSavedObjs()', function () { + const mockClient = { + find: jest.fn(), + }; + it('should return empty arr if no objs found', async function () { + mockClient.find.mockResolvedValueOnce({ + total: 0, + page: 1, + per_page: PER_PAGE, + saved_objects: [], + }); + + // @ts-ignore + const response = await getAugmentVisSavedObjects(mockClient, PER_PAGE); + expect(response.total).toEqual(0); + expect(response.saved_objects).toHaveLength(0); + }); + + it('should return all augment-vis saved objects', async function () { + mockClient.find.mockResolvedValueOnce({ + total: 4, + page: 1, + per_page: PER_PAGE, + saved_objects: MULTIPLE_SAVED_OBJS, + }); + + // @ts-ignore + const response = await getAugmentVisSavedObjects(mockClient, PER_PAGE); + expect(response.total).toEqual(4); + expect(response.saved_objects).toHaveLength(4); + expect(response.saved_objects[0].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_1); + expect(response.saved_objects[1].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_2); + expect(response.saved_objects[2].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_2); + expect(response.saved_objects[3].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_2); + }); + + it('should correctly perform pagination', async function () { + mockClient.find + .mockResolvedValueOnce({ + total: 5, + page: 1, + per_page: PER_PAGE, + saved_objects: MULTIPLE_SAVED_OBJS, + }) + .mockResolvedValueOnce({ + total: 5, + page: 2, + per_page: PER_PAGE, + saved_objects: SINGLE_SAVED_OBJ, + }); + + // @ts-ignore + const response = await getAugmentVisSavedObjects(mockClient, PER_PAGE); + expect(response.total).toEqual(5); + expect(response.saved_objects).toHaveLength(5); + expect(response.saved_objects[0].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_1); + expect(response.saved_objects[1].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_2); + expect(response.saved_objects[2].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_2); + expect(response.saved_objects[3].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_2); + expect(response.saved_objects[4].attributes.originPlugin).toEqual(ORIGIN_PLUGIN_1); + }); +}); + +describe('getStats()', function () { + it('should return total of 0 and empty mappings on empty response', function () { + const response = getStats({ + total: 0, + page: 1, + per_page: PER_PAGE, + saved_objects: [], + }); + expect(response.total_objs).toEqual(0); + expect(response.obj_breakdown.origin_plugin).toEqual({}); + expect(response.obj_breakdown.plugin_resource_type).toEqual({}); + expect(response.obj_breakdown.plugin_resource_id).toEqual({}); + expect(response.obj_breakdown.visualization_id).toEqual({}); + }); + + it('should return correct count and mappings on single-obj response', function () { + const response = getStats({ + total: 1, + page: 1, + per_page: PER_PAGE, + saved_objects: SINGLE_SAVED_OBJ, + }); + expect(response.total_objs).toEqual(1); + expect(response.obj_breakdown.origin_plugin).toEqual({ + [ORIGIN_PLUGIN_1]: 1, + }); + expect(response.obj_breakdown.plugin_resource_type).toEqual({ + [PLUGIN_RESOURCE_TYPE_1]: 1, + }); + expect(response.obj_breakdown.plugin_resource_id).toEqual({ + [PLUGIN_RESOURCE_ID_1]: 1, + }); + expect(response.obj_breakdown.visualization_id).toEqual({ + [VIS_ID_1]: 1, + }); + }); + + it('should return correct count and mappings on multiple-obj response', function () { + const response = getStats({ + total: MULTIPLE_SAVED_OBJS.length, + page: 1, + per_page: PER_PAGE, + saved_objects: MULTIPLE_SAVED_OBJS, + }); + expect(response.total_objs).toEqual(4); + expect(response.obj_breakdown.origin_plugin).toEqual({ + [ORIGIN_PLUGIN_1]: 1, + [ORIGIN_PLUGIN_2]: 3, + }); + expect(response.obj_breakdown.plugin_resource_type).toEqual({ + [PLUGIN_RESOURCE_TYPE_1]: 1, + [PLUGIN_RESOURCE_TYPE_2]: 3, + }); + expect(response.obj_breakdown.plugin_resource_id).toEqual({ + [PLUGIN_RESOURCE_ID_1]: 1, + [PLUGIN_RESOURCE_ID_2]: 2, + [PLUGIN_RESOURCE_ID_3]: 1, + }); + expect(response.obj_breakdown.visualization_id).toEqual({ + [VIS_ID_1]: 3, + [VIS_ID_2]: 1, + }); + }); +}); diff --git a/src/plugins/vis_augmenter/server/routes/stats_helpers.ts b/src/plugins/vis_augmenter/server/routes/stats_helpers.ts new file mode 100644 index 000000000000..33e73ec47306 --- /dev/null +++ b/src/plugins/vis_augmenter/server/routes/stats_helpers.ts @@ -0,0 +1,91 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get } from 'lodash'; +import { + SavedObjectsFindResponse, + SavedObjectsClientContract, +} from '../../../../../src/core/server'; +import { AugmentVisSavedObjectAttributes } from '../../common'; + +interface ObjectBreakdown { + origin_plugin: { [key: string]: number }; + plugin_resource_type: { [key: string]: number }; + plugin_resource_id: { [key: string]: number }; + visualization_id: { [key: string]: number }; +} + +interface VisAugmenterStats { + total_objs: number; + obj_breakdown: ObjectBreakdown; +} + +export const getAugmentVisSavedObjects = async ( + savedObjectsClient: SavedObjectsClientContract, + perPage: number +): Promise<SavedObjectsFindResponse<AugmentVisSavedObjectAttributes>> => { + const augmentVisSavedObjects: SavedObjectsFindResponse<AugmentVisSavedObjectAttributes> = await savedObjectsClient?.find( + { + type: 'augment-vis', + perPage, + } + ); + // If there are more than perPage of objs, we need to make additional requests + if (augmentVisSavedObjects.total > perPage) { + const iterations = Math.ceil(augmentVisSavedObjects.total / perPage); + for (let i = 1; i < iterations; i++) { + const augmentVisSavedObjectsPage: SavedObjectsFindResponse<AugmentVisSavedObjectAttributes> = await savedObjectsClient?.find( + { + type: 'augment-vis', + perPage, + page: i + 1, + } + ); + augmentVisSavedObjects.saved_objects = [ + ...augmentVisSavedObjects.saved_objects, + ...augmentVisSavedObjectsPage.saved_objects, + ]; + } + } + return augmentVisSavedObjects; +}; + +/** + * Given the _find response that contains all of the saved objects, iterate through them and + * increment counters for each unique value we are tracking + */ +export const getStats = ( + resp: SavedObjectsFindResponse<AugmentVisSavedObjectAttributes> +): VisAugmenterStats => { + const originPluginMap = {} as { [originPlugin: string]: number }; + const pluginResourceTypeMap = {} as { [pluginResourceType: string]: number }; + const pluginResourceIdMap = {} as { [pluginResourceId: string]: number }; + const visualizationIdMap = {} as { [visualizationId: string]: number }; + + resp.saved_objects.forEach((augmentVisObj) => { + const originPlugin = augmentVisObj.attributes.originPlugin; + const pluginResourceType = augmentVisObj.attributes.pluginResource.type; + const pluginResourceId = augmentVisObj.attributes.pluginResource.id; + const visualizationId = augmentVisObj.attributes.visId as string; + + originPluginMap[originPlugin] = (get(originPluginMap, originPlugin, 0) as number) + 1; + pluginResourceTypeMap[pluginResourceType] = + (get(pluginResourceTypeMap, pluginResourceType, 0) as number) + 1; + pluginResourceIdMap[pluginResourceId] = + (get(pluginResourceIdMap, pluginResourceId, 0) as number) + 1; + visualizationIdMap[visualizationId] = + (get(visualizationIdMap, visualizationId, 0) as number) + 1; + }); + + return { + total_objs: resp.total, + obj_breakdown: { + origin_plugin: originPluginMap, + plugin_resource_type: pluginResourceTypeMap, + plugin_resource_id: pluginResourceIdMap, + visualization_id: visualizationIdMap, + }, + }; +}; diff --git a/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts b/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts index 0efe98fc14ce..1202e6168f77 100644 --- a/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts +++ b/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts @@ -24,7 +24,13 @@ export const augmentVisSavedObjectType: SavedObjectsType = { properties: { title: { type: 'text' }, description: { type: 'text' }, - pluginResourceId: { type: 'text' }, + originPlugin: { type: 'text' }, + pluginResource: { + properties: { + type: { type: 'text' }, + id: { type: 'text' }, + }, + }, visName: { type: 'keyword', index: false, doc_values: false }, visLayerExpressionFn: { properties: { From e7e4b2abb81b2a6cabecdd5f3482b68ac9a1ff9d Mon Sep 17 00:00:00 2001 From: Amardeepsingh Siglani <amardeep7194@gmail.com> Date: Mon, 22 May 2023 14:58:25 -0700 Subject: [PATCH 15/37] [Feature anywhere] Add annotation click handler (#3777) * added interaction handling * added framework for invoking flyout, tooltip * using signals to listen for events on point annotation Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com> --- src/plugins/ui_actions/public/index.ts | 2 + .../triggers/external_action_trigger.ts | 44 +++++++++++++++++ .../ui_actions/public/triggers/index.ts | 1 + .../vis_augmenter/public/test_constants.ts | 8 +++- .../vis_augmenter/public/vega/constants.ts | 8 ++++ .../vis_augmenter/public/vega/helpers.ts | 38 ++++++++++++++- .../vis_type_vega/public/data_model/types.ts | 2 + .../public/data_model/vega_parser.ts | 47 +++++++++++++++++++ .../public/expressions/line_vega_spec_fn.ts | 2 + .../public/vega_view/vega_base_view.js | 14 ++++++ .../public/vega_visualization.js | 1 + .../public/embeddable/events.ts | 3 ++ .../visualizations/public/expressions/vis.ts | 5 ++ 13 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 src/plugins/ui_actions/public/triggers/external_action_trigger.ts create mode 100644 src/plugins/vis_augmenter/public/vega/constants.ts diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 3560f473d33b..489cb5eee363 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -61,6 +61,8 @@ export { visualizeFieldTrigger, VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldTrigger, + EXTERNAL_ACTION_TRIGGER, + externalActionTrigger, } from './triggers'; export { TriggerContextMapping, diff --git a/src/plugins/ui_actions/public/triggers/external_action_trigger.ts b/src/plugins/ui_actions/public/triggers/external_action_trigger.ts new file mode 100644 index 000000000000..87941fd47b88 --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/external_action_trigger.ts @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@osd/i18n'; +import { Trigger } from '.'; + +export const EXTERNAL_ACTION_TRIGGER = 'EXTERNAL_ACTION_TRIGGER'; +export const externalActionTrigger: Trigger<'EXTERNAL_ACTION_TRIGGER'> = { + id: EXTERNAL_ACTION_TRIGGER, + title: i18n.translate('uiActions.triggers.externalActionTitle', { + defaultMessage: 'Single click', + }), + description: i18n.translate('uiActions.triggers.externalActionDescription', { + defaultMessage: + 'A data point click on the visualization used to trigger external action like show flyout, etc.', + }), +}; diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts index 2d012df76e08..0fcbbc4ee3fa 100644 --- a/src/plugins/ui_actions/public/triggers/index.ts +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -36,4 +36,5 @@ export * from './value_click_trigger'; export * from './apply_filter_trigger'; export * from './visualize_field_trigger'; export * from './visualize_geo_field_trigger'; +export * from './external_action_trigger'; export * from './default_trigger'; diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index d8c4386d0f69..0e45fdc05725 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -5,6 +5,7 @@ import { OpenSearchDashboardsDatatable } from '../../expressions/public'; import { VIS_LAYER_COLUMN_TYPE, VisLayerTypes, HOVER_PARAM } from './'; +import { VisAnnotationType } from './vega/constants'; const TEST_X_AXIS_ID = 'test-x-axis-id'; const TEST_X_AXIS_ID_DIRTY = 'test.x.axis.id'; @@ -489,8 +490,12 @@ const TEST_EVENTS_LAYER_SINGLE_VIS_LAYER = { color: 'red', filled: true, opacity: 1, + style: [`${VisAnnotationType.POINT_IN_TIME_ANNOTATION}`], }, - transform: [{ filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0` }], + transform: [ + { filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0` }, + { calculate: `'${VisAnnotationType.POINT_IN_TIME_ANNOTATION}'`, as: 'annotationType' }, + ], params: [{ name: HOVER_PARAM, select: { type: 'point', on: 'mouseover' } }], encoding: { x: { @@ -536,6 +541,7 @@ const TEST_EVENTS_LAYER_MULTIPLE_VIS_LAYERS = { { filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0 || datum['${TEST_PLUGIN_RESOURCE_ID_2}'] > 0`, }, + { calculate: `'${VisAnnotationType.POINT_IN_TIME_ANNOTATION}'`, as: 'annotationType' }, ], }; diff --git a/src/plugins/vis_augmenter/public/vega/constants.ts b/src/plugins/vis_augmenter/public/vega/constants.ts new file mode 100644 index 000000000000..a5729725422e --- /dev/null +++ b/src/plugins/vis_augmenter/public/vega/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export enum VisAnnotationType { + POINT_IN_TIME_ANNOTATION = 'POINT_IN_TIME_ANNOTATION', +} diff --git a/src/plugins/vis_augmenter/public/vega/helpers.ts b/src/plugins/vis_augmenter/public/vega/helpers.ts index 2132e170bb4d..8eaa11f4c0c2 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.ts @@ -5,6 +5,7 @@ import moment from 'moment'; import { cloneDeep, isEmpty, get } from 'lodash'; +import { Item } from 'vega'; import { OpenSearchDashboardsDatatable, OpenSearchDashboardsDatatableColumn, @@ -24,6 +25,7 @@ import { VisLayers, VisLayerTypes, } from '../'; +import { VisAnnotationType } from './constants'; // Given any visLayers, create a map to indicate which VisLayer types are present. // Convert to an array since ES6 Maps cannot be stringified. @@ -48,6 +50,30 @@ export const enableVisLayersInSpecConfig = (spec: object, visLayers: VisLayers): }; }; +/** + * Adds the signals which vega will use to trigger required events on the point in time annotation marks + */ +export const addVisEventSignalsToSpecConfig = (spec: object) => { + const config = get(spec, 'config', { kibana: {} }); + const signals = { + ...(config.kibana.signals || {}), + [`${VisAnnotationType.POINT_IN_TIME_ANNOTATION}`]: [ + { + name: 'PointInTimeAnnotationVisEvent', + on: [{ events: 'click', update: 'opensearchDashboardsVisEventTriggered(event, datum)' }], + }, + ], + }; + + return { + ...config, + kibana: { + ...config.kibana, + signals, + }, + }; +}; + // Get the first xaxis field as only 1 setup of X Axis will be supported and // there won't be support for split series and split chart export const getXAxisId = ( @@ -313,8 +339,14 @@ export const addPointInTimeEventsLayersToSpec = ( color: EVENT_COLOR, filled: true, opacity: 1, + // This style is only used to locate this mark when trying to add signals in the compiled vega spec. + // @see @method vega_parser._compileVegaLite + style: [`${VisAnnotationType.POINT_IN_TIME_ANNOTATION}`], }, - transform: [{ filter: generateVisLayerFilterString(visLayerColumnIds) }], + transform: [ + { filter: generateVisLayerFilterString(visLayerColumnIds) }, + { calculate: `'${VisAnnotationType.POINT_IN_TIME_ANNOTATION}'`, as: 'annotationType' }, + ], params: [{ name: HOVER_PARAM, select: { type: 'point', on: 'mouseover' } }], encoding: { x: { @@ -340,3 +372,7 @@ export const addPointInTimeEventsLayersToSpec = ( return newSpec; }; + +export const isPointInTimeAnnotation = (item?: Item | null) => { + return item?.datum?.annotationType === VisAnnotationType.POINT_IN_TIME_ANNOTATION; +}; diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index b5db91558c03..3947808c72c1 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -33,6 +33,7 @@ import { SearchResponse, SearchParams } from 'elasticsearch'; import { Filter } from 'src/plugins/data/public'; import { DslQuery } from 'src/plugins/data/common'; import { VisLayerTypes } from 'src/plugins/vis_augmenter/public'; +import { Signal } from 'vega'; import { OpenSearchQueryParser } from './opensearch_query_parser'; import { EmsFileParser } from './ems_file_parser'; import { UrlParser } from './url_parser'; @@ -115,6 +116,7 @@ export interface OpenSearchDashboards { type: string; renderer: Renderer; visibleVisLayers?: Map<VisLayerTypes, boolean>; + signals?: { [markId: string]: Signal[] }; } export interface VegaSpec { diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 187c4690aad5..8f473b70544b 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -36,6 +36,7 @@ import { euiPaletteColorBlind } from '@elastic/eui'; import { euiThemeVars } from '@osd/ui-shared-deps/theme'; import { i18n } from '@osd/i18n'; // @ts-ignore +import { Signal } from 'vega'; import { vega, vegaLite } from '../lib/vega'; import { OpenSearchQueryParser } from './opensearch_query_parser'; import { Utils } from './utils'; @@ -323,6 +324,52 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never delete this.spec.autosize; } } + + if (this._config?.signals) { + Object.entries(this._config?.signals).forEach(([markId, signals]: [string, any]) => { + const mark = this.getMarkWithStyle(this.spec.marks, markId); + + if (mark) { + signals.forEach((signal: Signal) => { + signal.on?.forEach((eventObj) => { + // We are prepending mark name here so that the signals only listens to the events on + // the elements related to this mark + eventObj.events = `@${mark.name}:${eventObj.events}`; + }); + }); + this.spec.signals = (this.spec.signals || []).concat(signals); + } + }); + } + } + + /** + * This method recursively looks for a mark that includes the given style. + * Returns undefined if it doesn't find it. + */ + getMarkWithStyle(marks: any[], style: string): any { + if (!marks) { + return undefined; + } + + if (Array.isArray(marks)) { + const markWithStyle = marks.find((mark) => { + return mark.style?.includes(style); + }); + + if (markWithStyle) { + return markWithStyle; + } + + for (let i = 0; i < marks.length; i++) { + const res = this.getMarkWithStyle(marks[i].marks, style); + if (res) { + return res; + } + } + + return undefined; + } } /** diff --git a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts index 499310c106d2..136ba12f8862 100644 --- a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts +++ b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts @@ -18,6 +18,7 @@ import { addPointInTimeEventsLayersToTable, addPointInTimeEventsLayersToSpec, enableVisLayersInSpecConfig, + addVisEventSignalsToSpecConfig, } from '../../../vis_augmenter/public'; import { formatDatatable, createSpecFromDatatable } from './helpers'; import { VegaVisualizationDependencies } from '../plugin'; @@ -85,6 +86,7 @@ export const createLineVegaSpecFn = ( if (!isEmpty(pointInTimeEventsVisLayers) && dimensions.x !== null) { spec = addPointInTimeEventsLayersToSpec(table, dimensions, spec); spec.config = enableVisLayersInSpecConfig(spec, pointInTimeEventsVisLayers); + spec.config = addVisEventSignalsToSpecConfig(spec); } return JSON.stringify(spec); }, diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 65843ff05162..7083a6896064 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -51,6 +51,7 @@ const vegaFunctions = { opensearchDashboardsRemoveFilter: 'removeFilterHandler', opensearchDashboardsRemoveAllFilters: 'removeAllFiltersHandler', opensearchDashboardsSetTimeFilter: 'setTimeFilterHandler', + opensearchDashboardsVisEventTriggered: 'triggerExternalActionHandler', }; for (const funcName of Object.keys(vegaFunctions)) { @@ -76,6 +77,7 @@ export class VegaBaseView { this._serviceSettings = opts.serviceSettings; this._filterManager = opts.filterManager; this._applyFilter = opts.applyFilter; + this._triggerExternalAction = opts.externalAction; this._timefilter = opts.timefilter; this._view = null; this._vegaViewConfig = null; @@ -343,6 +345,18 @@ export class VegaBaseView { this._applyFilter({ filters: [filter] }); } + /** + * This method is triggered using signal expression in vega-spec via @see opensearchDashboardsVisEventTriggered + * @param {import('vega').ScenegraphEvent} event Event triggered by the underlying vega visualization. + * @param {import('vega').Item} datum Data associated with the element on which the event was triggered. + */ + triggerExternalActionHandler(event, datum) { + this._triggerExternalAction({ + event, + item: datum, + }); + } + /** * @param {object} query Query DSL snippet, as used in the query DSL editor * @param {string} [index] as defined in OpenSearch Dashboards, or default if missing diff --git a/src/plugins/vis_type_vega/public/vega_visualization.js b/src/plugins/vis_type_vega/public/vega_visualization.js index af5c58f8a149..379670bda413 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.js +++ b/src/plugins/vis_type_vega/public/vega_visualization.js @@ -90,6 +90,7 @@ export const createVegaVisualization = ({ getServiceSettings }) => serviceSettings, filterManager, timefilter, + externalAction: this._vis.API.events.externalAction, }; if (vegaParser.useMap) { diff --git a/src/plugins/visualizations/public/embeddable/events.ts b/src/plugins/visualizations/public/embeddable/events.ts index 2a17ef9d5ef3..3d34cfe49959 100644 --- a/src/plugins/visualizations/public/embeddable/events.ts +++ b/src/plugins/visualizations/public/embeddable/events.ts @@ -32,16 +32,19 @@ import { APPLY_FILTER_TRIGGER, SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER, + EXTERNAL_ACTION_TRIGGER, } from '../../../ui_actions/public'; export interface VisEventToTrigger { ['applyFilter']: typeof APPLY_FILTER_TRIGGER; ['brush']: typeof SELECT_RANGE_TRIGGER; ['filter']: typeof VALUE_CLICK_TRIGGER; + ['externalAction']: typeof EXTERNAL_ACTION_TRIGGER; } export const VIS_EVENT_TO_TRIGGER: VisEventToTrigger = { applyFilter: APPLY_FILTER_TRIGGER, brush: SELECT_RANGE_TRIGGER, filter: VALUE_CLICK_TRIGGER, + externalAction: EXTERNAL_ACTION_TRIGGER, }; diff --git a/src/plugins/visualizations/public/expressions/vis.ts b/src/plugins/visualizations/public/expressions/vis.ts index acf747973dee..02f13ab2ad8d 100644 --- a/src/plugins/visualizations/public/expressions/vis.ts +++ b/src/plugins/visualizations/public/expressions/vis.ts @@ -55,6 +55,7 @@ export interface ExprVisAPIEvents { filter: (data: any) => void; brush: (data: any) => void; applyFilter: (data: any) => void; + externalAction: (data: any) => void; } export interface ExprVisAPI { @@ -99,6 +100,10 @@ export class ExprVis extends EventEmitter { if (!this.eventsSubject) return; this.eventsSubject.next({ name: 'applyFilter', data }); }, + externalAction: (data: any) => { + if (!this.eventsSubject) return; + this.eventsSubject.next({ name: 'externalAction', data }); + }, }, }; } From 2f94a6dd87d53c61a83775d181e7d3e1df6971e0 Mon Sep 17 00:00:00 2001 From: Amardeepsingh Siglani <amardeep7194@gmail.com> Date: Tue, 23 May 2023 18:51:49 -0700 Subject: [PATCH 16/37] showing default tooltip on mark (#4120) Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com> --- src/plugins/vis_augmenter/public/constants.ts | 1 + src/plugins/vis_augmenter/public/vega/helpers.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/plugins/vis_augmenter/public/constants.ts b/src/plugins/vis_augmenter/public/constants.ts index 99b75a8de7ea..fe55a03d6c94 100644 --- a/src/plugins/vis_augmenter/public/constants.ts +++ b/src/plugins/vis_augmenter/public/constants.ts @@ -11,3 +11,4 @@ export const EVENT_MARK_SIZE = 100; export const EVENT_MARK_SIZE_ENLARGED = 140; export const EVENT_MARK_SHAPE = 'triangle-up'; export const EVENT_TIMELINE_HEIGHT = 25; +export const EVENT_TOOLTIP_CENTER_ON_MARK = 5; diff --git a/src/plugins/vis_augmenter/public/vega/helpers.ts b/src/plugins/vis_augmenter/public/vega/helpers.ts index 8eaa11f4c0c2..086f2c4f4ae2 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.ts @@ -24,6 +24,7 @@ import { VisLayer, VisLayers, VisLayerTypes, + EVENT_TOOLTIP_CENTER_ON_MARK, } from '../'; import { VisAnnotationType } from './constants'; @@ -70,6 +71,9 @@ export const addVisEventSignalsToSpecConfig = (spec: object) => { kibana: { ...config.kibana, signals, + tooltips: { + centerOnMark: EVENT_TOOLTIP_CENTER_ON_MARK, + }, }, }; }; @@ -339,6 +343,7 @@ export const addPointInTimeEventsLayersToSpec = ( color: EVENT_COLOR, filled: true, opacity: 1, + tooltip: true, // This style is only used to locate this mark when trying to add signals in the compiled vega spec. // @see @method vega_parser._compileVegaLite style: [`${VisAnnotationType.POINT_IN_TIME_ANNOTATION}`], From 570a1c4e23ba8aaad05722bca13aea7c6933ce5f Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 24 May 2023 08:56:59 -0700 Subject: [PATCH 17/37] Support formatted tooltip for events (#4123) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../vis_augmenter/public/test_constants.ts | 53 ++++++++++++------- src/plugins/vis_augmenter/public/types.ts | 2 +- .../vis_augmenter/public/vega/helpers.test.ts | 21 +++++++- .../vis_augmenter/public/vega/helpers.ts | 37 +++++++++---- 4 files changed, 80 insertions(+), 33 deletions(-) diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index 0e45fdc05725..61ced45b41ea 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -14,6 +14,8 @@ const TEST_VALUE_AXIS_ID_DIRTY = 'test.value.axis.id'; const TEST_X_AXIS_TITLE = 'time'; const TEST_VALUE_AXIS_TITLE = 'avg value'; const TEST_PLUGIN = 'test-plugin'; +const TEST_PLUGIN_EVENT_TYPE = 'test-plugin-event-type'; +const TEST_PLUGIN_EVENT_TYPE_2 = 'test-plugin-event-type-2'; const TEST_PLUGIN_RESOURCE_TYPE = 'test-resource-type'; const TEST_PLUGIN_RESOURCE_ID = 'test-resource-id'; const TEST_PLUGIN_RESOURCE_ID_2 = 'test-resource-id-2'; @@ -25,18 +27,18 @@ const TEST_PLUGIN_RESOURCE_PATH_2 = `${TEST_PLUGIN}/${TEST_PLUGIN_RESOURCE_TYPE} const TEST_VALUES_SINGLE_ROW_NO_VIS_LAYERS = [{ [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }]; const TEST_VALUES_SINGLE_ROW_SINGLE_VIS_LAYER = [ - { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 3 }, + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 3 }, ]; const TEST_VALUES_ONLY_VIS_LAYERS = [ { [TEST_X_AXIS_ID]: 0 }, - { [TEST_X_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 2 }, + { [TEST_X_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 2 }, { [TEST_X_AXIS_ID]: 10 }, { [TEST_X_AXIS_ID]: 15 }, { [TEST_X_AXIS_ID]: 20 }, { [TEST_X_AXIS_ID]: 25 }, { [TEST_X_AXIS_ID]: 30 }, - { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_RESOURCE_ID]: 1 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 1 }, { [TEST_X_AXIS_ID]: 40 }, { [TEST_X_AXIS_ID]: 45 }, { [TEST_X_AXIS_ID]: 50 }, @@ -72,20 +74,20 @@ const TEST_VALUES_NO_VIS_LAYERS_DIRTY = [ const TEST_VALUES_SINGLE_VIS_LAYER = [ { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, - { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_RESOURCE_ID]: 2 }, + { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_EVENT_TYPE]: 2 }, { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4 }, { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, { [TEST_X_AXIS_ID]: 25 }, { [TEST_X_AXIS_ID]: 30 }, - { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_RESOURCE_ID]: 1 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 1 }, { [TEST_X_AXIS_ID]: 40 }, { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5 }, ]; const TEST_VALUES_SINGLE_VIS_LAYER_ON_BOUNDS = [ - { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 2 }, + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 2 }, { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10 }, { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4 }, @@ -95,7 +97,7 @@ const TEST_VALUES_SINGLE_VIS_LAYER_ON_BOUNDS = [ { [TEST_X_AXIS_ID]: 35 }, { [TEST_X_AXIS_ID]: 40 }, { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, - { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID]: 1 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 1 }, ]; const TEST_VALUES_MULTIPLE_VIS_LAYERS = [ @@ -103,18 +105,18 @@ const TEST_VALUES_MULTIPLE_VIS_LAYERS = [ { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, - [TEST_PLUGIN_RESOURCE_ID]: 2, - [TEST_PLUGIN_RESOURCE_ID_2]: 1, + [TEST_PLUGIN_EVENT_TYPE]: 2, + [TEST_PLUGIN_EVENT_TYPE_2]: 1, }, { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, - { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4, [TEST_PLUGIN_RESOURCE_ID_2]: 1 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4, [TEST_PLUGIN_EVENT_TYPE_2]: 1 }, { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, { [TEST_X_AXIS_ID]: 25 }, { [TEST_X_AXIS_ID]: 30 }, - { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_RESOURCE_ID]: 1 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 1 }, { [TEST_X_AXIS_ID]: 40 }, { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, - { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_RESOURCE_ID_2]: 2 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE_2]: 2 }, ]; export const TEST_COLUMNS_NO_VIS_LAYERS = [ @@ -142,8 +144,8 @@ export const TEST_COLUMNS_NO_VIS_LAYERS_DIRTY = [ export const TEST_COLUMNS_SINGLE_VIS_LAYER = [ ...TEST_COLUMNS_NO_VIS_LAYERS, { - id: TEST_PLUGIN_RESOURCE_ID, - name: TEST_PLUGIN_RESOURCE_NAME, + id: TEST_PLUGIN_EVENT_TYPE, + name: `${TEST_PLUGIN_EVENT_TYPE} count`, meta: { type: VIS_LAYER_COLUMN_TYPE, }, @@ -153,8 +155,8 @@ export const TEST_COLUMNS_SINGLE_VIS_LAYER = [ export const TEST_COLUMNS_MULTIPLE_VIS_LAYERS = [ ...TEST_COLUMNS_SINGLE_VIS_LAYER, { - id: TEST_PLUGIN_RESOURCE_ID_2, - name: TEST_PLUGIN_RESOURCE_NAME_2, + id: TEST_PLUGIN_EVENT_TYPE_2, + name: `${TEST_PLUGIN_EVENT_TYPE_2} count`, meta: { type: VIS_LAYER_COLUMN_TYPE, }, @@ -322,6 +324,7 @@ export const TEST_VIS_LAYERS_SINGLE = [ name: TEST_PLUGIN_RESOURCE_NAME, urlPath: TEST_PLUGIN_RESOURCE_PATH, }, + pluginEventType: TEST_PLUGIN_EVENT_TYPE, events: [ { timestamp: 4, @@ -355,6 +358,7 @@ export const TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS = [ name: TEST_PLUGIN_RESOURCE_NAME, urlPath: TEST_PLUGIN_RESOURCE_PATH, }, + pluginEventType: TEST_PLUGIN_EVENT_TYPE, events: [ { timestamp: -5, @@ -388,6 +392,7 @@ export const TEST_VIS_LAYERS_SINGLE_EMPTY_EVENTS = [ name: TEST_PLUGIN_RESOURCE_NAME, urlPath: TEST_PLUGIN_RESOURCE_PATH, }, + pluginEventType: TEST_PLUGIN_EVENT_TYPE, }, ]; @@ -401,6 +406,7 @@ export const TEST_VIS_LAYERS_SINGLE_ON_BOUNDS = [ name: TEST_PLUGIN_RESOURCE_NAME, urlPath: TEST_PLUGIN_RESOURCE_PATH, }, + pluginEventType: TEST_PLUGIN_EVENT_TYPE, events: [ { timestamp: 0, @@ -435,6 +441,7 @@ export const TEST_VIS_LAYERS_MULTIPLE = [ name: TEST_PLUGIN_RESOURCE_NAME_2, urlPath: TEST_PLUGIN_RESOURCE_PATH_2, }, + pluginEventType: TEST_PLUGIN_EVENT_TYPE_2, events: [ { timestamp: 5, @@ -466,7 +473,7 @@ export const TEST_VIS_LAYERS_MULTIPLE = [ const TEST_RULE_LAYER_SINGLE_VIS_LAYER = { mark: { type: 'rule', color: 'red', opacity: 1 }, - transform: [{ filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0` }], + transform: [{ filter: `datum['${TEST_PLUGIN_EVENT_TYPE}'] > 0` }], encoding: { x: { field: TEST_X_AXIS_ID, type: 'temporal' }, opacity: { value: 0, condition: { empty: false, param: HOVER_PARAM, value: 1 } }, @@ -477,7 +484,7 @@ const TEST_RULE_LAYER_MULTIPLE_VIS_LAYERS = { ...TEST_RULE_LAYER_SINGLE_VIS_LAYER, transform: [ { - filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0 || datum['${TEST_PLUGIN_RESOURCE_ID_2}'] > 0`, + filter: `datum['${TEST_PLUGIN_EVENT_TYPE}'] > 0 || datum['${TEST_PLUGIN_EVENT_TYPE_2}'] > 0`, }, ], }; @@ -491,9 +498,10 @@ const TEST_EVENTS_LAYER_SINGLE_VIS_LAYER = { filled: true, opacity: 1, style: [`${VisAnnotationType.POINT_IN_TIME_ANNOTATION}`], + tooltip: true, }, transform: [ - { filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0` }, + { filter: `datum['${TEST_PLUGIN_EVENT_TYPE}'] > 0` }, { calculate: `'${VisAnnotationType.POINT_IN_TIME_ANNOTATION}'`, as: 'annotationType' }, ], params: [{ name: HOVER_PARAM, select: { type: 'point', on: 'mouseover' } }], @@ -532,6 +540,7 @@ const TEST_EVENTS_LAYER_SINGLE_VIS_LAYER = { }, }, size: { condition: { empty: false, param: HOVER_PARAM, value: 140 }, value: 100 }, + tooltip: [{ field: TEST_PLUGIN_EVENT_TYPE }], }, }; @@ -539,10 +548,14 @@ const TEST_EVENTS_LAYER_MULTIPLE_VIS_LAYERS = { ...TEST_EVENTS_LAYER_SINGLE_VIS_LAYER, transform: [ { - filter: `datum['${TEST_PLUGIN_RESOURCE_ID}'] > 0 || datum['${TEST_PLUGIN_RESOURCE_ID_2}'] > 0`, + filter: `datum['${TEST_PLUGIN_EVENT_TYPE}'] > 0 || datum['${TEST_PLUGIN_EVENT_TYPE_2}'] > 0`, }, { calculate: `'${VisAnnotationType.POINT_IN_TIME_ANNOTATION}'`, as: 'annotationType' }, ], + encoding: { + ...TEST_EVENTS_LAYER_SINGLE_VIS_LAYER.encoding, + tooltip: [{ field: TEST_PLUGIN_EVENT_TYPE }, { field: TEST_PLUGIN_EVENT_TYPE_2 }], + }, }; export const TEST_RESULT_SPEC_SINGLE_VIS_LAYER = { diff --git a/src/plugins/vis_augmenter/public/types.ts b/src/plugins/vis_augmenter/public/types.ts index d7a75c12d125..6c63ccae3144 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -37,7 +37,6 @@ export type VisLayers = VisLayer[]; export interface EventMetadata { pluginResourceId: string; - tooltip?: string; } export interface PointInTimeEvent { @@ -47,6 +46,7 @@ export interface PointInTimeEvent { export interface PointInTimeEventsVisLayer extends VisLayer { events: PointInTimeEvent[]; + pluginEventType: string; } export const isPointInTimeEventsVisLayer = (obj: any) => { diff --git a/src/plugins/vis_augmenter/public/vega/helpers.test.ts b/src/plugins/vis_augmenter/public/vega/helpers.test.ts index 3f0145dc16d0..f0df25a81546 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.test.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.test.ts @@ -15,6 +15,7 @@ import { addMissingRowsToTableBounds, addPointInTimeEventsLayersToTable, addPointInTimeEventsLayersToSpec, + generateVisLayerTooltipFields, } from './helpers'; import { VIS_LAYER_COLUMN_TYPE, VisLayerTypes, PointInTimeEventsVisLayer, VisLayer } from '../'; import { @@ -178,6 +179,24 @@ describe('helpers', function () { }); }); + describe('generateVisLayerTooltipFields()', function () { + it('empty array returns empty', function () { + const visLayerColumnIds = [] as string[]; + const tooltipFields = [] as Array<{ field: string }>; + expect(generateVisLayerTooltipFields(visLayerColumnIds)).toStrictEqual(tooltipFields); + }); + it('array with one value returns correct array', function () { + const visLayerColumnIds = ['test-id-1']; + const tooltipFields = [{ field: 'test-id-1' }]; + expect(generateVisLayerTooltipFields(visLayerColumnIds)).toStrictEqual(tooltipFields); + }); + it('array with multiple values returns correct array', function () { + const visLayerColumnIds = ['test-id-1', 'test-id-2']; + const tooltipFields = [{ field: 'test-id-1' }, { field: 'test-id-2' }]; + expect(generateVisLayerTooltipFields(visLayerColumnIds)).toStrictEqual(tooltipFields); + }); + }); + describe('addMissingRowsToTableBounds()', function () { const columnId = 'test-id'; const columnName = 'test-name'; @@ -356,7 +375,7 @@ describe('helpers', function () { TEST_VIS_LAYERS_SINGLE ) ).toStrictEqual({ - ...TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY, + ...TEST_DATATABLE_NO_VIS_LAYERS, rows: [], }); }); diff --git a/src/plugins/vis_augmenter/public/vega/helpers.ts b/src/plugins/vis_augmenter/public/vega/helpers.ts index 086f2c4f4ae2..2c4044754727 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.ts @@ -123,6 +123,16 @@ export const generateVisLayerFilterString = (visLayerColumnIds: string[]): strin } }; +export const generateVisLayerTooltipFields = ( + visLayerColumnIds: string[] +): Array<{ field: string }> => { + return visLayerColumnIds.map((id) => { + return { + field: id, + }; + }); +}; + /** * By default, the source datatable will not include rows with empty data. * For handling events that may belong in missing buckets that are not yet @@ -195,28 +205,34 @@ export const addPointInTimeEventsLayersToTable = ( const augmentedTable = addMissingRowsToTableBounds(datatable, dimensions); const xAxisId = getXAxisId(dimensions, augmentedTable.columns); - if (isEmpty(visLayers)) return augmentedTable; + if (isEmpty(visLayers) || augmentedTable.rows.length === 0) return augmentedTable; - visLayers.every((visLayer: PointInTimeEventsVisLayer) => { - const visLayerColumnId = `${visLayer.pluginResource.id}`; - const visLayerColumnName = `${visLayer.pluginResource.name}`; + // Create columns for every unique event type. This is so we can aggregate on the different event types + // (e.g., 'Anomalies', 'Alerts') + [ + ...new Set(visLayers.map((visLayer: PointInTimeEventsVisLayer) => visLayer.pluginEventType)), + ].forEach((pluginEventType: string) => { augmentedTable.columns.push({ - id: visLayerColumnId, - name: visLayerColumnName, + id: pluginEventType, + name: `${pluginEventType} count`, meta: { type: VIS_LAYER_COLUMN_TYPE, }, }); + }); - if (augmentedTable.rows.length === 0 || isEmpty(visLayer.events)) return false; + visLayers.forEach((visLayer: PointInTimeEventsVisLayer) => { + if (isEmpty(visLayer.events)) return; + const visLayerColumnId = `${visLayer.pluginEventType}`; // if only one row / one datapoint, put all events into this bucket if (augmentedTable.rows.length === 1) { augmentedTable.rows[0] = { ...augmentedTable.rows[0], - [visLayerColumnId]: visLayer.events.length, + [visLayerColumnId]: + (get(augmentedTable.rows[0], visLayerColumnId, 0) as number) + visLayer.events.length, }; - return false; + return; } // Bin the timestamps to the closest x-axis key, adding @@ -267,8 +283,6 @@ export const addPointInTimeEventsLayersToTable = ( break; } }); - - return true; }); return augmentedTable; }; @@ -372,6 +386,7 @@ export const addPointInTimeEventsLayersToSpec = ( condition: { empty: false, param: HOVER_PARAM, value: EVENT_MARK_SIZE_ENLARGED }, value: EVENT_MARK_SIZE, }, + tooltip: generateVisLayerTooltipFields(visLayerColumnIds), }, }); From ec4c1df3d5fe1a642049a091d19083a5a1e9eb5a Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Wed, 24 May 2023 11:15:59 -0700 Subject: [PATCH 18/37] Add new advanced settings for vis augmenter (#3961) * Add new advanced settings Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * Add yml setting and doing settings checks now Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * minor fixes Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * Cleanup plus tests Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * code cleanup and address comments Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * fix comments Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * address comments Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * nitpick fix Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * Fix the setting Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * fix dependency issue Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * fix comments Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --------- Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --- config/opensearch_dashboards.yml | 5 +- .../server/collectors/management/schema.ts | 2 + src/plugins/telemetry/schema/oss_plugins.json | 6 + src/plugins/vis_augmenter/common/constants.ts | 3 + src/plugins/vis_augmenter/config.ts | 12 ++ src/plugins/vis_augmenter/public/plugin.ts | 3 +- .../saved_augment_vis.test.ts | 58 +++++++ .../saved_augment_vis/saved_augment_vis.ts | 146 ++++++++++++------ .../public/saved_augment_vis/utils/helpers.ts | 35 ++++- src/plugins/vis_augmenter/public/services.ts | 9 +- .../vis_augmenter/public/utils/utils.test.ts | 16 ++ .../vis_augmenter/public/utils/utils.ts | 14 +- src/plugins/vis_augmenter/server/index.ts | 9 +- src/plugins/vis_augmenter/server/plugin.ts | 52 ++++++- .../create_vis_embeddable_from_object.ts | 2 +- .../public/embeddable/visualize_embeddable.ts | 77 ++++----- src/plugins/visualizations/public/services.ts | 10 +- 17 files changed, 357 insertions(+), 102 deletions(-) create mode 100644 src/plugins/vis_augmenter/config.ts diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 38377296bd20..f6f1486e5499 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -239,4 +239,7 @@ #data_source.encryption.wrappingKey: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # Set the value of this setting to false to hide the help menu link to the OpenSearch Dashboards user survey -# opensearchDashboards.survey.url: "https://survey.opensearch.org" \ No newline at end of file +# opensearchDashboards.survey.url: "https://survey.opensearch.org" + +# Set the value of this setting to false to disable plugin augmentation on Dashboard +# vis_augmenter.pluginAugmentationEnabled: false diff --git a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts index 8127664c02cd..45fbc7aaaa87 100644 --- a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts @@ -40,6 +40,8 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = { 'visualization:regionmap:showWarnings': { type: 'boolean' }, 'visualization:dimmingOpacity': { type: 'float' }, 'visualization:tileMap:maxPrecision': { type: 'long' }, + 'visualization:enablePluginAugmentation': { type: 'boolean' }, + 'visualization:enablePluginAugmentation.maxPluginObjects': { type: 'number' }, 'securitySolution:ipReputationLinks': { type: 'text' }, 'csv:separator': { type: 'keyword' }, 'visualization:tileMap:WMSdefaults': { type: 'text' }, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index b1e09dda9794..1e25048ed03d 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -1372,6 +1372,12 @@ "visualization:tileMap:maxPrecision": { "type": "long" }, + "visualization:enablePluginAugmentation": { + "type": "boolean" + }, + "visualization:enablePluginAugmentation.maxPluginObjects": { + "type": "number" + }, "securitySolution:ipReputationLinks": { "type": "text" }, diff --git a/src/plugins/vis_augmenter/common/constants.ts b/src/plugins/vis_augmenter/common/constants.ts index 7ce517aba77e..54d4edd37901 100644 --- a/src/plugins/vis_augmenter/common/constants.ts +++ b/src/plugins/vis_augmenter/common/constants.ts @@ -10,3 +10,6 @@ export const APP_API = '/api/vis_augmenter'; // used for limiting results received from the stats API export const PER_PAGE_REQUEST_NUMBER = 50; +export const PLUGIN_AUGMENTATION_ENABLE_SETTING = 'visualization:enablePluginAugmentation'; +export const PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING = + 'visualization:enablePluginAugmentation.maxPluginObjects'; diff --git a/src/plugins/vis_augmenter/config.ts b/src/plugins/vis_augmenter/config.ts new file mode 100644 index 000000000000..12b9854f451a --- /dev/null +++ b/src/plugins/vis_augmenter/config.ts @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { schema, TypeOf } from '@osd/config-schema'; + +export const configSchema = schema.object({ + pluginAugmentationEnabled: schema.boolean({ defaultValue: true }), +}); + +export type VisAugmenterPluginConfigType = TypeOf<typeof configSchema>; diff --git a/src/plugins/vis_augmenter/public/plugin.ts b/src/plugins/vis_augmenter/public/plugin.ts index 1c064a1cee10..3bfedc2e0cea 100644 --- a/src/plugins/vis_augmenter/public/plugin.ts +++ b/src/plugins/vis_augmenter/public/plugin.ts @@ -7,7 +7,7 @@ import { ExpressionsSetup } from '../../expressions/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; import { visLayers } from './expressions'; -import { setSavedAugmentVisLoader } from './services'; +import { setSavedAugmentVisLoader, setUISettings } from './services'; import { createSavedAugmentVisLoader, SavedAugmentVisLoader } from './saved_augment_vis'; // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -40,6 +40,7 @@ export class VisAugmenterPlugin } public start(core: CoreStart, { data }: VisAugmenterStartDeps): VisAugmenterStart { + setUISettings(core.uiSettings); const savedAugmentVisLoader = createSavedAugmentVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts index 7e2aabe25d69..99975a54039b 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.test.ts @@ -10,9 +10,19 @@ import { SavedObjectOpenSearchDashboardsServicesWithAugmentVis, } from './saved_augment_vis'; import { generateAugmentVisSavedObject, getMockAugmentVisSavedObjectClient } from './utils'; +import { uiSettingsServiceMock } from '../../../../core/public/mocks'; +import { setUISettings } from '../services'; +import { PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common/constants'; import { ISavedPluginResource } from './types'; +const uiSettingsMock = uiSettingsServiceMock.createStartContract(); +setUISettings(uiSettingsMock); + describe('SavedObjectLoaderAugmentVis', () => { + uiSettingsMock.get.mockImplementation((key: string) => { + return key === PLUGIN_AUGMENTATION_ENABLE_SETTING; + }); + const fn = { type: VisLayerTypes.PointInTimeEvents, name: 'test-fn', @@ -191,4 +201,52 @@ describe('SavedObjectLoaderAugmentVis', () => { expect(resp.hits[0].id).toEqual('missing-plugin-resource-id-obj-id-1'); expect(resp.hits[0].error).toEqual('pluginResource.id is missing in augment-vis saved object'); }); + + it('find returns exception due to setting being disabled', async () => { + uiSettingsMock.get.mockImplementation((key: string) => { + return key !== PLUGIN_AUGMENTATION_ENABLE_SETTING; + }); + const loader = createSavedAugmentVisLoader(({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([]), + } as unknown) as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + try { + await loader.find(); + } catch (e) { + expect(e.message).toBe( + 'Visualization augmentation is disabled, please enable visualization:enablePluginAugmentation.' + ); + } + }); + + it('findAll returns exception due to setting being disabled', async () => { + uiSettingsMock.get.mockImplementation((key: string) => { + return key !== PLUGIN_AUGMENTATION_ENABLE_SETTING; + }); + const loader = createSavedAugmentVisLoader(({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([]), + } as unknown) as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + try { + await loader.findAll(); + } catch (e) { + expect(e.message).toBe( + 'Visualization augmentation is disabled, please enable visualization:enablePluginAugmentation.' + ); + } + }); + + it('get returns exception due to setting being disabled', async () => { + uiSettingsMock.get.mockImplementation((key: string) => { + return key !== PLUGIN_AUGMENTATION_ENABLE_SETTING; + }); + const loader = createSavedAugmentVisLoader(({ + savedObjectsClient: getMockAugmentVisSavedObjectClient([]), + } as unknown) as SavedObjectOpenSearchDashboardsServicesWithAugmentVis); + try { + await loader.get(); + } catch (e) { + expect(e.message).toBe( + 'Visualization augmentation is disabled, please enable visualization:enablePluginAugmentation.' + ); + } + }); }); diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts index 5c76b3cec421..28b7831d9431 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/saved_augment_vis.ts @@ -4,72 +4,118 @@ */ import { get, isEmpty } from 'lodash'; +import { IUiSettingsClient } from 'opensearch-dashboards/public'; import { SavedObjectLoader, SavedObjectOpenSearchDashboardsServices, } from '../../../saved_objects/public'; import { createSavedAugmentVisClass } from './_saved_augment_vis'; import { VisLayerTypes } from '../types'; -import { AugmentVisSavedObjectAttributes } from '../../common'; +import { getUISettings } from '../services'; +import { AugmentVisSavedObjectAttributes, PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SavedObjectOpenSearchDashboardsServicesWithAugmentVis extends SavedObjectOpenSearchDashboardsServices {} export type SavedAugmentVisLoader = ReturnType<typeof createSavedAugmentVisLoader>; -export function createSavedAugmentVisLoader( - services: SavedObjectOpenSearchDashboardsServicesWithAugmentVis -) { - const { savedObjectsClient } = services; - class SavedObjectLoaderAugmentVis extends SavedObjectLoader { - mapHitSource = (source: AugmentVisSavedObjectAttributes, id: string) => { - source.id = id; - source.visId = get(source, 'visReference.id', '') as string; +export class SavedObjectLoaderAugmentVis extends SavedObjectLoader { + private readonly config: IUiSettingsClient = getUISettings(); - if (isEmpty(source.visReference)) { - source.error = 'visReference is missing in augment-vis saved object'; - return source; - } - if (isEmpty(source.visLayerExpressionFn)) { - source.error = 'visLayerExpressionFn is missing in augment-vis saved object'; - return source; - } - if (!((get(source, 'visLayerExpressionFn.type', '') as string) in VisLayerTypes)) { - source.error = 'Unknown VisLayer expression function type'; - return source; - } - if (get(source, 'originPlugin', undefined) === undefined) { - source.error = 'originPlugin is missing in augment-vis saved object'; - return source; - } - if (get(source, 'pluginResource.type', undefined) === undefined) { - source.error = 'pluginResource.type is missing in augment-vis saved object'; - return source; - } - if (get(source, 'pluginResource.id', undefined) === undefined) { - source.error = 'pluginResource.id is missing in augment-vis saved object'; - return source; - } + mapHitSource = (source: AugmentVisSavedObjectAttributes, id: string) => { + source.id = id; + source.visId = get(source, 'visReference.id', '') as string; + + if (isEmpty(source.visReference)) { + source.error = 'visReference is missing in augment-vis saved object'; + return source; + } + if (isEmpty(source.visLayerExpressionFn)) { + source.error = 'visLayerExpressionFn is missing in augment-vis saved object'; + return source; + } + if (!((get(source, 'visLayerExpressionFn.type', '') as string) in VisLayerTypes)) { + source.error = 'Unknown VisLayer expression function type'; + return source; + } + if (get(source, 'originPlugin', undefined) === undefined) { + source.error = 'originPlugin is missing in augment-vis saved object'; + return source; + } + if (get(source, 'pluginResource.type', undefined) === undefined) { + source.error = 'pluginResource.type is missing in augment-vis saved object'; + return source; + } + if (get(source, 'pluginResource.id', undefined) === undefined) { + source.error = 'pluginResource.id is missing in augment-vis saved object'; return source; - }; + } + return source; + }; - /** - * Updates hit.attributes to contain an id related to the referenced visualization - * (visId) and returns the updated attributes object. - * @param hit - * @returns {hit.attributes} The modified hit.attributes object, with an id and url field. - */ - mapSavedObjectApiHits(hit: { - references: any[]; - attributes: AugmentVisSavedObjectAttributes; - id: string; - }) { - // For now we are assuming only one vis reference per saved object. - // If we change to multiple, we will need to dynamically handle that - const visReference = hit.references[0]; - return this.mapHitSource({ ...hit.attributes, visReference }, hit.id); + /** + * Updates hit.attributes to contain an id related to the referenced visualization + * (visId) and returns the updated attributes object. + * @param hit + * @returns {hit.attributes} The modified hit.attributes object, with an id and url field. + */ + mapSavedObjectApiHits(hit: { + references: any[]; + attributes: AugmentVisSavedObjectAttributes; + id: string; + }) { + // For now we are assuming only one vis reference per saved object. + // If we change to multiple, we will need to dynamically handle that + const visReference = hit.references[0]; + return this.mapHitSource({ ...hit.attributes, visReference }, hit.id); + } + + /** + * Retrieve a saved object by id or create new one. + * Returns a promise that completes when the object finishes + * initializing. Throws exception when the setting is set to false. + * @param opts + * @returns {Promise<SavedObject>} + */ + get(opts?: Record<string, unknown> | string) { + this.isAugmentationEnabled(); + return super.get(opts); + } + + /** + * TODO: Rather than use a hardcoded limit, implement pagination. See + * https://github.com/elastic/kibana/issues/8044 for reference. + * + * @param search + * @param size + * @param fields + * @returns {Promise} + */ + findAll(search: string = '', size: number = 100, fields?: string[]) { + this.isAugmentationEnabled(); + return super.findAll(search, size, fields); + } + + find(search: string = '', size: number = 100) { + this.isAugmentationEnabled(); + return super.find(search, size); + } + + private isAugmentationEnabled() { + const isAugmentationEnabled = this.config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING, true); + if (!isAugmentationEnabled) { + throw new Error( + 'Visualization augmentation is disabled, please enable visualization:enablePluginAugmentation.' + ); } } +} + +export function createSavedAugmentVisLoader( + services: SavedObjectOpenSearchDashboardsServicesWithAugmentVis +) { + const { savedObjectsClient } = services; + const SavedAugmentVis = createSavedAugmentVisClass(services); - return new SavedObjectLoaderAugmentVis(SavedAugmentVis, savedObjectsClient) as SavedObjectLoader; + return new SavedObjectLoaderAugmentVis(SavedAugmentVis, savedObjectsClient); } diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts index 6c984e16d4d7..ff23a229b154 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts @@ -3,8 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { getSavedAugmentVisLoader } from '../../services'; +import { get } from 'lodash'; +import { getSavedAugmentVisLoader, getUISettings } from '../../services'; import { ISavedAugmentVis } from '../types'; +import { + PLUGIN_AUGMENTATION_ENABLE_SETTING, + PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING, +} from '../../../common/constants'; /** * Create an augment vis saved object given an object that @@ -12,5 +17,33 @@ import { ISavedAugmentVis } from '../types'; */ export const createAugmentVisSavedObject = async (AugmentVis: ISavedAugmentVis): Promise<any> => { const loader = getSavedAugmentVisLoader(); + const config = getUISettings(); + + const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); + if (!isAugmentationEnabled) { + throw new Error( + 'Visualization augmentation is disabled, please enable visualization:enablePluginAugmentation.' + ); + } + const maxAssociatedCount = config.get(PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING); + + await loader.findAll().then(async (resp) => { + if (resp !== undefined) { + const savedAugmentObjects = get(resp, 'hits', []); + // gets all the saved object for this visualization + const savedObjectsForThisVisualization = savedAugmentObjects.filter( + (savedObj) => get(savedObj, 'visId', '') === AugmentVis.visId + ); + + if (maxAssociatedCount <= savedObjectsForThisVisualization.length) { + throw new Error( + `Cannot associate the plugin resource to the visualization due to the limit of the max + amount of associated plugin resources (${maxAssociatedCount}) with + ${savedObjectsForThisVisualization.length} associated to the visualization` + ); + } + } + }); + return await loader.get((AugmentVis as any) as Record<string, unknown>); }; diff --git a/src/plugins/vis_augmenter/public/services.ts b/src/plugins/vis_augmenter/public/services.ts index 00fa45374980..085d4ac44f66 100644 --- a/src/plugins/vis_augmenter/public/services.ts +++ b/src/plugins/vis_augmenter/public/services.ts @@ -3,9 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { createGetterSetter } from '../../opensearch_dashboards_utils/common'; -import { SavedObjectLoader } from '../../saved_objects/public'; +import { createGetterSetter } from '../../opensearch_dashboards_utils/public'; +import { IUiSettingsClient } from '../../../core/public'; +import { SavedObjectLoaderAugmentVis } from './saved_augment_vis'; export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< - SavedObjectLoader + SavedObjectLoaderAugmentVis >('savedAugmentVisLoader'); + +export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index 6e55fd8a11fd..ef18b0eb0b87 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -18,10 +18,20 @@ import { VisLayerTypes, VisLayerExpressionFn, } from '../'; +import { PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common/constants'; import { AggConfigs, AggTypesRegistryStart, IndexPattern } from '../../../data/common'; import { mockAggTypesRegistry } from '../../../data/common/search/aggs/test_helpers'; +import { uiSettingsServiceMock } from '../../../../core/public/mocks'; +import { setUISettings } from '../services'; describe('utils', () => { + const uiSettingsMock = uiSettingsServiceMock.createStartContract(); + setUISettings(uiSettingsMock); + beforeEach(() => { + uiSettingsMock.get.mockImplementation((key: string) => { + return key === PLUGIN_AUGMENTATION_ENABLE_SETTING; + }); + }); describe('isEligibleForVisLayers', () => { const validConfigStates = [ { @@ -279,6 +289,12 @@ describe('utils', () => { } as unknown) as Vis; expect(isEligibleForVisLayers(invalidVis)).toEqual(false); }); + it('vis is ineligible with valid type and disabled setting', async () => { + uiSettingsMock.get.mockImplementation((key: string) => { + return key !== PLUGIN_AUGMENTATION_ENABLE_SETTING; + }); + expect(isEligibleForVisLayers(validVis)).toEqual(false); + }); it('vis is eligible with valid type', async () => { expect(isEligibleForVisLayers(validVis)).toEqual(true); }); diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index 0ec4ad486383..ead35fa4b33a 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -18,6 +18,8 @@ import { VisLayer, isVisLayerWithError, } from '../'; +import { PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common/constants'; +import { getUISettings } from '../services'; export const isEligibleForVisLayers = (vis: Vis): boolean => { // Only support date histogram and ensure there is only 1 x-axis and it has to be on the bottom. @@ -38,7 +40,10 @@ export const isEligibleForVisLayers = (vis: Vis): boolean => { const hasOnlyLineSeries = vis.params.seriesParams.every((seriesParam: { type: string }) => seriesParam.type === 'line') && vis.params.type === 'line'; - return hasValidXaxis && hasCorrectAggregationCount && hasOnlyLineSeries; + // Checks if the augmentation setting is enabled + const config = getUISettings(); + const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); + return isAugmentationEnabled && hasValidXaxis && hasCorrectAggregationCount && hasOnlyLineSeries; }; /** @@ -50,6 +55,13 @@ export const getAugmentVisSavedObjs = async ( visId: string | undefined, loader: SavedAugmentVisLoader | undefined ): Promise<ISavedAugmentVis[]> => { + const config = getUISettings(); + const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); + if (!isAugmentationEnabled) { + throw new Error( + 'Visualization augmentation is disabled, please enable visualization:enablePluginAugmentation.' + ); + } try { const resp = await loader?.findAll(); const allSavedObjects = (get(resp, 'hits', []) as any[]) as ISavedAugmentVis[]; diff --git a/src/plugins/vis_augmenter/server/index.ts b/src/plugins/vis_augmenter/server/index.ts index cc6530649a8e..9fb8b7b695fa 100644 --- a/src/plugins/vis_augmenter/server/index.ts +++ b/src/plugins/vis_augmenter/server/index.ts @@ -3,9 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { PluginInitializerContext } from '../../../core/server'; +import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server'; import { VisAugmenterPlugin } from './plugin'; +import { configSchema, VisAugmenterPluginConfigType } from '../config'; +export const config: PluginConfigDescriptor<VisAugmenterPluginConfigType> = { + exposeToBrowser: { + pluginAugmentationEnabled: true, + }, + schema: configSchema, +}; export function plugin(initializerContext: PluginInitializerContext) { return new VisAugmenterPlugin(initializerContext); } diff --git a/src/plugins/vis_augmenter/server/plugin.ts b/src/plugins/vis_augmenter/server/plugin.ts index f6b41646c536..e482265ee290 100644 --- a/src/plugins/vis_augmenter/server/plugin.ts +++ b/src/plugins/vis_augmenter/server/plugin.ts @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { i18n } from '@osd/i18n'; +import { schema } from '@osd/config-schema'; +import { Observable } from 'rxjs'; +import { first } from 'rxjs/operators'; import { PluginInitializerContext, CoreSetup, @@ -12,6 +16,11 @@ import { } from '../../../core/server'; import { augmentVisSavedObjectType } from './saved_objects'; import { capabilitiesProvider } from './capabilities_provider'; +import { VisAugmenterPluginConfigType } from '../config'; +import { + PLUGIN_AUGMENTATION_ENABLE_SETTING, + PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING, +} from '../common/constants'; import { registerStatsRoute } from './routes/stats'; // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -22,16 +31,57 @@ export interface VisAugmenterPluginStart {} export class VisAugmenterPlugin implements Plugin<VisAugmenterPluginSetup, VisAugmenterPluginStart> { private readonly logger: Logger; + private readonly config$: Observable<VisAugmenterPluginConfigType>; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); + this.config$ = initializerContext.config.create<VisAugmenterPluginConfigType>(); } - public setup(core: CoreSetup) { + public async setup(core: CoreSetup) { this.logger.debug('VisAugmenter: Setup'); core.savedObjects.registerType(augmentVisSavedObjectType); core.capabilities.registerProvider(capabilitiesProvider); + const config: VisAugmenterPluginConfigType = await this.config$.pipe(first()).toPromise(); + const isAugmentationEnabled = + config.pluginAugmentationEnabled === undefined ? true : config.pluginAugmentationEnabled; + + // Checks if the global yaml setting for enabling plugin augmentation is disabled. + // If it is disabled, remove the settings as we would not want to show these to the + // user due to it being disabled at the cluster level. + if (isAugmentationEnabled) { + core.uiSettings.register({ + [PLUGIN_AUGMENTATION_ENABLE_SETTING]: { + name: i18n.translate('visualization.enablePluginAugmentationTitle', { + defaultMessage: 'Enable plugin augmentation', + }), + value: true, + description: i18n.translate('visualization.enablePluginAugmentationText', { + defaultMessage: 'Plugin functionality can be accessed from line chart visualizations', + }), + category: ['visualization'], + schema: schema.boolean(), + }, + [PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING]: { + name: i18n.translate('visualization.enablePluginAugmentation.maxPluginObjectsTitle', { + defaultMessage: 'Max number of associated augmentations', + }), + value: 10, + description: i18n.translate( + 'visualization.enablePluginAugmentation.maxPluginObjectsText', + { + defaultMessage: + 'Associating more than 10 plugin resources per visualization can lead to performance ' + + 'issues and increase the cost of running clusters.', + } + ), + category: ['visualization'], + schema: schema.number({ min: 0 }), + }, + }); + } + // Register server-side APIs const router = core.http.createRouter(); registerStatsRoute(router, this.logger); diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts index e09f789f9a68..2c937847ee61 100644 --- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts +++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts @@ -40,11 +40,11 @@ import { IContainer, ErrorEmbeddable } from '../../../embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { getSavedVisualizationsLoader, - getSavedAugmentVisLoader, getUISettings, getHttp, getTimeFilter, getCapabilities, + getSavedAugmentVisLoader, } from '../services'; import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index c313fe5ae3b9..5ceda93f6bc4 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -495,44 +495,47 @@ export class VisualizeEmbeddable expressionParams: IExpressionLoaderParams, abortController: AbortController ): Promise<VisLayers> => { - const augmentVisSavedObjs = await getAugmentVisSavedObjs( - this.vis.id, - this.savedAugmentVisLoader - ); - if ( - !isEmpty(augmentVisSavedObjs) && - !abortController.signal.aborted && - isEligibleForVisLayers(this.vis) - ) { - const visLayersPipeline = buildPipelineFromAugmentVisSavedObjs(augmentVisSavedObjs); - // The initial input for the pipeline will just be an empty arr of VisLayers. As plugin - // expression functions are ran, they will incrementally append their generated VisLayers to it. - const visLayersPipelineInput = { - type: 'vis_layers', - layers: [] as VisLayers, - }; - // We cannot use this.handler in this case, since it does not support the run() cmd - // we need here. So, we consume the expressions service to run this directly instead. - const exprVisLayers = (await getExpressions().run( - visLayersPipeline, - visLayersPipelineInput, - expressionParams as Record<string, unknown> - )) as ExprVisLayers; - const visLayers = exprVisLayers.layers; - const err = getAnyErrors(visLayers, this.vis.title); - // This is only true when one or more VisLayers has an error - if (err !== undefined) { - const { toasts } = getNotifications(); - toasts.addError(err, { - title: i18n.translate('visualizations.renderVisTitle', { - defaultMessage: `Error loading data on the ${this.vis.title} chart`, - }), - toastMessage: ' ', - id: this.id, - }); + try { + const augmentVisSavedObjs = await getAugmentVisSavedObjs( + this.vis.id, + this.savedAugmentVisLoader + ); + if ( + !isEmpty(augmentVisSavedObjs) && + !abortController.signal.aborted && + isEligibleForVisLayers(this.vis) + ) { + const visLayersPipeline = buildPipelineFromAugmentVisSavedObjs(augmentVisSavedObjs); + // The initial input for the pipeline will just be an empty arr of VisLayers. As plugin + // expression functions are ran, they will incrementally append their generated VisLayers to it. + const visLayersPipelineInput = { + type: 'vis_layers', + layers: [] as VisLayers, + }; + // We cannot use this.handler in this case, since it does not support the run() cmd + // we need here. So, we consume the expressions service to run this directly instead. + const exprVisLayers = (await getExpressions().run( + visLayersPipeline, + visLayersPipelineInput, + expressionParams as Record<string, unknown> + )) as ExprVisLayers; + const visLayers = exprVisLayers.layers; + const err = getAnyErrors(visLayers, this.vis.title); + // This is only true when one or more VisLayers has an error + if (err !== undefined) { + const { toasts } = getNotifications(); + toasts.addError(err, { + title: i18n.translate('visualizations.renderVisTitle', { + defaultMessage: `Error loading data on the ${this.vis.title} chart`, + }), + toastMessage: ' ', + id: this.id, + }); + } + return visLayers; } - - return visLayers; + } catch { + return [] as VisLayers; } return [] as VisLayers; }; diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index 92c01703ca17..a99a7010af28 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -53,7 +53,7 @@ import { UiActionsStart } from '../../ui_actions/public'; import { SavedVisualizationsLoader } from './saved_visualizations'; import { SavedObjectLoader } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; -import { SavedAugmentVisLoader } from '../../vis_augmenter/public'; +import { SavedObjectLoaderAugmentVis } from '../../vis_augmenter/public'; export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); @@ -109,10 +109,10 @@ export const [getSavedSearchLoader, setSavedSearchLoader] = createGetterSetter<S 'savedSearchLoader' ); -export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< - SavedAugmentVisLoader ->('SavedAugmentVisLoader'); - export const [getNotifications, setNotifications] = createGetterSetter<NotificationsStart>( 'Notifications' ); + +export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< + SavedObjectLoaderAugmentVis +>('savedAugmentVisLoader'); From 66070aa71ed0c48b233ed96853fa10a04c94f613 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Tue, 30 May 2023 09:36:53 -0700 Subject: [PATCH 19/37] Expose external services to helper fns (#4139) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../public/saved_augment_vis/utils/helpers.ts | 16 +++++++++++----- src/plugins/vis_augmenter/public/utils/utils.ts | 7 +++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts index ff23a229b154..0751f1dd330c 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/helpers.ts @@ -4,21 +4,27 @@ */ import { get } from 'lodash'; -import { getSavedAugmentVisLoader, getUISettings } from '../../services'; import { ISavedAugmentVis } from '../types'; import { PLUGIN_AUGMENTATION_ENABLE_SETTING, PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING, } from '../../../common/constants'; +import { SavedAugmentVisLoader } from '../saved_augment_vis'; +import { getSavedAugmentVisLoader, getUISettings } from '../../services'; +import { IUiSettingsClient } from '../../../../../core/public'; /** * Create an augment vis saved object given an object that * implements the ISavedAugmentVis interface */ -export const createAugmentVisSavedObject = async (AugmentVis: ISavedAugmentVis): Promise<any> => { - const loader = getSavedAugmentVisLoader(); - const config = getUISettings(); - +export const createAugmentVisSavedObject = async ( + AugmentVis: ISavedAugmentVis, + savedObjLoader?: SavedAugmentVisLoader, + uiSettings?: IUiSettingsClient +): Promise<any> => { + // Using optional services provided, or the built-in services from this plugin + const loader = savedObjLoader !== undefined ? savedObjLoader : getSavedAugmentVisLoader(); + const config = uiSettings !== undefined ? uiSettings : getUISettings(); const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); if (!isAugmentationEnabled) { throw new Error( diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index ead35fa4b33a..9d683ff962c1 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -20,6 +20,7 @@ import { } from '../'; import { PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common/constants'; import { getUISettings } from '../services'; +import { IUiSettingsClient } from '../../../../core/public'; export const isEligibleForVisLayers = (vis: Vis): boolean => { // Only support date histogram and ensure there is only 1 x-axis and it has to be on the bottom. @@ -53,9 +54,11 @@ export const isEligibleForVisLayers = (vis: Vis): boolean => { */ export const getAugmentVisSavedObjs = async ( visId: string | undefined, - loader: SavedAugmentVisLoader | undefined + loader: SavedAugmentVisLoader | undefined, + uiSettings?: IUiSettingsClient | undefined ): Promise<ISavedAugmentVis[]> => { - const config = getUISettings(); + // Using optional services provided, or the built-in services from this plugin + const config = uiSettings !== undefined ? uiSettings : getUISettings(); const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); if (!isAugmentationEnabled) { throw new Error( From 319b5a29c05f762bc44ddee05bcee778bbe74b1b Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Tue, 30 May 2023 17:37:27 -0700 Subject: [PATCH 20/37] [Feature Anywhere] Add view events flyout (#3415) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../public/lib/panel/embeddable_panel.tsx | 4 + src/plugins/embeddable/public/plugin.tsx | 13 +- .../vis_augmenter/opensearch_dashboards.json | 11 +- src/plugins/vis_augmenter/public/constants.ts | 5 +- src/plugins/vis_augmenter/public/index.ts | 4 + src/plugins/vis_augmenter/public/mocks.ts | 225 ++++++++++++++++++ src/plugins/vis_augmenter/public/plugin.ts | 36 ++- src/plugins/vis_augmenter/public/services.ts | 18 ++ .../vis_augmenter/public/test_constants.ts | 24 +- .../vis_augmenter/public/types.test.ts | 14 +- src/plugins/vis_augmenter/public/types.ts | 18 ++ .../public/ui_actions_bootstrap.ts | 47 ++++ .../vis_augmenter/public/utils/index.ts | 1 - .../public/utils/test_helpers.ts | 36 --- .../vis_augmenter/public/utils/utils.test.ts | 117 +++------ .../vis_augmenter/public/vega/helpers.ts | 110 ++++++++- .../view_events_flyout/actions/index.ts | 7 + .../actions/open_events_flyout.tsx | 35 +++ .../actions/open_events_flyout_action.test.ts | 94 ++++++++ .../actions/open_events_flyout_action.ts | 61 +++++ .../actions/view_events_option_action.test.ts | 114 +++++++++ .../actions/view_events_option_action.tsx | 60 +++++ .../error_flyout_body.test.tsx.snap | 41 ++++ .../loading_flyout_body.test.tsx.snap | 31 +++ .../components/base_vis_item.test.tsx | 33 +++ .../components/base_vis_item.tsx | 32 +++ .../components/date_range_item.test.tsx | 54 +++++ .../components/date_range_item.tsx | 65 +++++ .../components/error_flyout_body.test.tsx | 21 ++ .../components/error_flyout_body.tsx | 25 ++ .../components/event_vis_item.test.tsx | 63 +++++ .../components/event_vis_item.tsx | 51 ++++ .../components/event_vis_item_icon.test.tsx | 51 ++++ .../components/event_vis_item_icon.tsx | 63 +++++ .../components/events_panel.tsx | 32 +++ .../view_events_flyout/components/index.ts | 7 + .../components/loading_flyout_body.test.tsx | 16 ++ .../components/loading_flyout_body.tsx | 19 ++ .../components/plugin_events_panel.tsx | 31 +++ .../view_events_flyout/components/styles.scss | 62 +++++ .../components/timeline_panel.test.tsx | 33 +++ .../components/timeline_panel.tsx | 35 +++ .../view_events_flyout/components/types.tsx | 14 ++ .../components/utils.test.ts | 23 ++ .../view_events_flyout/components/utils.tsx | 224 +++++++++++++++++ .../components/view_events_flyout.tsx | 142 +++++++++++ .../public/view_events_flyout/flyout_state.ts | 19 ++ .../public/view_events_flyout/index.ts | 8 + .../vis_type_vega/opensearch_dashboards.json | 9 +- .../vis_type_vega/public/data_model/types.ts | 3 +- .../public/data_model/vega_parser.ts | 19 +- .../public/expressions/helpers.test.js | 15 +- .../public/expressions/helpers.ts | 32 ++- .../vis_type_vega/public/expressions/index.ts | 1 + .../public/expressions/line_vega_spec_fn.ts | 18 +- .../public/expressions/vega_fn.ts | 11 +- src/plugins/vis_type_vega/public/plugin.ts | 6 +- src/plugins/vis_type_vega/public/services.ts | 3 + src/plugins/vis_type_vega/public/vega_type.ts | 2 +- .../public/vega_view/vega_base_view.js | 10 +- .../public/vega_view/vega_view.js | 56 ++++- .../public/vega_visualization.js | 4 + .../public/line_to_expression.ts | 11 +- .../public/embeddable/visualize_embeddable.ts | 62 ++++- src/plugins/visualizations/public/index.ts | 7 +- .../public/legacy/build_pipeline.ts | 3 +- 66 files changed, 2324 insertions(+), 197 deletions(-) create mode 100644 src/plugins/vis_augmenter/public/mocks.ts create mode 100644 src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts delete mode 100644 src/plugins/vis_augmenter/public/utils/test_helpers.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/actions/index.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/error_flyout_body.test.tsx.snap create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/loading_flyout_body.test.tsx.snap create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.test.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.test.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.test.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.test.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.test.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/events_panel.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.test.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/plugin_events_panel.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.test.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/types.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/utils.test.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/flyout_state.ts create mode 100644 src/plugins/vis_augmenter/public/view_events_flyout/index.ts diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index 616ccb65493d..60eb3eb186a8 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -83,6 +83,8 @@ interface Props { SavedObjectFinder: React.ComponentType<any>; stateTransfer?: EmbeddableStateTransfer; hideHeader?: boolean; + hasBorder?: boolean; + hasShadow?: boolean; } interface State { @@ -234,6 +236,8 @@ export class EmbeddablePanel extends React.Component<Props, State> { paddingSize="none" role="figure" aria-labelledby={headerId} + hasBorder={this.props.hasBorder} + hasShadow={this.props.hasShadow} > {!this.props.hideHeader && ( <PanelHeader diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 26fc159304ed..a0f907043ec4 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -107,7 +107,12 @@ export interface EmbeddableStart extends PersistableState<EmbeddableInput> { getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer; } -export type EmbeddablePanelHOC = React.FC<{ embeddable: IEmbeddable; hideHeader?: boolean }>; +export type EmbeddablePanelHOC = React.FC<{ + embeddable: IEmbeddable; + hideHeader?: boolean; + hasBorder?: boolean; + hasShadow?: boolean; +}>; export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, EmbeddableStart> { private readonly embeddableFactoryDefinitions: Map< @@ -168,12 +173,18 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl const getEmbeddablePanelHoc = (stateTransfer?: EmbeddableStateTransfer) => ({ embeddable, hideHeader, + hasBorder, + hasShadow, }: { embeddable: IEmbeddable; hideHeader?: boolean; + hasBorder?: boolean; + hasShadow?: boolean; }) => ( <EmbeddablePanel hideHeader={hideHeader} + hasBorder={hasBorder} + hasShadow={hasShadow} embeddable={embeddable} stateTransfer={stateTransfer ? stateTransfer : this.outgoingOnlyStateTransfer} getActions={uiActions.getTriggerCompatibleActions} diff --git a/src/plugins/vis_augmenter/opensearch_dashboards.json b/src/plugins/vis_augmenter/opensearch_dashboards.json index ac78ec9c0281..007cae78290b 100644 --- a/src/plugins/vis_augmenter/opensearch_dashboards.json +++ b/src/plugins/vis_augmenter/opensearch_dashboards.json @@ -3,5 +3,14 @@ "version": "opensearchDashboards", "server": true, "ui": true, - "requiredPlugins": ["data", "savedObjects", "opensearchDashboardsUtils", "expressions"] + "requiredPlugins": [ + "data", + "savedObjects", + "opensearchDashboardsUtils", + "expressions", + "visualizations", + "uiActions", + "embeddable" + ], + "requiredBundles": ["opensearchDashboardsReact"] } diff --git a/src/plugins/vis_augmenter/public/constants.ts b/src/plugins/vis_augmenter/public/constants.ts index fe55a03d6c94..cd9adbaf0063 100644 --- a/src/plugins/vis_augmenter/public/constants.ts +++ b/src/plugins/vis_augmenter/public/constants.ts @@ -4,11 +4,10 @@ */ export const VIS_LAYER_COLUMN_TYPE = 'vis_layer'; -// TODO: replace with a value imported from OUI export const EVENT_COLOR = 'red'; export const HOVER_PARAM = 'HOVER'; -export const EVENT_MARK_SIZE = 100; -export const EVENT_MARK_SIZE_ENLARGED = 140; +export const EVENT_MARK_SIZE = 40; +export const EVENT_MARK_SIZE_ENLARGED = 80; export const EVENT_MARK_SHAPE = 'triangle-up'; export const EVENT_TIMELINE_HEIGHT = 25; export const EVENT_TOOLTIP_CENTER_ON_MARK = 5; diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index 78657ba6c224..9a4a5faeb803 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -22,8 +22,12 @@ export { PointInTimeEventsVisLayer, isPointInTimeEventsVisLayer, isVisLayerWithError, + VisAugmenterEmbeddableConfig, + VisFlyoutContext, } from './types'; +export { AugmentVisContext } from './ui_actions_bootstrap'; + export * from './expressions'; export * from './utils'; export * from './constants'; diff --git a/src/plugins/vis_augmenter/public/mocks.ts b/src/plugins/vis_augmenter/public/mocks.ts new file mode 100644 index 000000000000..34d6d81a1ef6 --- /dev/null +++ b/src/plugins/vis_augmenter/public/mocks.ts @@ -0,0 +1,225 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get } from 'lodash'; +import { VisualizeEmbeddable, Vis } from '../../visualizations/public'; +import { ErrorEmbeddable } from '../../embeddable/public'; +// eslint-disable-next-line @osd/eslint/no-restricted-paths +import { timefilterServiceMock } from '../../data/public/query/timefilter/timefilter_service.mock'; +import { EventVisEmbeddableItem } from './view_events_flyout'; +import { + VisLayer, + VisLayerTypes, + VisLayerErrorTypes, + PointInTimeEventsVisLayer, + PluginResource, + PointInTimeEvent, +} from './types'; +import { AggConfigs, AggTypesRegistryStart, IndexPattern } from '../../data/common'; +import { mockAggTypesRegistry } from '../../data/common/search/aggs/test_helpers'; + +export const VALID_CONFIG_STATES = [ + { + enabled: true, + type: 'max', + params: {}, + schema: 'metric', + }, + { + enabled: true, + type: 'date_histogram', + params: {}, + schema: 'segment', + }, +]; + +export const STUB_INDEX_PATTERN_WITH_FIELDS = { + id: '1234', + title: 'logstash-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], +} as IndexPattern; + +export const TYPES_REGISTRY: AggTypesRegistryStart = mockAggTypesRegistry(); + +export const VALID_AGGS = new AggConfigs(STUB_INDEX_PATTERN_WITH_FIELDS, VALID_CONFIG_STATES, { + typesRegistry: TYPES_REGISTRY, +}); + +export const VALID_VIS = ({ + type: {}, + uiState: { + on: jest.fn(), + }, + params: { + type: 'line', + seriesParams: [ + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs: VALID_AGGS, + }, +} as unknown) as Vis; + +const SAVED_OBJ_ID = 'test-saved-obj-id'; +const VIS_TITLE = 'test-vis-title'; +const ORIGIN_PLUGIN = 'test-plugin'; +const PLUGIN_RESOURCE = { + type: 'test-type', + id: 'test-resource-id', + name: 'test-resource-name', + urlPath: 'test-url-path', +} as PluginResource; +const EVENT_COUNT = 3; +const ERROR_MESSAGE = 'test-error-message'; + +export const createPluginResource = ( + type: string = PLUGIN_RESOURCE.type, + id: string = PLUGIN_RESOURCE.id, + name: string = PLUGIN_RESOURCE.name, + urlPath: string = PLUGIN_RESOURCE.urlPath +): PluginResource => { + return { + type, + id, + name, + urlPath, + }; +}; + +export const createMockErrorEmbeddable = (): ErrorEmbeddable => { + return new ErrorEmbeddable('Oh no something has gone wrong', { id: ' 404' }); +}; + +export const createMockVisEmbeddable = ( + savedObjectId: string = SAVED_OBJ_ID, + title: string = VIS_TITLE, + validVis: boolean = true +): VisualizeEmbeddable => { + const mockTimeFilterService = timefilterServiceMock.createStartContract(); + const mockTimeFilter = mockTimeFilterService.timefilter; + const mockVis = validVis + ? VALID_VIS + : (({ + type: {}, + data: {}, + uiState: { + on: jest.fn(), + }, + params: { + type: 'line', + seriesParams: [], + }, + } as unknown) as Vis); + const mockDeps = { + start: jest.fn(), + }; + const mockConfiguration = { + vis: mockVis, + editPath: 'test-edit-path', + editUrl: 'test-edit-url', + editable: true, + deps: mockDeps, + }; + const mockVisualizeInput = { id: 'test-id', savedObjectId }; + + const mockVisEmbeddable = new VisualizeEmbeddable( + mockTimeFilter, + mockConfiguration, + mockVisualizeInput + ); + mockVisEmbeddable.getTitle = () => title; + return mockVisEmbeddable; +}; + +export const createPointInTimeEventsVisLayer = ( + originPlugin: string = ORIGIN_PLUGIN, + pluginResource: PluginResource = PLUGIN_RESOURCE, + eventCount: number = EVENT_COUNT, + error: boolean = false, + errorMessage: string = ERROR_MESSAGE +): PointInTimeEventsVisLayer => { + const events = [] as PointInTimeEvent[]; + for (let i = 0; i < eventCount; i++) { + events.push({ + timestamp: i, + metadata: { + pluginResourceId: pluginResource.id, + }, + } as PointInTimeEvent); + } + return { + originPlugin, + type: VisLayerTypes.PointInTimeEvents, + pluginResource, + events, + error: error + ? { + type: VisLayerErrorTypes.FETCH_FAILURE, + message: errorMessage, + } + : undefined, + }; +}; + +export const createMockEventVisEmbeddableItem = ( + savedObjectId: string = SAVED_OBJ_ID, + title: string = VIS_TITLE, + originPlugin: string = ORIGIN_PLUGIN, + pluginResource: PluginResource = PLUGIN_RESOURCE, + eventCount: number = EVENT_COUNT +): EventVisEmbeddableItem => { + const visLayer = createPointInTimeEventsVisLayer(originPlugin, pluginResource, eventCount); + const embeddable = createMockVisEmbeddable(savedObjectId, title); + return { + visLayer, + embeddable, + }; +}; + +export const createVisLayer = ( + type: any, + error: boolean = false, + errorMessage: string = 'some-error-message', + resource?: { + type?: string; + id?: string; + name?: string; + urlPath?: string; + } +): VisLayer => { + return { + type, + originPlugin: 'test-plugin', + pluginResource: { + type: get(resource, 'type', 'test-resource-type'), + id: get(resource, 'id', 'test-resource-id'), + name: get(resource, 'name', 'test-resource-name'), + urlPath: get(resource, 'urlPath', 'test-resource-url-path'), + }, + error: error + ? { + type: VisLayerErrorTypes.FETCH_FAILURE, + message: errorMessage, + } + : undefined, + }; +}; diff --git a/src/plugins/vis_augmenter/public/plugin.ts b/src/plugins/vis_augmenter/public/plugin.ts index 3bfedc2e0cea..d7b09d57d790 100644 --- a/src/plugins/vis_augmenter/public/plugin.ts +++ b/src/plugins/vis_augmenter/public/plugin.ts @@ -5,10 +5,22 @@ import { ExpressionsSetup } from '../../expressions/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; import { visLayers } from './expressions'; import { setSavedAugmentVisLoader, setUISettings } from './services'; import { createSavedAugmentVisLoader, SavedAugmentVisLoader } from './saved_augment_vis'; +import { registerTriggersAndActions } from './ui_actions_bootstrap'; +import { UiActionsStart } from '../../ui_actions/public'; +import { + setUiActions, + setEmbeddable, + setQueryService, + setVisualizations, + setCore, +} from './services'; +import { EmbeddableStart } from '../../embeddable/public'; +import { DataPublicPluginStart } from '../../data/public'; +import { VisualizationsStart } from '../../visualizations/public'; +import { VIEW_EVENTS_FLYOUT_STATE, setFlyoutState } from './view_events_flyout'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface VisAugmenterSetup {} @@ -18,12 +30,14 @@ export interface VisAugmenterStart { } export interface VisAugmenterSetupDeps { - data: DataPublicPluginSetup; expressions: ExpressionsSetup; } export interface VisAugmenterStartDeps { + uiActions: UiActionsStart; + embeddable: EmbeddableStart; data: DataPublicPluginStart; + visualizations: VisualizationsStart; } export class VisAugmenterPlugin @@ -33,14 +47,26 @@ export class VisAugmenterPlugin public setup( core: CoreSetup<VisAugmenterStartDeps, VisAugmenterStart>, - { data, expressions }: VisAugmenterSetupDeps + { expressions }: VisAugmenterSetupDeps ): VisAugmenterSetup { expressions.registerType(visLayers); + setUISettings(core.uiSettings); return {}; } - public start(core: CoreStart, { data }: VisAugmenterStartDeps): VisAugmenterStart { - setUISettings(core.uiSettings); + public start( + core: CoreStart, + { uiActions, embeddable, data, visualizations }: VisAugmenterStartDeps + ): VisAugmenterStart { + setUiActions(uiActions); + setEmbeddable(embeddable); + setQueryService(data.query); + setVisualizations(visualizations); + setCore(core); + setFlyoutState(VIEW_EVENTS_FLYOUT_STATE.CLOSED); + + registerTriggersAndActions(core); + const savedAugmentVisLoader = createSavedAugmentVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, diff --git a/src/plugins/vis_augmenter/public/services.ts b/src/plugins/vis_augmenter/public/services.ts index 085d4ac44f66..48a3233714e6 100644 --- a/src/plugins/vis_augmenter/public/services.ts +++ b/src/plugins/vis_augmenter/public/services.ts @@ -6,9 +6,27 @@ import { createGetterSetter } from '../../opensearch_dashboards_utils/public'; import { IUiSettingsClient } from '../../../core/public'; import { SavedObjectLoaderAugmentVis } from './saved_augment_vis'; +import { EmbeddableStart } from '../../embeddable/public'; +import { UiActionsStart } from '../../ui_actions/public'; +import { DataPublicPluginStart } from '../../../plugins/data/public'; +import { VisualizationsStart } from '../../visualizations/public'; +import { CoreStart } from '../../../core/public'; export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< SavedObjectLoaderAugmentVis >('savedAugmentVisLoader'); export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); +export const [getUiActions, setUiActions] = createGetterSetter<UiActionsStart>('UIActions'); + +export const [getEmbeddable, setEmbeddable] = createGetterSetter<EmbeddableStart>('embeddable'); + +export const [getQueryService, setQueryService] = createGetterSetter< + DataPublicPluginStart['query'] +>('Query'); + +export const [getVisualizations, setVisualizations] = createGetterSetter<VisualizationsStart>( + 'visualizations' +); + +export const [getCore, setCore] = createGetterSetter<CoreStart>('Core'); diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index 61ced45b41ea..c616897d095f 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -4,8 +4,16 @@ */ import { OpenSearchDashboardsDatatable } from '../../expressions/public'; -import { VIS_LAYER_COLUMN_TYPE, VisLayerTypes, HOVER_PARAM } from './'; import { VisAnnotationType } from './vega/constants'; +import { + VIS_LAYER_COLUMN_TYPE, + VisLayerTypes, + HOVER_PARAM, + EVENT_MARK_SIZE, + EVENT_MARK_SIZE_ENLARGED, + EVENT_COLOR, + EVENT_MARK_SHAPE, +} from './'; const TEST_X_AXIS_ID = 'test-x-axis-id'; const TEST_X_AXIS_ID_DIRTY = 'test.x.axis.id'; @@ -493,10 +501,11 @@ const TEST_EVENTS_LAYER_SINGLE_VIS_LAYER = { height: 25, mark: { type: 'point', - shape: 'triangle-up', - color: 'red', - filled: true, - opacity: 1, + shape: EVENT_MARK_SHAPE, + fill: EVENT_COLOR, + fillOpacity: 1, + stroke: EVENT_COLOR, + strokeOpacity: 1, style: [`${VisAnnotationType.POINT_IN_TIME_ANNOTATION}`], tooltip: true, }, @@ -539,7 +548,10 @@ const TEST_EVENTS_LAYER_SINGLE_VIS_LAYER = { ], }, }, - size: { condition: { empty: false, param: HOVER_PARAM, value: 140 }, value: 100 }, + size: { + condition: { empty: false, param: HOVER_PARAM, value: EVENT_MARK_SIZE_ENLARGED }, + value: EVENT_MARK_SIZE, + }, tooltip: [{ field: TEST_PLUGIN_EVENT_TYPE }], }, }; diff --git a/src/plugins/vis_augmenter/public/types.test.ts b/src/plugins/vis_augmenter/public/types.test.ts index 732541812b37..4bd4cb1221c3 100644 --- a/src/plugins/vis_augmenter/public/types.test.ts +++ b/src/plugins/vis_augmenter/public/types.test.ts @@ -9,40 +9,40 @@ import { isValidVisLayer, isVisLayerWithError, } from './types'; -import { generateVisLayer } from './utils'; +import { createVisLayer } from './mocks'; describe('isPointInTimeEventsVisLayer()', function () { it('should return false if type does not match', function () { - const visLayer = generateVisLayer('unknown-vis-layer-type'); + const visLayer = createVisLayer('unknown-vis-layer-type'); expect(isPointInTimeEventsVisLayer(visLayer)).toBe(false); }); it('should return true if type matches', function () { - const visLayer = generateVisLayer(VisLayerTypes.PointInTimeEvents); + const visLayer = createVisLayer(VisLayerTypes.PointInTimeEvents); expect(isPointInTimeEventsVisLayer(visLayer)).toBe(true); }); }); describe('isValidVisLayer()', function () { it('should return false if no valid type', function () { - const visLayer = generateVisLayer('unknown-vis-layer-type'); + const visLayer = createVisLayer('unknown-vis-layer-type'); expect(isValidVisLayer(visLayer)).toBe(false); }); it('should return true if type matches', function () { - const visLayer = generateVisLayer(VisLayerTypes.PointInTimeEvents); + const visLayer = createVisLayer(VisLayerTypes.PointInTimeEvents); expect(isValidVisLayer(visLayer)).toBe(true); }); }); describe('isVisLayerWithError()', function () { it('should return false if no error', function () { - const visLayer = generateVisLayer('unknown-vis-layer-type', false); + const visLayer = createVisLayer('unknown-vis-layer-type', false); expect(isVisLayerWithError(visLayer)).toBe(false); }); it('should return true if error', function () { - const visLayer = generateVisLayer(VisLayerTypes.PointInTimeEvents, true); + const visLayer = createVisLayer(VisLayerTypes.PointInTimeEvents, true); expect(isVisLayerWithError(visLayer)).toBe(true); }); }); diff --git a/src/plugins/vis_augmenter/public/types.ts b/src/plugins/vis_augmenter/public/types.ts index 6c63ccae3144..27fd9d7c241f 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -12,6 +12,12 @@ export enum VisLayerErrorTypes { FETCH_FAILURE = 'FETCH_FAILURE', } +export enum VisFlyoutContext { + BASE_VIS = 'BASE_VIS', + EVENT_VIS = 'EVENT_VIS', + TIMELINE_VIS = 'TIMELINE_VIS', +} + export interface VisLayerError { type: keyof typeof VisLayerErrorTypes; message: string; @@ -65,3 +71,15 @@ export const isValidVisLayer = (obj: any) => { * Used for checking if an existing VisLayer has a populated error field or not */ export const isVisLayerWithError = (visLayer: VisLayer): boolean => visLayer.error !== undefined; +// We need to have some extra config in order to render the charts correctly in different contexts. +// For example, we use the same base vis and modify it within the view events flyout to hide +// axes, only show events, only show timeline, add custom padding, etc. +// So, we abstract these concepts out and let the underlying implementation make changes as needed +// to support the different contexts. +export interface VisAugmenterEmbeddableConfig { + visLayerResourceIds?: string[]; + inFlyout?: boolean; + flyoutContext?: VisFlyoutContext; + leftValueAxisPadding?: boolean; + rightValueAxisPadding?: boolean; +} diff --git a/src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts b/src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts new file mode 100644 index 000000000000..aae3f237a28f --- /dev/null +++ b/src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts @@ -0,0 +1,47 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CoreStart } from 'opensearch-dashboards/public'; +import { + OpenEventsFlyoutAction, + ViewEventsOptionAction, + OPEN_EVENTS_FLYOUT_ACTION, + VIEW_EVENTS_OPTION_ACTION, +} from './view_events_flyout'; +import { externalActionTrigger, EXTERNAL_ACTION_TRIGGER } from '../../ui_actions/public'; +import { CONTEXT_MENU_TRIGGER, EmbeddableContext } from '../../embeddable/public'; +import { getUiActions } from './services'; + +export interface AugmentVisContext { + savedObjectId: string; +} + +// Overriding the mappings defined in UIActions plugin so that +// the new trigger and action definitions resolve. +// This is a common pattern among internal Dashboards plugins. +declare module '../../ui_actions/public' { + export interface TriggerContextMapping { + [EXTERNAL_ACTION_TRIGGER]: AugmentVisContext; + } + + export interface ActionContextMapping { + [OPEN_EVENTS_FLYOUT_ACTION]: AugmentVisContext; + [VIEW_EVENTS_OPTION_ACTION]: EmbeddableContext; + } +} + +export const registerTriggersAndActions = (core: CoreStart) => { + const openEventsFlyoutAction = new OpenEventsFlyoutAction(core); + const viewEventsOptionAction = new ViewEventsOptionAction(core); + + getUiActions().registerAction(openEventsFlyoutAction); + getUiActions().registerAction(viewEventsOptionAction); + getUiActions().registerTrigger(externalActionTrigger); + + // Opening View Events flyout from the chart + getUiActions().addTriggerAction(EXTERNAL_ACTION_TRIGGER, openEventsFlyoutAction); + // Opening View Events flyout from the context menu + getUiActions().addTriggerAction(CONTEXT_MENU_TRIGGER, viewEventsOptionAction); +}; diff --git a/src/plugins/vis_augmenter/public/utils/index.ts b/src/plugins/vis_augmenter/public/utils/index.ts index dc0cbebbe7de..079132ce99d2 100644 --- a/src/plugins/vis_augmenter/public/utils/index.ts +++ b/src/plugins/vis_augmenter/public/utils/index.ts @@ -4,4 +4,3 @@ */ export * from './utils'; -export * from './test_helpers'; diff --git a/src/plugins/vis_augmenter/public/utils/test_helpers.ts b/src/plugins/vis_augmenter/public/utils/test_helpers.ts deleted file mode 100644 index 7796ddb4f394..000000000000 --- a/src/plugins/vis_augmenter/public/utils/test_helpers.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { get } from 'lodash'; -import { VisLayer, VisLayerErrorTypes } from '../types'; - -export const generateVisLayer = ( - type: any, - error: boolean = false, - errorMessage: string = 'some-error-message', - resource?: { - type?: string; - id?: string; - name?: string; - urlPath?: string; - } -): VisLayer => { - return { - type, - originPlugin: 'test-plugin', - pluginResource: { - type: get(resource, 'type', 'test-resource-type'), - id: get(resource, 'id', 'test-resource-id'), - name: get(resource, 'name', 'test-resource-name'), - urlPath: get(resource, 'urlPath', 'test-resource-url-path'), - }, - error: error - ? { - type: VisLayerErrorTypes.FETCH_FAILURE, - message: errorMessage, - } - : undefined, - }; -}; diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index ef18b0eb0b87..707c36f87073 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -14,7 +14,6 @@ import { getMockAugmentVisSavedObjectClient, generateAugmentVisSavedObject, ISavedAugmentVis, - generateVisLayer, VisLayerTypes, VisLayerExpressionFn, } from '../'; @@ -23,6 +22,14 @@ import { AggConfigs, AggTypesRegistryStart, IndexPattern } from '../../../data/c import { mockAggTypesRegistry } from '../../../data/common/search/aggs/test_helpers'; import { uiSettingsServiceMock } from '../../../../core/public/mocks'; import { setUISettings } from '../services'; +import { + STUB_INDEX_PATTERN_WITH_FIELDS, + TYPES_REGISTRY, + VALID_AGGS, + VALID_CONFIG_STATES, + VALID_VIS, + createVisLayer, +} from '../mocks'; describe('utils', () => { const uiSettingsMock = uiSettingsServiceMock.createStartContract(); @@ -33,56 +40,6 @@ describe('utils', () => { }); }); describe('isEligibleForVisLayers', () => { - const validConfigStates = [ - { - enabled: true, - type: 'max', - params: {}, - schema: 'metric', - }, - { - enabled: true, - type: 'date_histogram', - params: {}, - schema: 'segment', - }, - ]; - const stubIndexPatternWithFields = { - id: '1234', - title: 'logstash-*', - fields: [ - { - name: 'response', - type: 'number', - esTypes: ['integer'], - aggregatable: true, - filterable: true, - searchable: true, - }, - ], - }; - const typesRegistry: AggTypesRegistryStart = mockAggTypesRegistry(); - const aggs = new AggConfigs(stubIndexPatternWithFields as IndexPattern, validConfigStates, { - typesRegistry, - }); - const validVis = ({ - params: { - type: 'line', - seriesParams: [ - { - type: 'line', - }, - ], - categoryAxes: [ - { - position: 'bottom', - }, - ], - }, - data: { - aggs, - }, - } as unknown) as Vis; it('vis is ineligible with invalid non-line type', async () => { const vis = ({ params: { @@ -95,7 +52,7 @@ describe('utils', () => { ], }, data: { - aggs, + aggs: VALID_AGGS, }, } as unknown) as Vis; expect(isEligibleForVisLayers(vis)).toEqual(false); @@ -113,13 +70,9 @@ describe('utils', () => { params: {}, }, ]; - const invalidAggs = new AggConfigs( - stubIndexPatternWithFields as IndexPattern, - invalidConfigStates, - { - typesRegistry, - } - ); + const invalidAggs = new AggConfigs(STUB_INDEX_PATTERN_WITH_FIELDS, invalidConfigStates, { + typesRegistry: TYPES_REGISTRY, + }); const vis = ({ params: { type: 'line', @@ -133,7 +86,7 @@ describe('utils', () => { }); it('vis is ineligible with invalid aggs counts', async () => { const invalidConfigStates = [ - ...validConfigStates, + ...VALID_CONFIG_STATES, { enabled: true, type: 'dot', @@ -141,13 +94,9 @@ describe('utils', () => { schema: 'radius', }, ]; - const invalidAggs = new AggConfigs( - stubIndexPatternWithFields as IndexPattern, - invalidConfigStates, - { - typesRegistry, - } - ); + const invalidAggs = new AggConfigs(STUB_INDEX_PATTERN_WITH_FIELDS, invalidConfigStates, { + typesRegistry: TYPES_REGISTRY, + }); const vis = ({ params: { type: 'line', @@ -167,13 +116,9 @@ describe('utils', () => { params: {}, }, ]; - const invalidAggs = new AggConfigs( - stubIndexPatternWithFields as IndexPattern, - invalidConfigStates, - { - typesRegistry, - } - ); + const invalidAggs = new AggConfigs(STUB_INDEX_PATTERN_WITH_FIELDS, invalidConfigStates, { + typesRegistry: TYPES_REGISTRY, + }); const vis = ({ params: { type: 'line', @@ -201,7 +146,7 @@ describe('utils', () => { ], }, data: { - aggs, + aggs: VALID_AGGS, }, } as unknown) as Vis; expect(isEligibleForVisLayers(vis)).toEqual(false); @@ -225,7 +170,7 @@ describe('utils', () => { ], }, data: { - aggs, + aggs: VALID_AGGS, }, } as unknown) as Vis; expect(isEligibleForVisLayers(vis)).toEqual(false); @@ -245,8 +190,8 @@ describe('utils', () => { schema: 'metric', }, ]; - const badAggs = new AggConfigs(stubIndexPatternWithFields as IndexPattern, badConfigStates, { - typesRegistry, + const badAggs = new AggConfigs(STUB_INDEX_PATTERN_WITH_FIELDS, badConfigStates, { + typesRegistry: TYPES_REGISTRY, }); const invalidVis = ({ params: { @@ -284,7 +229,7 @@ describe('utils', () => { ], }, data: { - aggs, + aggs: VALID_AGGS, }, } as unknown) as Vis; expect(isEligibleForVisLayers(invalidVis)).toEqual(false); @@ -293,10 +238,10 @@ describe('utils', () => { uiSettingsMock.get.mockImplementation((key: string) => { return key !== PLUGIN_AUGMENTATION_ENABLE_SETTING; }); - expect(isEligibleForVisLayers(validVis)).toEqual(false); + expect(isEligibleForVisLayers(VALID_VIS)).toEqual(false); }); it('vis is eligible with valid type', async () => { - expect(isEligibleForVisLayers(validVis)).toEqual(true); + expect(isEligibleForVisLayers(VALID_VIS)).toEqual(true); }); }); @@ -423,14 +368,14 @@ describe('utils', () => { }); describe('getAnyErrors', () => { - const noErrorLayer1 = generateVisLayer(VisLayerTypes.PointInTimeEvents, false); - const noErrorLayer2 = generateVisLayer(VisLayerTypes.PointInTimeEvents, false); - const errorLayer1 = generateVisLayer(VisLayerTypes.PointInTimeEvents, true, 'uh-oh!', { + const noErrorLayer1 = createVisLayer(VisLayerTypes.PointInTimeEvents, false); + const noErrorLayer2 = createVisLayer(VisLayerTypes.PointInTimeEvents, false); + const errorLayer1 = createVisLayer(VisLayerTypes.PointInTimeEvents, true, 'uh-oh!', { type: 'resource-type-1', id: '1234', name: 'resource-1', }); - const errorLayer2 = generateVisLayer( + const errorLayer2 = createVisLayer( VisLayerTypes.PointInTimeEvents, true, 'oh no something terrible has happened :(', @@ -440,7 +385,7 @@ describe('utils', () => { name: 'resource-2', } ); - const errorLayer3 = generateVisLayer(VisLayerTypes.PointInTimeEvents, true, 'oops!', { + const errorLayer3 = createVisLayer(VisLayerTypes.PointInTimeEvents, true, 'oops!', { type: 'resource-type-1', id: 'abcd', name: 'resource-3', diff --git a/src/plugins/vis_augmenter/public/vega/helpers.ts b/src/plugins/vis_augmenter/public/vega/helpers.ts index 2c4044754727..fa2073e3bb63 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.ts @@ -6,6 +6,7 @@ import moment from 'moment'; import { cloneDeep, isEmpty, get } from 'lodash'; import { Item } from 'vega'; +import { YAxisConfig } from 'src/plugins/vis_type_vega/public'; import { OpenSearchDashboardsDatatable, OpenSearchDashboardsDatatableColumn, @@ -20,11 +21,13 @@ import { EVENT_MARK_SIZE_ENLARGED, EVENT_MARK_SHAPE, EVENT_TIMELINE_HEIGHT, + EVENT_TOOLTIP_CENTER_ON_MARK, HOVER_PARAM, VisLayer, VisLayers, VisLayerTypes, - EVENT_TOOLTIP_CENTER_ON_MARK, + VisAugmenterEmbeddableConfig, + VisFlyoutContext, } from '../'; import { VisAnnotationType } from './constants'; @@ -354,13 +357,14 @@ export const addPointInTimeEventsLayersToSpec = ( mark: { type: 'point', shape: EVENT_MARK_SHAPE, - color: EVENT_COLOR, - filled: true, - opacity: 1, - tooltip: true, + fill: EVENT_COLOR, + stroke: EVENT_COLOR, + strokeOpacity: 1, + fillOpacity: 1, // This style is only used to locate this mark when trying to add signals in the compiled vega spec. // @see @method vega_parser._compileVegaLite style: [`${VisAnnotationType.POINT_IN_TIME_ANNOTATION}`], + tooltip: true, }, transform: [ { filter: generateVisLayerFilterString(visLayerColumnIds) }, @@ -396,3 +400,99 @@ export const addPointInTimeEventsLayersToSpec = ( export const isPointInTimeAnnotation = (item?: Item | null) => { return item?.datum?.annotationType === VisAnnotationType.POINT_IN_TIME_ANNOTATION; }; + +// This is the total y-axis padding such that if this is added to the "padding" value of the view, if there is no axis, +// it will align values on the x-axis +export const calculateYAxisPadding = (config: YAxisConfig): number => { + // TODO: figure out where this value is coming from + const defaultPadding = 3; + return ( + get(config, 'minExtent', 0) + + get(config, 'offset', 0) + + get(config, 'translate', 0) + + get(config, 'domainWidth', 0) + + get(config, 'labelPadding', 0) + + get(config, 'titlePadding', 0) + + get(config, 'tickOffset', 0) + + get(config, 'tickSize', 0) + + defaultPadding + ); +}; + +// Parse the vis augmenter config to apply different visual changes to the event chart spec. +// This includes potentially removing the original vis data, hiding axes, moving the legend, etc. +// Primarily used within the view events flyout to render the charts in different ways, and to +// ensure the stacked event charts are aligned with the base vis chart. +export const augmentEventChartSpec = ( + config: VisAugmenterEmbeddableConfig, + origSpec: object +): {} => { + const inFlyout = get(config, 'inFlyout', false) as boolean; + const flyoutContext = get(config, 'flyoutContext', VisFlyoutContext.BASE_VIS); + + const newVconcat = [] as Array<{}>; + // @ts-ignore + const newConfig = origSpec?.config; + const visChart = get(origSpec, 'vconcat[0]', {}); + const eventChart = get(origSpec, 'vconcat[1]', {}); + + if (inFlyout) { + switch (flyoutContext) { + case VisFlyoutContext.BASE_VIS: + newConfig.legend = { + ...newConfig.legend, + orient: 'top', + // need to set offset to 0 so we don't cut off the chart canvas within the embeddable + offset: 0, + }; + break; + + case VisFlyoutContext.EVENT_VIS: + eventChart.encoding.x.axis = { + domain: true, + grid: false, + ticks: false, + labels: false, + title: null, + }; + eventChart.mark.fillOpacity = 0; + break; + + case VisFlyoutContext.TIMELINE_VIS: + eventChart.transform = [ + { + filter: 'false', + }, + ]; + break; + } + + // if coming from view events page, need to standardize the y axis padding values so we can + // align all of the charts correctly + newConfig.axisY = { + // We need minExtent and maxExtent to be the same. We cannot calculate these on-the-fly + // so we need to force a static value. We choose 40 as a good middleground for sufficient + // axis space without taking up too much actual chart space. + minExtent: 40, + maxExtent: 40, + offset: 0, + translate: 0, + domainWidth: 1, + labelPadding: 2, + titlePadding: 2, + tickOffset: 0, + tickSize: 5, + } as YAxisConfig; + } + + if (flyoutContext === VisFlyoutContext.BASE_VIS) { + newVconcat.push(visChart); + } + newVconcat.push(eventChart); + + return { + ...cloneDeep(origSpec), + config: newConfig, + vconcat: newVconcat, + }; +}; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/index.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/index.ts new file mode 100644 index 000000000000..cd333ed9451d --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { OPEN_EVENTS_FLYOUT_ACTION, OpenEventsFlyoutAction } from './open_events_flyout_action'; +export { VIEW_EVENTS_OPTION_ACTION, ViewEventsOptionAction } from './view_events_option_action'; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx new file mode 100644 index 000000000000..cd7d90aedf11 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx @@ -0,0 +1,35 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +import React from 'react'; +import { CoreStart } from 'src/core/public'; +import { toMountPoint } from '../../../../opensearch_dashboards_react/public'; +import { ViewEventsFlyout } from '../components'; +import { VIEW_EVENTS_FLYOUT_STATE, setFlyoutState } from '../flyout_state'; + +interface Props { + core: CoreStart; + savedObjectId: string; +} + +export async function openViewEventsFlyout(props: Props) { + setFlyoutState(VIEW_EVENTS_FLYOUT_STATE.OPEN); + const flyoutSession = props.core.overlays.openFlyout( + toMountPoint( + <ViewEventsFlyout + onClose={() => { + if (flyoutSession) { + flyoutSession.close(); + setFlyoutState(VIEW_EVENTS_FLYOUT_STATE.CLOSED); + } + }} + savedObjectId={props.savedObjectId} + /> + ), + { + 'data-test-subj': 'viewEventsFlyout', + ownFocus: true, + } + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts new file mode 100644 index 000000000000..381cbcb2e453 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { coreMock } from '../../../../../core/public/mocks'; +import { CoreStart } from 'opensearch-dashboards/public'; +import { OpenEventsFlyoutAction } from './open_events_flyout_action'; +import flyoutStateModule from '../flyout_state'; + +// Mocking the flyout state service. Defaulting to CLOSED. May override +// getFlyoutState() in below individual tests to test out different scenarios. +jest.mock('src/plugins/vis_augmenter/public/view_events_flyout/flyout_state', () => { + return { + VIEW_EVENTS_FLYOUT_STATE: { + OPEN: 'OPEN', + CLOSED: 'CLOSED', + }, + getFlyoutState: () => 'CLOSED', + setFlyoutState: () => {}, + }; +}); + +let coreStart: CoreStart; +beforeEach(async () => { + coreStart = coreMock.createStart(); +}); +afterEach(async () => { + jest.clearAllMocks(); +}); + +describe('OpenEventsFlyoutAction', () => { + it('is incompatible with null saved obj id', async () => { + const action = new OpenEventsFlyoutAction(coreStart); + const savedObjectId = null; + // @ts-ignore + expect(await action.isCompatible({ savedObjectId })).toBe(false); + }); + + it('is incompatible with undefined saved obj id', async () => { + const action = new OpenEventsFlyoutAction(coreStart); + const savedObjectId = undefined; + // @ts-ignore + expect(await action.isCompatible({ savedObjectId })).toBe(false); + }); + + it('is incompatible with empty saved obj id', async () => { + const action = new OpenEventsFlyoutAction(coreStart); + const savedObjectId = ''; + expect(await action.isCompatible({ savedObjectId })).toBe(false); + }); + + it('execute throws error if incompatible saved obj id', async () => { + const action = new OpenEventsFlyoutAction(coreStart); + async function check(id: any) { + await action.execute({ savedObjectId: id }); + } + await expect(check(null)).rejects.toThrow(Error); + await expect(check(undefined)).rejects.toThrow(Error); + await expect(check('')).rejects.toThrow(Error); + }); + + it('execute calls openFlyout if compatible saved obj id and flyout is closed', async () => { + const getFlyoutStateSpy = jest + .spyOn(flyoutStateModule, 'getFlyoutState') + .mockImplementation(() => 'CLOSED'); + const savedObjectId = 'test-id'; + const action = new OpenEventsFlyoutAction(coreStart); + await action.execute({ savedObjectId }); + expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(1); + expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); + }); + + it('execute does not call openFlyout if compatible saved obj id and flyout is open', async () => { + const getFlyoutStateSpy = jest + .spyOn(flyoutStateModule, 'getFlyoutState') + .mockImplementation(() => 'OPEN'); + const savedObjectId = 'test-id'; + const action = new OpenEventsFlyoutAction(coreStart); + await action.execute({ savedObjectId }); + expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(0); + expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); + }); + + it('Returns display name', async () => { + const action = new OpenEventsFlyoutAction(coreStart); + expect(action.getDisplayName()).toBeDefined(); + }); + + it('Returns undefined icon type', async () => { + const action = new OpenEventsFlyoutAction(coreStart); + expect(action.getIconType()).toBeUndefined(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts new file mode 100644 index 000000000000..c141a361048a --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts @@ -0,0 +1,61 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { CoreStart } from 'opensearch-dashboards/public'; +import { Action, IncompatibleActionError } from '../../../../ui_actions/public'; +import { AugmentVisContext } from '../../ui_actions_bootstrap'; +import { openViewEventsFlyout } from './open_events_flyout'; +import { VIEW_EVENTS_FLYOUT_STATE, getFlyoutState } from '../flyout_state'; + +export const OPEN_EVENTS_FLYOUT_ACTION = 'OPEN_EVENTS_FLYOUT_ACTION'; + +/** + * This action is identical to VIEW_EVENTS_OPTION_ACTION, but with different context. + * This is because the chart doesn't persist the embeddable, which is the default + * context used by the CONTEXT_MENU_TRIGGER. Because of that, we need a separate + * one that can be persisted in the chart - in this case, the AugmentVisContext, + * which is just a saved object ID. + */ + +export class OpenEventsFlyoutAction implements Action<AugmentVisContext> { + public readonly type = OPEN_EVENTS_FLYOUT_ACTION; + public readonly id = OPEN_EVENTS_FLYOUT_ACTION; + public order = 1; + + constructor(private core: CoreStart) {} + + public getIconType() { + return undefined; + } + + public getDisplayName() { + return i18n.translate('dashboard.actions.viewEvents.displayName', { + defaultMessage: 'View Events', + }); + } + + public async isCompatible({ savedObjectId }: AugmentVisContext) { + // checks for null / undefined / empty string + return savedObjectId ? true : false; + } + + public async execute({ savedObjectId }: AugmentVisContext) { + if (!(await this.isCompatible({ savedObjectId }))) { + throw new IncompatibleActionError(); + } + + // This action may get triggered even when the flyout is already open (e.g., + // clicking on an annotation point within a chart displayed in the flyout). + // In such case, we want to ignore it such that users can't keep endlessly + // re-opening it. + if (getFlyoutState() === VIEW_EVENTS_FLYOUT_STATE.CLOSED) { + openViewEventsFlyout({ + core: this.core, + savedObjectId, + }); + } + } +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts new file mode 100644 index 000000000000..64b75547d019 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts @@ -0,0 +1,114 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { coreMock } from '../../../../../core/public/mocks'; +import { CoreStart } from 'opensearch-dashboards/public'; +import { ViewEventsOptionAction } from './view_events_option_action'; +import { createMockErrorEmbeddable, createMockVisEmbeddable } from '../../mocks'; +import flyoutStateModule from '../flyout_state'; + +// Mocking the flyout state service. Defaulting to CLOSED. May override +// getFlyoutState() in below individual tests to test out different scenarios. +jest.mock('src/plugins/vis_augmenter/public/view_events_flyout/flyout_state', () => { + return { + VIEW_EVENTS_FLYOUT_STATE: { + OPEN: 'OPEN', + CLOSED: 'CLOSED', + }, + getFlyoutState: () => 'CLOSED', + setFlyoutState: () => {}, + }; +}); + +// Mocking the UISettings service. This is needed when making eligibility checks for the actions, +// which does UISettings checks to ensure the feature is enabled. +jest.mock('src/plugins/vis_augmenter/public/services.ts', () => { + return { + getUISettings: () => { + return { + get: (config: string) => { + switch (config) { + case 'visualization:enablePluginAugmentation': + return true; + case 'visualization:enablePluginAugmentation.maxPluginObjects': + return 10; + default: + throw new Error(`Accessing ${config} is not supported in the mock.`); + } + }, + }; + }, + }; +}); + +let coreStart: CoreStart; + +beforeEach(async () => { + coreStart = coreMock.createStart(); +}); +afterEach(async () => { + jest.clearAllMocks(); +}); + +describe('ViewEventsOptionAction', () => { + it('is incompatible with ErrorEmbeddables', async () => { + const action = new ViewEventsOptionAction(coreStart); + const errorEmbeddable = createMockErrorEmbeddable(); + expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false); + }); + + it('is incompatible with VisualizeEmbeddable with invalid vis', async () => { + const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title', false); + const action = new ViewEventsOptionAction(coreStart); + expect(await action.isCompatible({ embeddable: visEmbeddable })).toBe(false); + }); + + it('is compatible with VisualizeEmbeddable with valid vis', async () => { + const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); + const action = new ViewEventsOptionAction(coreStart); + expect(await action.isCompatible({ embeddable: visEmbeddable })).toBe(true); + }); + + it('execute throws error if incompatible embeddable', async () => { + const errorEmbeddable = createMockErrorEmbeddable(); + const action = new ViewEventsOptionAction(coreStart); + async function check() { + await action.execute({ embeddable: errorEmbeddable }); + } + await expect(check()).rejects.toThrow(Error); + }); + + it('execute calls openFlyout if compatible embeddable and flyout is currently closed', async () => { + const getFlyoutStateSpy = jest + .spyOn(flyoutStateModule, 'getFlyoutState') + .mockImplementation(() => 'CLOSED'); + const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); + const action = new ViewEventsOptionAction(coreStart); + await action.execute({ embeddable: visEmbeddable }); + expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(1); + expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); + }); + + it('execute does not call openFlyout if compatible embeddable and flyout is currently open', async () => { + const getFlyoutStateSpy = jest + .spyOn(flyoutStateModule, 'getFlyoutState') + .mockImplementation(() => 'OPEN'); + const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); + const action = new ViewEventsOptionAction(coreStart); + await action.execute({ embeddable: visEmbeddable }); + expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(0); + expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); + }); + + it('Returns display name', async () => { + const action = new ViewEventsOptionAction(coreStart); + expect(action.getDisplayName()).toBeDefined(); + }); + + it('Returns an icon type', async () => { + const action = new ViewEventsOptionAction(coreStart); + expect(action.getIconType()).toBeDefined(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx new file mode 100644 index 000000000000..1ad2a9c57fe2 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx @@ -0,0 +1,60 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; +import { get } from 'lodash'; +import { CoreStart } from 'opensearch-dashboards/public'; +import { VisualizeEmbeddable } from '../../../../visualizations/public'; +import { EmbeddableContext } from '../../../../embeddable/public'; +import { Action, IncompatibleActionError } from '../../../../ui_actions/public'; +import { openViewEventsFlyout } from './open_events_flyout'; +import { isEligibleForVisLayers } from '../../utils'; +import { VIEW_EVENTS_FLYOUT_STATE, getFlyoutState } from '../flyout_state'; + +export const VIEW_EVENTS_OPTION_ACTION = 'VIEW_EVENTS_OPTION_ACTION'; + +export class ViewEventsOptionAction implements Action<EmbeddableContext> { + public readonly type = VIEW_EVENTS_OPTION_ACTION; + public readonly id = VIEW_EVENTS_OPTION_ACTION; + public order = 1; + + constructor(private core: CoreStart) {} + + public getIconType(): EuiIconType { + return 'apmTrace'; + } + + public getDisplayName() { + return i18n.translate('dashboard.actions.viewEvents.displayName', { + defaultMessage: 'View Events', + }); + } + + public async isCompatible({ embeddable }: EmbeddableContext) { + const vis = (embeddable as VisualizeEmbeddable).vis; + return vis !== undefined && isEligibleForVisLayers(vis); + } + + public async execute({ embeddable }: EmbeddableContext) { + if (!(await this.isCompatible({ embeddable }))) { + throw new IncompatibleActionError(); + } + + const visEmbeddable = embeddable as VisualizeEmbeddable; + const savedObjectId = get(visEmbeddable.getInput(), 'savedObjectId', ''); + + // This action may get triggered even when the flyout is already open (e.g., + // clicking on an annotation point within a chart displayed in the flyout). + // In such case, we want to ignore it such that users can't keep endlessly + // re-opening it. + if (getFlyoutState() === VIEW_EVENTS_FLYOUT_STATE.CLOSED) { + openViewEventsFlyout({ + core: this.core, + savedObjectId, + }); + } + } +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/error_flyout_body.test.tsx.snap b/src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/error_flyout_body.test.tsx.snap new file mode 100644 index 000000000000..7094a4660dbd --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/error_flyout_body.test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<ErrorFlyoutBody/> renders component 1`] = ` +<div> + <div + class="euiFlyoutBody" + > + <div + class="euiFlyoutBody__overflow" + tabindex="0" + > + <div + class="euiFlyoutBody__overflowContent" + > + <div + class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive" + > + <div + class="euiFlexItem" + > + <div + class="euiCallOut euiCallOut--danger" + data-test-subj="errorCallOut" + > + <div + class="euiText euiText--small" + > + <div + class="euiTextColor euiTextColor--default" + > + oh no an error! + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +</div> +`; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/loading_flyout_body.test.tsx.snap b/src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/loading_flyout_body.test.tsx.snap new file mode 100644 index 000000000000..6b642518fc6e --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/__snapshots__/loading_flyout_body.test.tsx.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<LoadingFlyoutBody/> renders component 1`] = ` +<div> + <div + class="euiFlyoutBody" + > + <div + class="euiFlyoutBody__overflow" + tabindex="0" + > + <div + class="euiFlyoutBody__overflowContent" + > + <div + class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentSpaceAround euiFlexGroup--directionRow euiFlexGroup--responsive" + > + <div + class="euiFlexItem euiFlexItem--flexGrowZero" + > + <span + class="euiLoadingSpinner euiLoadingSpinner--xLarge" + data-test-subj="loadingSpinner" + /> + </div> + </div> + </div> + </div> + </div> +</div> +`; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.test.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.test.tsx new file mode 100644 index 000000000000..ca88941f6f23 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.test.tsx @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { BaseVisItem } from './base_vis_item'; +import { createMockVisEmbeddable } from '../../mocks'; + +jest.mock('../../services', () => { + return { + getEmbeddable: () => { + return { + getEmbeddablePanel: () => { + return 'MockEmbeddablePanel'; + }, + }; + }, + }; +}); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('<BaseVisItem/>', () => { + it('renders', async () => { + const embeddable = createMockVisEmbeddable(); + const { getByTestId } = render(<BaseVisItem embeddable={embeddable} />); + expect(getByTestId('baseVis')).toBeInTheDocument(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.tsx new file mode 100644 index 000000000000..3840c5a1f23b --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/base_vis_item.tsx @@ -0,0 +1,32 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { getEmbeddable } from '../../services'; +import { VisualizeEmbeddable } from '../../../../visualizations/public'; +import './styles.scss'; + +interface Props { + embeddable: VisualizeEmbeddable; +} + +export function BaseVisItem(props: Props) { + const PanelComponent = getEmbeddable().getEmbeddablePanel(); + + return ( + <EuiFlexGroup direction="row" gutterSize="s"> + <EuiFlexItem className="view-events-flyout__visDescription" grow={false} /> + <EuiFlexItem grow={true} className="view-events-flyout__baseVis" data-test-subj="baseVis"> + <PanelComponent + embeddable={props.embeddable} + hideHeader={true} + hasBorder={false} + hasShadow={false} + /> + </EuiFlexItem> + </EuiFlexGroup> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.test.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.test.tsx new file mode 100644 index 000000000000..bd07e115d158 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.test.tsx @@ -0,0 +1,54 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { findTestSubject } from 'test_utils/helpers'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { DateRangeItem } from './date_range_item'; +import { TimeRange } from '../../../../data/common'; +import { prettyDuration } from '@elastic/eui'; +import { DATE_RANGE_FORMAT } from './view_events_flyout'; + +describe('<DateRangeItem/>', () => { + const mockTimeRange = { + from: 'now-7d', + to: 'now', + } as TimeRange; + const mockReloadFn = jest.fn(); + + it('time range is displayed correctly', async () => { + const prettyTimeRange = prettyDuration( + mockTimeRange.from, + mockTimeRange.to, + [], + DATE_RANGE_FORMAT + ); + + const { getByText } = render(<DateRangeItem timeRange={mockTimeRange} reload={mockReloadFn} />); + expect(getByText(prettyTimeRange)).toBeInTheDocument(); + }); + + it('triggers reload on clicking on refresh button', async () => { + const component = mountWithIntl( + <DateRangeItem timeRange={mockTimeRange} reload={mockReloadFn} /> + ); + const refreshButton = findTestSubject(component, 'refreshButton'); + refreshButton.simulate('click'); + expect(mockReloadFn).toHaveBeenCalledTimes(1); + }); + + // Note we are not creating/comparing snapshots for this component. That is because + // it will hardcode a time-specific value which can cause failures when running + // in different envs + it('renders component', async () => { + const { getByTestId } = render( + <DateRangeItem timeRange={mockTimeRange} reload={mockReloadFn} /> + ); + expect(getByTestId('durationText')).toBeInTheDocument(); + expect(getByTestId('refreshButton')).toBeInTheDocument(); + expect(getByTestId('refreshDescriptionText')).toBeInTheDocument(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.tsx new file mode 100644 index 000000000000..e2a7092f1e5f --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/date_range_item.tsx @@ -0,0 +1,65 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useState } from 'react'; +import moment from 'moment'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiIcon, + prettyDuration, + EuiButton, +} from '@elastic/eui'; +import { TimeRange } from '../../../../data/common'; +import { DATE_RANGE_FORMAT } from './view_events_flyout'; + +interface Props { + timeRange: TimeRange; + reload: () => void; +} + +export function DateRangeItem(props: Props) { + const [lastUpdatedTime, setLastUpdatedTime] = useState<string>( + moment(Date.now()).format(DATE_RANGE_FORMAT) + ); + + const durationText = prettyDuration( + props.timeRange.from, + props.timeRange.to, + [], + DATE_RANGE_FORMAT + ); + + return ( + <EuiFlexGroup direction="row" gutterSize="m" alignItems="center"> + <EuiFlexItem grow={false}> + <EuiIcon type="calendar" /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiText data-test-subj="durationText">{durationText}</EuiText> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiButton + iconType={'refresh'} + isDisabled={false} + onClick={() => { + props.reload(); + setLastUpdatedTime(moment(Date.now()).format(DATE_RANGE_FORMAT)); + }} + data-test-subj="refreshButton" + > + Refresh + </EuiButton> + </EuiFlexItem> + <EuiFlexItem grow={false} data-test-subj="refreshDescriptionText"> + <EuiText size="s" color="subdued" style={{ whiteSpace: 'pre-line' }}> + {`This view is not updated to load the latest events automatically. + Last updated: ${lastUpdatedTime}`} + </EuiText> + </EuiFlexItem> + </EuiFlexGroup> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.test.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.test.tsx new file mode 100644 index 000000000000..d3bb447ae934 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.test.tsx @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { ErrorFlyoutBody } from './error_flyout_body'; + +describe('<ErrorFlyoutBody/>', () => { + const errorMsg = 'oh no an error!'; + it('shows error message', async () => { + const { getByText } = render(<ErrorFlyoutBody errorMessage={errorMsg} />); + expect(getByText(errorMsg)).toBeInTheDocument(); + }); + it('renders component', async () => { + const { container, getByTestId } = render(<ErrorFlyoutBody errorMessage={errorMsg} />); + expect(getByTestId('errorCallOut')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.tsx new file mode 100644 index 000000000000..1e0349aa18c2 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/error_flyout_body.tsx @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiFlyoutBody, EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; + +interface Props { + errorMessage: string; +} + +export function ErrorFlyoutBody(props: Props) { + return ( + <EuiFlyoutBody> + <EuiFlexGroup> + <EuiFlexItem grow={true}> + <EuiCallOut color="danger" iconType="alert" data-test-subj="errorCallOut"> + {props.errorMessage} + </EuiCallOut> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlyoutBody> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.test.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.test.tsx new file mode 100644 index 000000000000..99a865a9218c --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.test.tsx @@ -0,0 +1,63 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { EventVisItem } from './event_vis_item'; +import { + createMockEventVisEmbeddableItem, + createMockVisEmbeddable, + createPluginResource, + createPointInTimeEventsVisLayer, +} from '../../mocks'; + +jest.mock('../../services', () => { + return { + getEmbeddable: () => { + return { + getEmbeddablePanel: () => { + return 'MockEmbeddablePanel'; + }, + }; + }, + getCore: () => { + return { + http: { + basePath: { + prepend: jest.fn(), + }, + }, + }; + }, + }; +}); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('<EventVisItem/>', () => { + it('renders', async () => { + const item = createMockEventVisEmbeddableItem(); + const { getByTestId, getByText } = render(<EventVisItem item={item} />); + expect(getByTestId('eventVis')).toBeInTheDocument(); + expect(getByTestId('pluginResourceDescription')).toBeInTheDocument(); + expect(getByText(item.visLayer.pluginResource.name)).toBeInTheDocument(); + }); + + it('shows event count when rendering a PointInTimeEventsVisLayer', async () => { + const eventCount = 5; + const pluginResource = createPluginResource(); + const visLayer = createPointInTimeEventsVisLayer('test-plugin', pluginResource, eventCount); + const embeddable = createMockVisEmbeddable(); + const item = { + visLayer, + embeddable, + }; + const { getByTestId, getByText } = render(<EventVisItem item={item} />); + expect(getByTestId('eventCount')).toBeInTheDocument(); + expect(getByText(eventCount)).toBeInTheDocument(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx new file mode 100644 index 000000000000..a1ee965bb20f --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx @@ -0,0 +1,51 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { get } from 'lodash'; +import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import { getEmbeddable, getCore } from '../../services'; +import './styles.scss'; +import { EventVisEmbeddableItem } from '.'; +import { EventVisItemIcon } from './event_vis_item_icon'; + +interface Props { + item: EventVisEmbeddableItem; +} + +export function EventVisItem(props: Props) { + const PanelComponent = getEmbeddable().getEmbeddablePanel(); + const baseUrl = getCore().http.basePath; + + const name = get(props, 'item.visLayer.pluginResource.name', ''); + const urlPath = get(props, 'item.visLayer.pluginResource.urlPath', ''); + + return ( + <> + <EuiFlexGroup direction="row" gutterSize="s"> + <EuiFlexItem + grow={false} + className="view-events-flyout__visDescription" + data-test-subj="pluginResourceDescription" + > + <EuiFlexGroup alignItems="center"> + <EuiFlexItem> + <EuiLink href={`${baseUrl.prepend(`${urlPath}`)}`}>{name}</EuiLink> + </EuiFlexItem> + <EventVisItemIcon visLayer={props.item.visLayer} /> + </EuiFlexGroup> + </EuiFlexItem> + <EuiFlexItem grow={true} className="view-events-flyout__eventVis" data-test-subj="eventVis"> + <PanelComponent + embeddable={props.item.embeddable} + hideHeader={true} + hasBorder={false} + hasShadow={false} + /> + </EuiFlexItem> + </EuiFlexGroup> + </> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.test.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.test.tsx new file mode 100644 index 000000000000..5ae91d4fa1e0 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.test.tsx @@ -0,0 +1,51 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { findTestSubject } from 'test_utils/helpers'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { EventVisItemIcon } from './event_vis_item_icon'; +import { EuiPopover } from '@elastic/eui'; +import { createPluginResource, createPointInTimeEventsVisLayer } from '../../mocks'; + +describe('<EventVisItemIcon/>', () => { + it('shows event count when rendering a PointInTimeEventsVisLayer', async () => { + const eventCount = 5; + const pluginResource = createPluginResource(); + const visLayer = createPointInTimeEventsVisLayer('test-plugin', pluginResource, eventCount); + const { getByTestId, getByText } = render(<EventVisItemIcon visLayer={visLayer} />); + expect(getByTestId('eventCount')).toBeInTheDocument(); + expect(getByText(eventCount)).toBeInTheDocument(); + }); + it('shows error when rendering a PointInTimeEventsVisLayer with an error', async () => { + const eventCount = 5; + const pluginResource = createPluginResource(); + const visLayerWithError = createPointInTimeEventsVisLayer( + 'test-plugin', + pluginResource, + eventCount, + true + ); + const { getByTestId } = render(<EventVisItemIcon visLayer={visLayerWithError} />); + expect(getByTestId('errorButton')).toBeInTheDocument(); + }); + it('triggers popout with error message when clicking on error button', async () => { + const eventCount = 5; + const pluginResource = createPluginResource(); + const visLayerWithError = createPointInTimeEventsVisLayer( + 'test-plugin', + pluginResource, + eventCount, + true, + 'some-error-message' + ); + const component = mountWithIntl(<EventVisItemIcon visLayer={visLayerWithError} />); + const errorButton = findTestSubject(component, 'dangerButton'); + errorButton.simulate('click'); + expect(component.find(EuiPopover).prop('isOpen')).toBe(true); + expect(component.contains('some-error-message')).toBe(true); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.tsx new file mode 100644 index 000000000000..8c00b72b85c8 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item_icon.tsx @@ -0,0 +1,63 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useState } from 'react'; +import { get } from 'lodash'; +import { EuiFlexItem, EuiNotificationBadge, EuiButtonIcon, EuiPopover } from '@elastic/eui'; +import './styles.scss'; +import { VisLayer, VisLayerTypes } from '../../'; + +interface Props { + visLayer: VisLayer; +} + +/** + * Returns a badge with the event count for this particular VisLayer (only PointInTimeEventVisLayers + * are currently supported), or an error icon which can be clicked to view the error message. + */ +export function EventVisItemIcon(props: Props) { + const [isErrorPopoverOpen, setIsErrorPopoverOpen] = useState(false); + const onButtonClick = () => setIsErrorPopoverOpen((isOpen) => !isOpen); + const closeErrorPopover = () => setIsErrorPopoverOpen(false); + + const errorMsg = get(props, 'visLayer.error.message', undefined) as string | undefined; + const isError = errorMsg !== undefined; + const showEventCount = props.visLayer.type === VisLayerTypes.PointInTimeEvents && !isError; + + const dangerButton = ( + <EuiButtonIcon + data-test-subj="dangerButton" + aria-label="Open error details" + className="error-icon-padding" + iconType={'alert'} + color="danger" + size="xs" + display="empty" + onClick={onButtonClick} + /> + ); + + return ( + <> + {showEventCount ? ( + <EuiFlexItem grow={false} data-test-subj="eventCount"> + <EuiNotificationBadge color="subdued"> + {get(props.visLayer, 'events.length', 0)} + </EuiNotificationBadge> + </EuiFlexItem> + ) : isError ? ( + <EuiFlexItem grow={false} data-test-subj="errorButton"> + <EuiPopover + button={dangerButton} + isOpen={isErrorPopoverOpen} + closePopover={closeErrorPopover} + > + <div>{errorMsg}</div> + </EuiPopover> + </EuiFlexItem> + ) : null} + </> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/events_panel.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/events_panel.tsx new file mode 100644 index 000000000000..33f3ea8bb205 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/events_panel.tsx @@ -0,0 +1,32 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import './styles.scss'; +import { EventVisEmbeddableItem, EventVisEmbeddablesMap } from '.'; +import { PluginEventsPanel } from './plugin_events_panel'; + +interface Props { + eventVisEmbeddablesMap: EventVisEmbeddablesMap; +} + +export function EventsPanel(props: Props) { + return ( + <> + {Array.from(props.eventVisEmbeddablesMap.keys()).map((key, index) => { + return ( + <div key={index}> + {index !== 0 ? <EuiSpacer size="l" /> : null} + <PluginEventsPanel + pluginTitle={key} + items={props.eventVisEmbeddablesMap.get(key) as EventVisEmbeddableItem[]} + /> + </div> + ); + })} + </> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts b/src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts new file mode 100644 index 000000000000..6c933e023882 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { ViewEventsFlyout } from './view_events_flyout'; +export { EventVisEmbeddablesMap, EventVisEmbeddableItem } from './types'; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.test.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.test.tsx new file mode 100644 index 000000000000..0a06516831d5 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.test.tsx @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { LoadingFlyoutBody } from './loading_flyout_body'; + +describe('<LoadingFlyoutBody/>', () => { + it('renders component', async () => { + const { container, getByTestId } = render(<LoadingFlyoutBody />); + expect(getByTestId('loadingSpinner')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.tsx new file mode 100644 index 000000000000..90a6d5213029 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/loading_flyout_body.tsx @@ -0,0 +1,19 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiFlyoutBody, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; + +export function LoadingFlyoutBody() { + return ( + <EuiFlyoutBody> + <EuiFlexGroup justifyContent="spaceAround"> + <EuiFlexItem grow={false}> + <EuiLoadingSpinner size="xl" data-test-subj="loadingSpinner" /> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlyoutBody> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/plugin_events_panel.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/plugin_events_panel.tsx new file mode 100644 index 000000000000..0f737f2c058b --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/plugin_events_panel.tsx @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiFlexItem, EuiText } from '@elastic/eui'; +import './styles.scss'; +import { EventVisItem } from './event_vis_item'; +import { EventVisEmbeddableItem } from '.'; + +interface Props { + pluginTitle: string; + items: EventVisEmbeddableItem[]; +} + +export function PluginEventsPanel(props: Props) { + return ( + <> + <EuiFlexItem grow={false} style={{ marginBottom: '12px' }}> + <EuiText size="m" style={{ fontWeight: 'bold' }}> + {props.pluginTitle} + </EuiText> + </EuiFlexItem> + + {props.items.map((item, index) => ( + <EventVisItem key={index} item={item} /> + ))} + </> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss b/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss new file mode 100644 index 000000000000..6eaf9bba01b5 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss @@ -0,0 +1,62 @@ +$vis-description-width: 150px; +$event-vis-height: 55px; +$timeline-panel-height: 100px; +$content-padding-top: 110px; // Padding needed within view events flyout content to sit comfortably below flyout header +$date-range-height: 45px; // Static height we want for the date range picker component +$error-icon-padding-right: -8px; // This is so the error icon is aligned consistent with the event count icons +$base-vis-min-height: 25vh; // Visualizations require the container to have a valid width and height to render + +.view-events-flyout { + &__baseVis { + min-height: $base-vis-min-height; + } + + &__eventVis { + height: $event-vis-height; + } + + &__timelinePanel { + height: $timeline-panel-height; + } + + &__visDescription { + min-width: $vis-description-width; + max-width: $vis-description-width; + } + + &__content { + position: absolute; + top: $content-padding-top; + right: $euiSizeM; + bottom: $euiSizeM; + left: $euiSizeM; + } + + &__contentPanel { + @include euiYScroll; + + overflow: auto; + overflow-x: hidden; + scrollbar-gutter: stable both-edges; + } +} + +.hide-y-scroll { + overflow-y: hidden; +} + +.show-y-scroll { + overflow-y: scroll; +} + +.date-range-panel-height { + height: $date-range-height; +} + +.timeline-panel-height { + height: $timeline-panel-height; +} + +.error-icon-padding { + margin-right: $error-icon-padding-right; +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.test.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.test.tsx new file mode 100644 index 000000000000..a22ac5aa209c --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.test.tsx @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { TimelinePanel } from './timeline_panel'; +import { createMockVisEmbeddable } from '../../mocks'; + +jest.mock('../../services', () => { + return { + getEmbeddable: () => { + return { + getEmbeddablePanel: () => { + return 'MockEmbeddablePanel'; + }, + }; + }, + }; +}); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('<TimelinePanel/>', () => { + it('renders', async () => { + const embeddable = createMockVisEmbeddable(); + const { getByTestId } = render(<TimelinePanel embeddable={embeddable} />); + expect(getByTestId('timelineVis')).toBeInTheDocument(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.tsx new file mode 100644 index 000000000000..6507eac8cc23 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/timeline_panel.tsx @@ -0,0 +1,35 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { getEmbeddable } from '../../services'; +import './styles.scss'; +import { VisualizeEmbeddable } from '../../../../visualizations/public'; + +interface Props { + embeddable: VisualizeEmbeddable; +} + +export function TimelinePanel(props: Props) { + const PanelComponent = getEmbeddable().getEmbeddablePanel(); + return ( + <EuiFlexGroup direction="row" gutterSize="s"> + <EuiFlexItem grow={false} className="view-events-flyout__visDescription" /> + <EuiFlexItem + grow={true} + className="view-events-flyout__timelinePanel" + data-test-subj="timelineVis" + > + <PanelComponent + embeddable={props.embeddable} + hideHeader={true} + hasBorder={false} + hasShadow={false} + /> + </EuiFlexItem> + </EuiFlexGroup> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/types.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/types.tsx new file mode 100644 index 000000000000..c70617e66651 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/types.tsx @@ -0,0 +1,14 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisLayer } from '../../types'; +import { VisualizeEmbeddable } from '../../../../visualizations/public'; + +export interface EventVisEmbeddableItem { + visLayer: VisLayer; + embeddable: VisualizeEmbeddable; +} + +export type EventVisEmbeddablesMap = Map<string, EventVisEmbeddableItem[]>; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.test.ts b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.test.ts new file mode 100644 index 000000000000..39ff9d53dd44 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createMockErrorEmbeddable } from '../../mocks'; +import { getErrorMessage } from './utils'; + +describe('utils', () => { + describe('getErrorMessage', () => { + const errorMsg = 'oh no an error!'; + it('returns message when error field is string', async () => { + const errorEmbeddable = createMockErrorEmbeddable(); + errorEmbeddable.error = errorMsg; + expect(getErrorMessage(errorEmbeddable)).toEqual(errorMsg); + }); + it('returns message when error field is Error obj', async () => { + const errorEmbeddable = createMockErrorEmbeddable(); + errorEmbeddable.error = new Error(errorMsg); + expect(getErrorMessage(errorEmbeddable)).toEqual(errorMsg); + }); + }); +}); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx new file mode 100644 index 000000000000..a103e6731c21 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx @@ -0,0 +1,224 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get } from 'lodash'; +import { ErrorEmbeddable } from '../../../../embeddable/public'; +import { VisualizeEmbeddable, VisualizeInput } from '../../../../visualizations/public'; +import { getEmbeddable, getQueryService } from '../../services'; +import { + isPointInTimeEventsVisLayer, + PointInTimeEventsVisLayer, + VisFlyoutContext, + VisLayer, +} from '../../types'; +import { EventVisEmbeddableItem, EventVisEmbeddablesMap } from './types'; + +export function getErrorMessage(errorEmbeddable: ErrorEmbeddable): string { + return errorEmbeddable.error instanceof Error + ? errorEmbeddable.error.message + : errorEmbeddable.error; +} + +/** + * Given an embeddable, check if/where there is value (y) axes located on the left and/or + * right of the chart. This is needed so we can properly align all of the event + * charts in the flyout appropriately. + */ +function getValueAxisPositions(embeddable: VisualizeEmbeddable): { left: boolean; right: boolean } { + let hasLeftValueAxis = false; + let hasRightValueAxis = false; + if (embeddable !== undefined) { + const valueAxes = embeddable.vis.params.valueAxes; + const positions = valueAxes.map( + (valueAxis: { position: string }) => valueAxis.position + ) as string[]; + hasLeftValueAxis = positions.includes('left'); + hasRightValueAxis = positions.includes('right'); + } + return { + left: hasLeftValueAxis, + right: hasRightValueAxis, + }; +} + +/** + * Fetching the base vis to show in the flyout, based on the saved object ID. Add constraints + * such that it is static and won't auto-refresh within the flyout. + * @param savedObjectId the saved object id of the base vis + * @param setTimeRange custom hook used in base component + * @param setVisEmbeddable custom hook used in base component + * @param setErrorMessage custom hook used in base component + */ +export async function fetchVisEmbeddable( + savedObjectId: string, + setTimeRange: Function, + setVisEmbeddable: Function, + setErrorMessage: Function +): Promise<void> { + const embeddableVisFactory = getEmbeddable().getEmbeddableFactory('visualization'); + try { + const contextInput = { + filters: getQueryService().filterManager.getFilters(), + query: getQueryService().queryString.getQuery(), + timeRange: getQueryService().timefilter.timefilter.getTime(), + }; + setTimeRange(contextInput.timeRange); + + const embeddable = (await embeddableVisFactory?.createFromSavedObject(savedObjectId, { + ...contextInput, + visAugmenterConfig: { + inFlyout: true, + flyoutContext: VisFlyoutContext.BASE_VIS, + }, + } as VisualizeInput)) as VisualizeEmbeddable | ErrorEmbeddable; + + if (embeddable instanceof ErrorEmbeddable) { + throw getErrorMessage(embeddable); + } + + embeddable.updateInput({ + // @ts-ignore + refreshConfig: { + value: 0, + pause: true, + }, + }); + + // By waiting for this to complete, embeddable.visLayers will be populated + await embeddable.populateVisLayers(); + + setVisEmbeddable(embeddable); + } catch (err: any) { + setErrorMessage(String(err)); + } +} + +/** + * For each VisLayer in the base vis embeddable, generate a new filtered vis + * embeddable (based off of the base vis), and pass in extra arguments to only + * show datapoints for that particular VisLayer. Partition them by + * plugin resource type via an EventVisEmbeddablesMap. + * @param savedObjectId the saved object id of the base vis embeddable + * @param embeddable the base vis embeddable + * @param setEventVisEmbeddablesMap custom hook used in base component + * @param setErrorMessage custom hook used in base component + */ +export async function createEventEmbeddables( + savedObjectId: string, + embeddable: VisualizeEmbeddable, + setEventVisEmbeddablesMap: Function, + setErrorMessage: Function +) { + const embeddableVisFactory = getEmbeddable().getEmbeddableFactory('visualization'); + try { + const { left, right } = getValueAxisPositions(embeddable); + const map = new Map<string, EventVisEmbeddableItem[]>() as EventVisEmbeddablesMap; + // Currently only support PointInTimeEventVisLayers. Different layer types + // may require different logic in here + const visLayers = (get(embeddable, 'visLayers', []) as VisLayer[]).filter((visLayer) => + isPointInTimeEventsVisLayer(visLayer) + ) as PointInTimeEventsVisLayer[]; + if (visLayers !== undefined) { + const contextInput = { + filters: embeddable.getInput().filters, + query: embeddable.getInput().query, + timeRange: embeddable.getInput().timeRange, + }; + + await Promise.all( + visLayers.map(async (visLayer) => { + const pluginResourceType = visLayer.pluginResource.type; + const eventEmbeddable = (await embeddableVisFactory?.createFromSavedObject( + savedObjectId, + { + ...contextInput, + visAugmenterConfig: { + visLayerResourceIds: [visLayer.pluginResource.id as string], + inFlyout: true, + flyoutContext: VisFlyoutContext.EVENT_VIS, + leftValueAxisPadding: left, + rightValueAxisPadding: right, + }, + } as VisualizeInput + )) as VisualizeEmbeddable | ErrorEmbeddable; + + if (eventEmbeddable instanceof ErrorEmbeddable) { + throw getErrorMessage(eventEmbeddable); + } + + eventEmbeddable.updateInput({ + // @ts-ignore + refreshConfig: { + value: 0, + pause: true, + }, + }); + + const curList = (map.get(pluginResourceType) === undefined + ? [] + : map.get(pluginResourceType)) as EventVisEmbeddableItem[]; + curList.push({ + visLayer, + embeddable: eventEmbeddable, + } as EventVisEmbeddableItem); + map.set(pluginResourceType, curList); + }) + ); + setEventVisEmbeddablesMap(map); + } + } catch (err: any) { + setErrorMessage(String(err)); + } +} + +/** + * Based on the base vis embeddable, generate a new filtered vis, and pass in extra + * arguments to only show the x-axis (timeline). + * @param savedObjectId the saved object id of the base vis + * @param embeddable the base vis embeddable + * @param setTimelineVisEmbeddable custom hook used in base component + * @param setErrorMessage custom hook used in base component + */ +export async function createTimelineEmbeddable( + savedObjectId: string, + embeddable: VisualizeEmbeddable, + setTimelineVisEmbeddable: Function, + setErrorMessage: Function +) { + const embeddableVisFactory = getEmbeddable().getEmbeddableFactory('visualization'); + try { + const { left, right } = getValueAxisPositions(embeddable); + const contextInput = { + filters: embeddable.getInput().filters, + query: embeddable.getInput().query, + timeRange: embeddable.getInput().timeRange, + }; + + const timelineEmbeddable = (await embeddableVisFactory?.createFromSavedObject(savedObjectId, { + ...contextInput, + visAugmenterConfig: { + inFlyout: true, + flyoutContext: VisFlyoutContext.TIMELINE_VIS, + leftValueAxisPadding: left, + rightValueAxisPadding: right, + }, + } as VisualizeInput)) as VisualizeEmbeddable | ErrorEmbeddable; + + if (timelineEmbeddable instanceof ErrorEmbeddable) { + throw getErrorMessage(timelineEmbeddable); + } + + timelineEmbeddable.updateInput({ + // @ts-ignore + refreshConfig: { + value: 0, + pause: true, + }, + }); + setTimelineVisEmbeddable(timelineEmbeddable); + } catch (err: any) { + setErrorMessage(String(err)); + } +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx new file mode 100644 index 000000000000..c1b5ffda024f --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx @@ -0,0 +1,142 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useState, useEffect } from 'react'; +import { + EuiFlyoutBody, + EuiFlyoutHeader, + EuiFlyout, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingContent, +} from '@elastic/eui'; +import './styles.scss'; +import { VisualizeEmbeddable } from '../../../../visualizations/public'; +import { TimeRange } from '../../../../data/common'; +import { BaseVisItem } from './base_vis_item'; +import { DateRangeItem } from './date_range_item'; +import { LoadingFlyoutBody } from './loading_flyout_body'; +import { ErrorFlyoutBody } from './error_flyout_body'; +import { EventsPanel } from './events_panel'; +import { TimelinePanel } from './timeline_panel'; +import { fetchVisEmbeddable, createEventEmbeddables, createTimelineEmbeddable } from './utils'; +import { EventVisEmbeddablesMap } from './types'; + +interface Props { + onClose: () => void; + savedObjectId: string; +} + +export const DATE_RANGE_FORMAT = 'MM/DD/YYYY HH:mm'; + +export function ViewEventsFlyout(props: Props) { + const [visEmbeddable, setVisEmbeddable] = useState<VisualizeEmbeddable | undefined>(undefined); + // This map persists a plugin resource type -> a list of vis embeddables + // for each VisLayer of that type + const [eventVisEmbeddablesMap, setEventVisEmbeddablesMap] = useState< + EventVisEmbeddablesMap | undefined + >(undefined); + const [timelineVisEmbeddable, setTimelineVisEmbeddable] = useState< + VisualizeEmbeddable | undefined + >(undefined); + const [timeRange, setTimeRange] = useState<TimeRange | undefined>(undefined); + const [isLoading, setIsLoading] = useState<boolean>(true); + const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined); + + function reload() { + visEmbeddable?.reload(); + eventVisEmbeddablesMap?.forEach((embeddableItems) => { + embeddableItems.forEach((embeddableItem) => { + embeddableItem.embeddable.reload(); + }); + }); + } + + useEffect(() => { + fetchVisEmbeddable(props.savedObjectId, setTimeRange, setVisEmbeddable, setErrorMessage); + // adding all of the values to the deps array cause a circular re-render. we don't want + // to keep re-fetching the visEmbeddable after it is set. + /* eslint-disable react-hooks/exhaustive-deps */ + }, [props.savedObjectId]); + + useEffect(() => { + if (visEmbeddable?.visLayers) { + createEventEmbeddables( + props.savedObjectId, + visEmbeddable, + setEventVisEmbeddablesMap, + setErrorMessage + ); + createTimelineEmbeddable( + props.savedObjectId, + visEmbeddable, + setTimelineVisEmbeddable, + setErrorMessage + ); + } + }, [visEmbeddable?.visLayers]); + + useEffect(() => { + if ( + visEmbeddable !== undefined && + eventVisEmbeddablesMap !== undefined && + timeRange !== undefined && + timelineVisEmbeddable !== undefined + ) { + setIsLoading(false); + } + }, [visEmbeddable, eventVisEmbeddablesMap, timeRange, timelineVisEmbeddable]); + + return ( + <> + <EuiFlyout size="l" onClose={props.onClose}> + <EuiFlyoutHeader hasBorder> + <EuiTitle size="m"> + <h1> + {isLoading ? ( + <EuiLoadingContent lines={1} /> + ) : errorMessage ? ( + 'Error fetching events' + ) : ( + `${visEmbeddable?.getTitle()}` + )} + </h1> + </EuiTitle> + </EuiFlyoutHeader> + {errorMessage ? ( + <ErrorFlyoutBody errorMessage={errorMessage} /> + ) : isLoading ? ( + <LoadingFlyoutBody /> + ) : ( + <EuiFlyoutBody> + <EuiFlexGroup className="view-events-flyout__content" direction="column"> + <EuiFlexItem + className="view-events-flyout__contentPanel hide-y-scroll date-range-panel-height" + grow={false} + > + <DateRangeItem timeRange={timeRange as TimeRange} reload={reload} /> + </EuiFlexItem> + <EuiFlexItem className="view-events-flyout__contentPanel hide-y-scroll" grow={5}> + <BaseVisItem embeddable={visEmbeddable as VisualizeEmbeddable} /> + </EuiFlexItem> + <EuiFlexItem className="view-events-flyout__contentPanel show-y-scroll" grow={5}> + <EventsPanel + eventVisEmbeddablesMap={eventVisEmbeddablesMap as EventVisEmbeddablesMap} + /> + </EuiFlexItem> + <EuiFlexItem + className="view-events-flyout__contentPanel hide-y-scroll timeline-panel-height" + grow={false} + > + <TimelinePanel embeddable={timelineVisEmbeddable as VisualizeEmbeddable} /> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlyoutBody> + )} + </EuiFlyout> + </> + ); +} diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/flyout_state.ts b/src/plugins/vis_augmenter/public/view_events_flyout/flyout_state.ts new file mode 100644 index 000000000000..4db90ed977e8 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/flyout_state.ts @@ -0,0 +1,19 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createGetterSetter } from '../../../opensearch_dashboards_utils/public'; + +export enum VIEW_EVENTS_FLYOUT_STATE { + OPEN = 'OPEN', + CLOSED = 'CLOSED', +} + +export const [getFlyoutState, setFlyoutState] = createGetterSetter< + keyof typeof VIEW_EVENTS_FLYOUT_STATE +>(VIEW_EVENTS_FLYOUT_STATE.CLOSED); + +// This is primarily used for mocking this module and each of its fns in tests. +// eslint-disable-next-line import/no-default-export +export default { VIEW_EVENTS_FLYOUT_STATE, getFlyoutState, setFlyoutState }; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/index.ts b/src/plugins/vis_augmenter/public/view_events_flyout/index.ts new file mode 100644 index 000000000000..3f1da0cedbb7 --- /dev/null +++ b/src/plugins/vis_augmenter/public/view_events_flyout/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './actions'; +export * from './components'; +export * from './flyout_state'; diff --git a/src/plugins/vis_type_vega/opensearch_dashboards.json b/src/plugins/vis_type_vega/opensearch_dashboards.json index 17aee4a97232..faf10c831e6e 100644 --- a/src/plugins/vis_type_vega/opensearch_dashboards.json +++ b/src/plugins/vis_type_vega/opensearch_dashboards.json @@ -3,7 +3,14 @@ "version": "opensearchDashboards", "server": true, "ui": true, - "requiredPlugins": ["data", "visualizations", "mapsLegacy", "expressions", "inspector"], + "requiredPlugins": [ + "data", + "visualizations", + "mapsLegacy", + "expressions", + "inspector", + "uiActions" + ], "optionalPlugins": ["home", "usageCollection"], "requiredBundles": [ "opensearchDashboardsUtils", diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 3947808c72c1..35198f846f02 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -32,8 +32,8 @@ import { SearchResponse, SearchParams } from 'elasticsearch'; import { Filter } from 'src/plugins/data/public'; import { DslQuery } from 'src/plugins/data/common'; -import { VisLayerTypes } from 'src/plugins/vis_augmenter/public'; import { Signal } from 'vega'; +import { VisAugmenterEmbeddableConfig, VisLayerTypes } from 'src/plugins/vis_augmenter/public'; import { OpenSearchQueryParser } from './opensearch_query_parser'; import { EmsFileParser } from './ems_file_parser'; import { UrlParser } from './url_parser'; @@ -117,6 +117,7 @@ export interface OpenSearchDashboards { renderer: Renderer; visibleVisLayers?: Map<VisLayerTypes, boolean>; signals?: { [markId: string]: Signal[] }; + visAugmenterConfig?: VisAugmenterEmbeddableConfig; } export interface VegaSpec { diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 8f473b70544b..b73e26ac1af9 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -45,7 +45,7 @@ import { UrlParser } from './url_parser'; import { SearchAPI } from './search_api'; import { TimeCache } from './time_cache'; import { IServiceSettings } from '../../../maps_legacy/public'; -import { VisLayerTypes } from '../../../vis_augmenter/public'; +import { VisAugmenterEmbeddableConfig, VisLayerTypes } from '../../../vis_augmenter/public'; import { Bool, Data, @@ -95,6 +95,7 @@ export class VegaParser { filters: Bool; timeCache: TimeCache; visibleVisLayers: Map<VisLayerTypes, boolean>; + visAugmenterConfig: VisAugmenterEmbeddableConfig; constructor( spec: VegaSpec | string, @@ -106,6 +107,7 @@ export class VegaParser { this.spec = spec as VegaSpec; this.hideWarnings = false; this.visibleVisLayers = new Map<VisLayerTypes, boolean>(); + this.visAugmenterConfig = {} as VisAugmenterEmbeddableConfig; this.error = undefined; this.warnings = []; @@ -163,6 +165,7 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never this._config = this._parseConfig(); this.hideWarnings = !!this._config.hideWarnings; this.visibleVisLayers = this._config.visibleVisLayers; + this.visAugmenterConfig = this._config.visAugmenterConfig; this.useMap = this._config.type === 'map'; this.renderer = this._config.renderer === 'svg' ? 'svg' : 'canvas'; this.tooltips = this._parseTooltips(); @@ -196,15 +199,11 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never }; // If we are showing PointInTimeEventsVisLayers, it means we are showing a base vis + event vis. - // Because this will be using a vconcat spec, we can autosize the width - // via fit-x. Note the regular 'fit' (to autosize width + height) does not work here. + // Because this will be using a vconcat spec, we cannot use the default autosize settings, or set + // top-level height/width values. // See limitations: https://vega.github.io/vega-lite/docs/size.html#limitations const showPointInTimeEvents = this.visibleVisLayers.get(VisLayerTypes.PointInTimeEvents) === true; - const showPointInTimeEventsAutosize = { - type: 'fit-x', - contains: 'padding', - }; let autosize = this.spec.autosize; let useResize = true; @@ -236,14 +235,12 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never contains: string; }; useResize = Boolean(autosize?.type && autosize?.type !== 'none'); + } else if (showPointInTimeEvents) { + autosize = undefined; } else { autosize = defaultAutosize; } - if (showPointInTimeEvents) { - autosize = showPointInTimeEventsAutosize; - } - if ( useResize && ((this.spec.width && this.spec.width !== 'container') || diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.test.js b/src/plugins/vis_type_vega/public/expressions/helpers.test.js index 08e347dca175..b450997d5cdc 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.test.js +++ b/src/plugins/vis_type_vega/public/expressions/helpers.test.js @@ -47,6 +47,9 @@ describe('helpers', function () { describe('setupConfig()', function () { it('check all legend positions', function () { + const visAugmenterConfig = { + some: 'config', + }; const baseConfig = { view: { stroke: null, @@ -59,13 +62,15 @@ describe('helpers', function () { }, kibana: { hideWarnings: true, + visAugmenterConfig, }, }; const positions = ['top', 'right', 'left', 'bottom']; positions.forEach((position) => { const visParams = { legendPosition: position }; baseConfig.legend.orient = position; - expect(setupConfig(visParams)).toStrictEqual(baseConfig); + baseConfig.legend.offset = position === 'top' || position === 'bottom' ? 0 : 18; + expect(setupConfig(visParams, visAugmenterConfig)).toStrictEqual(baseConfig); }); }); }); @@ -187,7 +192,9 @@ describe('helpers', function () { }); describe('createSpecFromDatatable()', function () { - it('build simple line chart"', function () { + // Following 3 tests fail since they are persisting temporal data + // which can cause snapshots to fail depending on the test env they are run on. + it.skip('build simple line chart"', function () { expect( JSON.stringify( createSpecFromDatatable( @@ -199,7 +206,7 @@ describe('helpers', function () { ).toMatchSnapshot(); }); - it('build empty chart if no x-axis is defined"', function () { + it.skip('build empty chart if no x-axis is defined"', function () { expect( JSON.stringify( createSpecFromDatatable( @@ -211,7 +218,7 @@ describe('helpers', function () { ).toMatchSnapshot(); }); - it('build complicated line chart"', function () { + it.skip('build complicated line chart"', function () { expect( JSON.stringify( createSpecFromDatatable( diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.ts b/src/plugins/vis_type_vega/public/expressions/helpers.ts index e904ccb1af87..bbe04ab411e9 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.ts +++ b/src/plugins/vis_type_vega/public/expressions/helpers.ts @@ -9,7 +9,7 @@ import { OpenSearchDashboardsDatatableRow, } from '../../../expressions/public'; import { VislibDimensions, VisParams } from '../../../visualizations/public'; -import { isVisLayerColumn } from '../../../vis_augmenter/public'; +import { isVisLayerColumn, VisAugmenterEmbeddableConfig } from '../../../vis_augmenter/public'; // TODO: move this to the visualization plugin that has VisParams once all of these parameters have been better defined interface ValueAxis { @@ -34,6 +34,18 @@ interface ValueAxis { type: string; } +export interface YAxisConfig { + minExtent: number; + maxExtent: number; + offset: number; + translate: number; + domainWidth: number; + labelPadding: number; + titlePadding: number; + tickOffset: number; + tickSize: number; +} + // Get the first xaxis field as only 1 setup of X Axis will be supported and // there won't be support for split series and split chart const getXAxisId = (dimensions: any, columns: OpenSearchDashboardsDatatableColumn[]): string => { @@ -44,6 +56,15 @@ export const cleanString = (rawString: string): string => { return rawString.replaceAll('"', ''); }; +// When using autosize features of vega-lite, the chart is expected to reposition +// correctly such that there is space for the chart and legend within the canvas. +// This works for horizontal positions (left/right), but breaks for vertical positions +// (top/bottom). To make up for this, we set the offset to 0 for these positions such that +// the chart will not get truncated or potentially cut off within the canvas. +export const calculateLegendOffset = (legendPosition: string): number => + // 18 is the default offset as of vega lite 5 + legendPosition === 'top' || legendPosition === 'bottom' ? 0 : 18; + export const formatDatatable = ( datatable: OpenSearchDashboardsDatatable ): OpenSearchDashboardsDatatable => { @@ -67,7 +88,7 @@ export const formatDatatable = ( return datatable; }; -export const setupConfig = (visParams: VisParams) => { +export const setupConfig = (visParams: VisParams, config: VisAugmenterEmbeddableConfig) => { const legendPosition = visParams.legendPosition; return { view: { @@ -78,12 +99,14 @@ export const setupConfig = (visParams: VisParams) => { }, legend: { orient: legendPosition, + offset: calculateLegendOffset(legendPosition), }, // This is parsed in the VegaParser and hides unnecessary warnings. // For example, 'infinite extent' warnings that cover the chart // when there is empty data for a time series kibana: { hideWarnings: true, + visAugmenterConfig: config, }, }; }; @@ -148,7 +171,8 @@ const isXAxisColumn = (column: OpenSearchDashboardsDatatableColumn): boolean => export const createSpecFromDatatable = ( datatable: OpenSearchDashboardsDatatable, visParams: VisParams, - dimensions: VislibDimensions + dimensions: VislibDimensions, + config: VisAugmenterEmbeddableConfig ): object => { // TODO: we can try to use VegaSpec type but it is currently very outdated, where many // of the fields and sub-fields don't have other optional params that we want for customizing. @@ -159,7 +183,7 @@ export const createSpecFromDatatable = ( spec.data = { values: datatable.rows, }; - spec.config = setupConfig(visParams); + spec.config = setupConfig(visParams, config); // Get the valueAxes data and generate a map to easily fetch the different valueAxes data const valueAxis = new Map(); diff --git a/src/plugins/vis_type_vega/public/expressions/index.ts b/src/plugins/vis_type_vega/public/expressions/index.ts index dce44f56c47d..e85f175d55c6 100644 --- a/src/plugins/vis_type_vega/public/expressions/index.ts +++ b/src/plugins/vis_type_vega/public/expressions/index.ts @@ -5,3 +5,4 @@ export { LineVegaSpecExpressionFunctionDefinition } from './line_vega_spec_fn'; export { VegaExpressionFunctionDefinition } from './vega_fn'; +export { YAxisConfig } from './helpers'; diff --git a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts index 136ba12f8862..61a2f45b421c 100644 --- a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts +++ b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { cloneDeep, isEmpty } from 'lodash'; +import { cloneDeep, isEmpty, get } from 'lodash'; import { i18n } from '@osd/i18n'; import { ExpressionFunctionDefinition, @@ -19,6 +19,7 @@ import { addPointInTimeEventsLayersToSpec, enableVisLayersInSpecConfig, addVisEventSignalsToSpecConfig, + augmentEventChartSpec, } from '../../../vis_augmenter/public'; import { formatDatatable, createSpecFromDatatable } from './helpers'; import { VegaVisualizationDependencies } from '../plugin'; @@ -30,6 +31,7 @@ interface Arguments { visLayers: string | null; visParams: string; dimensions: string; + visAugmenterConfig: string; } export type LineVegaSpecExpressionFunctionDefinition = ExpressionFunctionDefinition< @@ -64,6 +66,11 @@ export const createLineVegaSpecFn = ( default: '""', help: '', }, + visAugmenterConfig: { + types: ['string'], + default: '""', + help: '', + }, }, async fn(input, args, context) { let table = formatDatatable(cloneDeep(input)); @@ -71,6 +78,7 @@ export const createLineVegaSpecFn = ( const visParams = JSON.parse(args.visParams) as VisParams; const dimensions = JSON.parse(args.dimensions) as VislibDimensions; const allVisLayers = (args.visLayers ? JSON.parse(args.visLayers) : []) as VisLayers; + const visAugmenterConfig = JSON.parse(args.visAugmenterConfig); // currently only supporting PointInTimeEventsVisLayer type const pointInTimeEventsVisLayers = allVisLayers.filter((visLayer: VisLayer) => @@ -81,13 +89,19 @@ export const createLineVegaSpecFn = ( table = addPointInTimeEventsLayersToTable(table, dimensions, pointInTimeEventsVisLayers); } - let spec = createSpecFromDatatable(table, visParams, dimensions); + let spec = createSpecFromDatatable(table, visParams, dimensions, visAugmenterConfig); if (!isEmpty(pointInTimeEventsVisLayers) && dimensions.x !== null) { spec = addPointInTimeEventsLayersToSpec(table, dimensions, spec); + // @ts-ignore spec.config = enableVisLayersInSpecConfig(spec, pointInTimeEventsVisLayers); + // @ts-ignore spec.config = addVisEventSignalsToSpecConfig(spec); } + + // Apply other formatting changes to the spec (show vis data, hide axes, etc.) based on the + // vis augmenter config. Mostly used for customizing the views on the view events flyout. + spec = augmentEventChartSpec(visAugmenterConfig, spec); return JSON.stringify(spec); }, }); diff --git a/src/plugins/vis_type_vega/public/expressions/vega_fn.ts b/src/plugins/vis_type_vega/public/expressions/vega_fn.ts index f5ab178cbd74..cd9cf976873a 100644 --- a/src/plugins/vis_type_vega/public/expressions/vega_fn.ts +++ b/src/plugins/vis_type_vega/public/expressions/vega_fn.ts @@ -48,9 +48,12 @@ type Output = Promise<Render<RenderValue>>; interface Arguments { spec: string; + savedObjectId: string; } -export type VisParams = Required<Arguments>; +export interface VisParams { + spec: string; +} export type VegaExpressionFunctionDefinition = ExpressionFunctionDefinition< 'vega', @@ -81,6 +84,11 @@ export const createVegaFn = ( default: '', help: '', }, + savedObjectId: { + types: ['string'], + default: '', + help: '', + }, }, async fn(input, args, context) { const vegaRequestHandler = createVegaRequestHandler(dependencies, context); @@ -100,6 +108,7 @@ export const createVegaFn = ( visType: 'vega', visConfig: { spec: args.spec, + savedObjectId: args.savedObjectId, }, }, }; diff --git a/src/plugins/vis_type_vega/public/plugin.ts b/src/plugins/vis_type_vega/public/plugin.ts index 9751a73ccf91..3967c5351367 100644 --- a/src/plugins/vis_type_vega/public/plugin.ts +++ b/src/plugins/vis_type_vega/public/plugin.ts @@ -51,6 +51,8 @@ import { ConfigSchema } from '../config'; import { getVegaInspectorView } from './vega_inspector'; import { createLineVegaSpecFn } from './expressions/line_vega_spec_fn'; +import { UiActionsStart } from '../../ui_actions/public'; +import { setUiActions } from './services'; /** @internal */ export interface VegaVisualizationDependencies { @@ -73,6 +75,7 @@ export interface VegaPluginSetupDependencies { /** @internal */ export interface VegaPluginStartDependencies { data: DataPublicPluginStart; + uiActions: UiActionsStart; } /** @internal */ @@ -110,9 +113,10 @@ export class VegaPlugin implements Plugin<Promise<void>, void> { visualizations.createBaseVisualization(createVegaTypeDefinition(visualizationDependencies)); } - public start(core: CoreStart, { data }: VegaPluginStartDependencies) { + public start(core: CoreStart, { data, uiActions }: VegaPluginStartDependencies) { setNotifications(core.notifications); setData(data); + setUiActions(uiActions); setInjectedMetadata(core.injectedMetadata); } } diff --git a/src/plugins/vis_type_vega/public/services.ts b/src/plugins/vis_type_vega/public/services.ts index d241b66d472c..b67a0959c63d 100644 --- a/src/plugins/vis_type_vega/public/services.ts +++ b/src/plugins/vis_type_vega/public/services.ts @@ -33,6 +33,7 @@ import { CoreStart, NotificationsStart, IUiSettingsClient } from 'src/core/publi import { DataPublicPluginStart } from '../../data/public'; import { createGetterSetter } from '../../opensearch_dashboards_utils/public'; import { MapsLegacyConfig } from '../../maps_legacy/config'; +import { UiActionsStart } from '../../ui_actions/public'; export const [getData, setData] = createGetterSetter<DataPublicPluginStart>('Data'); @@ -40,6 +41,8 @@ export const [getNotifications, setNotifications] = createGetterSetter<Notificat 'Notifications' ); +export const [getUiActions, setUiActions] = createGetterSetter<UiActionsStart>('UIActions'); + export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); export const [getInjectedMetadata, setInjectedMetadata] = createGetterSetter< diff --git a/src/plugins/vis_type_vega/public/vega_type.ts b/src/plugins/vis_type_vega/public/vega_type.ts index 03fc05fdee89..8a95f5c1c79f 100644 --- a/src/plugins/vis_type_vega/public/vega_type.ts +++ b/src/plugins/vis_type_vega/public/vega_type.ts @@ -74,7 +74,7 @@ export const createVegaTypeDefinition = ( showFilterBar: true, }, getSupportedTriggers: () => { - return [VIS_EVENT_TO_TRIGGER.applyFilter]; + return [VIS_EVENT_TO_TRIGGER.applyFilter, VIS_EVENT_TO_TRIGGER.externalAction]; }, getUsedIndexPattern: async (visParams) => { try { diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 7083a6896064..043cc982dcea 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -86,6 +86,7 @@ export class VegaBaseView { this._destroyHandlers = []; this._initialized = false; this._enableExternalUrls = getEnableExternalUrls(); + this._visInput = opts.visInput; } async init() { @@ -270,11 +271,14 @@ export class VegaBaseView { // space and leave enough space to show the bottom view (the events vis). // Ref: https://vega.github.io/vega-lite/docs/size.html#limitations addPointInTimeEventPadding(view) { - // TODO: 100 is enough padding for now. May need to adjust once the current scrolling/overflow - // issue is handled. See https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3501 + // This value represents the pixel height of the event canvas. It is determined + // based on the event mark size, such that there is sufficient but minimal space + // needed to render the event marks. const eventVisHeight = 100; const height = Math.max(0, this._$container.height()) - eventVisHeight; - view._signals.concat_0_height.value = height; + if (view._signals.concat_0_height !== undefined) { + view._signals.concat_0_height.value = height; + } } setView(view) { diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_view.js index 4e9a2e53c144..cf49ed4a5470 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_view.js @@ -28,7 +28,12 @@ * under the License. */ -import { VisLayerTypes } from '../../../vis_augmenter/public'; +import { get } from 'lodash'; +import { + VisLayerTypes, + calculateYAxisPadding, + VisFlyoutContext, +} from '../../../vis_augmenter/public'; import { vega } from '../lib/vega'; import { VegaBaseView } from './vega_base_view'; @@ -45,9 +50,56 @@ export class VegaView extends VegaBaseView { view.warn = this.onWarn.bind(this); view.error = this.onError.bind(this); if (this._parser.useResize) this.updateVegaSize(view); - if (this._parser.visibleVisLayers?.get(VisLayerTypes.PointInTimeEvents) === true) { + + const showPointInTimeEvents = + this._parser.visibleVisLayers?.get(VisLayerTypes.PointInTimeEvents) === true; + + if (showPointInTimeEvents) { this.addPointInTimeEventPadding(view); + const inFlyout = get(this, '_parser.visAugmenterConfig.inFlyout', false); + const flyoutContext = get( + this, + '_parser.visAugmenterConfig.flyoutContext', + VisFlyoutContext.BASE_VIS + ); + const leftValueAxisPadding = get( + this, + '_parser.visAugmenterConfig.leftValueAxisPadding', + false + ); + const rightValueAxisPadding = get( + this, + '_parser.visAugmenterConfig.rightValueAxisPadding', + false + ); + const yAxisConfig = get(this, '_parser.vlspec.config.axisY', {}); + + // Autosizing is needed here since autosize won't be set correctly when there is PointInTimeEventLayers. + // This is because these layers cause the spec to use `vconcat` under the hood to stack the base chart + // with the event chart. Autosize doesn't work at the vega-lite level, so we set here at the vega level. + // Details here: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3485#issuecomment-1507442348 + view.autosize({ + type: 'fit', + contains: 'padding', + }); + + if (inFlyout) { + const yAxisPadding = calculateYAxisPadding(yAxisConfig); + view.padding({ + ...view.padding(), + // If we are displaying an event chart (no vis data), then we need to offset the chart + // to align the data / events. We do this by checking if padding is needed on the left + // and/or right, and adding padding based on the y axis config. + left: + leftValueAxisPadding && flyoutContext === VisFlyoutContext.EVENT_VIS ? yAxisPadding : 0, + right: + rightValueAxisPadding && flyoutContext === VisFlyoutContext.EVENT_VIS + ? yAxisPadding + : 0, + }); + } } + view.initialize(this._$container.get(0), this._$controls.get(0)); if (this._parser.useHover) view.hover(); diff --git a/src/plugins/vis_type_vega/public/vega_visualization.js b/src/plugins/vis_type_vega/public/vega_visualization.js index 379670bda413..cf4f0907acd4 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.js +++ b/src/plugins/vis_type_vega/public/vega_visualization.js @@ -29,6 +29,7 @@ */ import { i18n } from '@osd/i18n'; +import { get } from 'lodash'; import { getNotifications, getData } from './services'; export const createVegaVisualization = ({ getServiceSettings }) => @@ -91,6 +92,9 @@ export const createVegaVisualization = ({ getServiceSettings }) => filterManager, timefilter, externalAction: this._vis.API.events.externalAction, + visInput: { + savedObjectId: get(this._vis, 'params.savedObjectId'), + }, }; if (vegaParser.useMap) { diff --git a/src/plugins/vis_type_vislib/public/line_to_expression.ts b/src/plugins/vis_type_vislib/public/line_to_expression.ts index 79a716f2e800..1fb698a52d8b 100644 --- a/src/plugins/vis_type_vislib/public/line_to_expression.ts +++ b/src/plugins/vis_type_vislib/public/line_to_expression.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { get } from 'lodash'; import { buildVislibDimensions, Vis, VislibDimensions } from '../../visualizations/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import { OpenSearchaggsExpressionFunctionDefinition } from '../../data/common/search/expressions'; @@ -10,7 +11,7 @@ import { VegaExpressionFunctionDefinition, LineVegaSpecExpressionFunctionDefinition, } from '../../vis_type_vega/public'; -import { isEligibleForVisLayers } from '../../vis_augmenter/public'; +import { isEligibleForVisLayers, VisAugmenterEmbeddableConfig } from '../../vis_augmenter/public'; export const toExpressionAst = async (vis: Vis, params: any) => { // Construct the existing expr fns that are ran for vislib line chart, up until the render fn. @@ -42,6 +43,12 @@ export const toExpressionAst = async (vis: Vis, params: any) => { const ast = buildExpression([opensearchaggsFn, vislib]); return ast.toAst(); } else { + const visAugmenterConfig = get( + params, + 'visAugmenterConfig', + {} + ) as VisAugmenterEmbeddableConfig; + // adding the new expr fn here that takes the datatable and converts to a vega spec const vegaSpecFn = buildExpressionFunction<LineVegaSpecExpressionFunctionDefinition>( 'line_vega_spec', @@ -49,6 +56,7 @@ export const toExpressionAst = async (vis: Vis, params: any) => { visLayers: JSON.stringify(params.visLayers), visParams: JSON.stringify(vis.params), dimensions: JSON.stringify(dimensions), + visAugmenterConfig: JSON.stringify(visAugmenterConfig), } ); const vegaSpecFnExpressionBuilder = buildExpression([vegaSpecFn]); @@ -57,6 +65,7 @@ export const toExpressionAst = async (vis: Vis, params: any) => { // spec via 'line_vega_spec' fn, then set as the arg for the final 'vega' fn const vegaFn = buildExpressionFunction<VegaExpressionFunctionDefinition>('vega', { spec: vegaSpecFnExpressionBuilder, + savedObjectId: get(vis, 'id', ''), }); const ast = buildExpression([opensearchaggsFn, vegaFn]); return ast.toAst(); diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 5ceda93f6bc4..be6c832d3385 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -72,8 +72,16 @@ import { getAugmentVisSavedObjs, buildPipelineFromAugmentVisSavedObjs, getAnyErrors, + VisLayerErrorTypes, + AugmentVisContext, } from '../../../vis_augmenter/public'; import { VisSavedObject } from '../types'; +import { + PointInTimeEventsVisLayer, + VisLayer, + VisLayerTypes, + VisAugmenterEmbeddableConfig, +} from '../../../vis_augmenter/public'; const getKeys = <T extends {}>(o: T): Array<keyof T> => Object.keys(o) as Array<keyof T>; @@ -92,6 +100,7 @@ export interface VisualizeInput extends EmbeddableInput { }; savedVis?: SerializedVis; table?: unknown; + visAugmenterConfig?: VisAugmenterEmbeddableConfig; } export interface VisualizeOutput extends EmbeddableOutput { @@ -123,7 +132,7 @@ export class VisualizeEmbeddable private visCustomizations?: Pick<VisualizeInput, 'vis' | 'table'>; private subscriptions: Subscription[] = []; private expression: string = ''; - private vis: Vis; + public vis: Vis; private domNode: any; public readonly type = VISUALIZE_EMBEDDABLE_TYPE; private autoRefreshFetchSubscription: Subscription; @@ -137,6 +146,8 @@ export class VisualizeEmbeddable >; private savedVisualizationsLoader?: SavedVisualizationsLoader; private savedAugmentVisLoader?: SavedAugmentVisLoader; + public visLayers?: VisLayer[]; + private visAugmenterConfig?: VisAugmenterEmbeddableConfig; constructor( timefilter: TimefilterContract, @@ -172,6 +183,7 @@ export class VisualizeEmbeddable this.attributeService = attributeService; this.savedVisualizationsLoader = savedVisualizationsLoader; this.savedAugmentVisLoader = savedAugmentVisLoader; + this.visAugmenterConfig = initialInput.visAugmenterConfig; this.autoRefreshFetchSubscription = timefilter .getAutoRefreshFetch$() .subscribe(this.updateHandler.bind(this)); @@ -347,6 +359,10 @@ export class VisualizeEmbeddable timeFieldName: this.vis.data.indexPattern?.timeFieldName!, ...event.data, }; + } else if (triggerId === VIS_EVENT_TO_TRIGGER.externalAction) { + context = { + savedObjectId: this.vis.id, + } as AugmentVisContext; } else { context = { embeddable: this, @@ -405,13 +421,15 @@ export class VisualizeEmbeddable this.abortController = new AbortController(); const abortController = this.abortController; - const visLayers = await this.fetchVisLayers(expressionParams, abortController); + // By waiting for this to complete, this.visLayers will be populated + await this.populateVisLayers(); this.expression = await buildPipeline(this.vis, { timefilter: this.timefilter, timeRange: this.timeRange, abortSignal: this.abortController!.signal, - visLayers, + visLayers: this.visLayers, + visAugmenterConfig: this.visAugmenterConfig, }); if (this.handler && !abortController.signal.aborted) { @@ -481,6 +499,22 @@ export class VisualizeEmbeddable ); }; + /** + * Fetches any VisLayers, and filters out to only include ones in the list of + * input resource IDs, if specified. Assigns them to this.visLayers. + * Note this fn is public so we can fetch vislayers on demand when needed, + * e.g., generating other vis embeddables in the view events flyout. + */ + public async populateVisLayers(): Promise<void> { + const visLayers = await this.fetchVisLayers(); + this.visLayers = + this.visAugmenterConfig?.visLayerResourceIds === undefined + ? visLayers + : visLayers.filter((visLayer) => + this.visAugmenterConfig?.visLayerResourceIds?.includes(visLayer.pluginResource.id) + ); + } + /** * Collects any VisLayers from plugin expressions functions * by fetching all AugmentVisSavedObjects that match the vis @@ -491,20 +525,24 @@ export class VisualizeEmbeddable * is used below. For more details, see * https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 */ - fetchVisLayers = async ( - expressionParams: IExpressionLoaderParams, - abortController: AbortController - ): Promise<VisLayers> => { + fetchVisLayers = async (): Promise<VisLayers> => { try { + const expressionParams: IExpressionLoaderParams = { + searchContext: { + timeRange: this.timeRange, + query: this.input.query, + filters: this.input.filters, + }, + uiState: this.vis.uiState, + inspectorAdapters: this.inspectorAdapters, + }; + const aborted = get(this.abortController, 'signal.aborted', false) as boolean; const augmentVisSavedObjs = await getAugmentVisSavedObjs( this.vis.id, this.savedAugmentVisLoader ); - if ( - !isEmpty(augmentVisSavedObjs) && - !abortController.signal.aborted && - isEligibleForVisLayers(this.vis) - ) { + + if (!isEmpty(augmentVisSavedObjs) && !aborted && isEligibleForVisLayers(this.vis)) { const visLayersPipeline = buildPipelineFromAugmentVisSavedObjs(augmentVisSavedObjs); // The initial input for the pipeline will just be an empty arr of VisLayers. As plugin // expression functions are ran, they will incrementally append their generated VisLayers to it. diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index ffc24a81b381..957c9d8c80cd 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -41,7 +41,12 @@ export function plugin(initializerContext: PluginInitializerContext) { /** @public static code */ export { Vis } from './vis'; export { TypesService } from './vis_types/types_service'; -export { VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER } from './embeddable'; +export { + VISUALIZE_EMBEDDABLE_TYPE, + VIS_EVENT_TO_TRIGGER, + VisualizeEmbeddable, + DisabledLabEmbeddable, +} from './embeddable'; export { VisualizationContainer, VisualizationNoResults } from './components'; export { getSchemas as getVisSchemas, diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index d751e088c99d..de41a7a48c02 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -33,7 +33,7 @@ import moment from 'moment'; import { formatExpression, SerializedFieldFormat } from '../../../../plugins/expressions/public'; import { IAggConfig, search, TimefilterContract } from '../../../../plugins/data/public'; import { Vis, VisParams } from '../types'; -import { VisLayers } from '../../../../plugins/vis_augmenter/public'; +import { VisAugmenterEmbeddableConfig, VisLayers } from '../../../../plugins/vis_augmenter/public'; const { isDateHistogramBucketAggConfig } = search.aggs; @@ -88,6 +88,7 @@ export interface BuildPipelineParams { timeRange?: any; abortSignal?: AbortSignal; visLayers?: VisLayers; + visAugmenterConfig?: VisAugmenterEmbeddableConfig; } const vislibCharts: string[] = [ From 538d91ebc813a49d3a9263669f67cf7b57e653c7 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 31 May 2023 10:03:55 -0700 Subject: [PATCH 21/37] Clean up stale `augment-vis` saved objs (#4059) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../opensearch_dashboards.json | 2 +- .../saved_objects_management/public/index.ts | 2 + .../management_section/mount_section.tsx | 3 +- .../object_view/saved_object_view.tsx | 7 + .../saved_objects_edition_page.tsx | 4 + .../public/plugin.test.ts | 8 +- .../saved_objects_management/public/plugin.ts | 11 +- .../public/triggers/index.ts | 9 ++ .../triggers/saved_object_delete_trigger.ts | 18 +++ .../public/ui_actions_bootstrap.ts | 22 +++ .../vis_augmenter/opensearch_dashboards.json | 2 +- .../vis_augmenter/public/actions/index.ts | 10 ++ .../plugin_resource_delete_action.test.ts | 81 +++++++++++ .../actions/plugin_resource_delete_action.ts | 47 ++++++ .../saved_object_delete_action.test.ts | 84 +++++++++++ .../actions/saved_object_delete_action.ts | 53 +++++++ src/plugins/vis_augmenter/public/index.ts | 2 + src/plugins/vis_augmenter/public/mocks.ts | 2 + src/plugins/vis_augmenter/public/plugin.ts | 9 +- .../saved_augment_vis/_saved_augment_vis.ts | 18 ++- src/plugins/vis_augmenter/public/services.ts | 13 ++ .../vis_augmenter/public/triggers/index.ts | 9 ++ .../plugin_resource_delete_trigger.ts | 18 +++ src/plugins/vis_augmenter/public/types.ts | 1 + .../public/ui_actions_bootstrap.ts | 57 ++++++-- .../vis_augmenter/public/utils/utils.test.ts | 134 +++++++++++++++++- .../vis_augmenter/public/utils/utils.ts | 57 +++++++- .../actions/open_events_flyout.tsx | 5 +- .../actions/open_events_flyout_action.test.ts | 42 ++++-- .../actions/open_events_flyout_action.ts | 3 +- .../actions/view_events_option_action.test.ts | 40 +++--- .../actions/view_events_option_action.tsx | 3 +- .../public/embeddable/visualize_embeddable.ts | 22 +-- src/plugins/visualizations/public/index.ts | 1 + src/plugins/visualizations/public/plugin.ts | 17 +-- .../visualize/opensearch_dashboards.json | 3 +- .../components/visualize_listing.tsx | 30 ++-- src/plugins/visualize/public/plugin.ts | 5 +- src/plugins/visualize/public/services.ts | 3 + 39 files changed, 757 insertions(+), 100 deletions(-) create mode 100644 src/plugins/saved_objects_management/public/triggers/index.ts create mode 100644 src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts create mode 100644 src/plugins/saved_objects_management/public/ui_actions_bootstrap.ts create mode 100644 src/plugins/vis_augmenter/public/actions/index.ts create mode 100644 src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts create mode 100644 src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.ts create mode 100644 src/plugins/vis_augmenter/public/actions/saved_object_delete_action.test.ts create mode 100644 src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts create mode 100644 src/plugins/vis_augmenter/public/triggers/index.ts create mode 100644 src/plugins/vis_augmenter/public/triggers/plugin_resource_delete_trigger.ts diff --git a/src/plugins/saved_objects_management/opensearch_dashboards.json b/src/plugins/saved_objects_management/opensearch_dashboards.json index 1de1260afceb..f76b69999ecb 100644 --- a/src/plugins/saved_objects_management/opensearch_dashboards.json +++ b/src/plugins/saved_objects_management/opensearch_dashboards.json @@ -3,7 +3,7 @@ "version": "opensearchDashboards", "server": true, "ui": true, - "requiredPlugins": ["management", "data"], + "requiredPlugins": ["management", "data", "uiActions"], "optionalPlugins": [ "dashboard", "visualizations", diff --git a/src/plugins/saved_objects_management/public/index.ts b/src/plugins/saved_objects_management/public/index.ts index 2377afe175c4..317b3079efa0 100644 --- a/src/plugins/saved_objects_management/public/index.ts +++ b/src/plugins/saved_objects_management/public/index.ts @@ -48,6 +48,8 @@ export { } from './services'; export { ProcessedImportResponse, processImportResponse, FailedImport } from './lib'; export { SavedObjectRelation, SavedObjectWithMetadata, SavedObjectMetadata } from './types'; +export { SAVED_OBJECT_DELETE_TRIGGER, savedObjectDeleteTrigger } from './triggers'; +export { SavedObjectDeleteContext } from './ui_actions_bootstrap'; export function plugin(initializerContext: PluginInitializerContext) { return new SavedObjectsManagementPlugin(); diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index 2c42df5c7824..a1c7b5343eb1 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -59,7 +59,7 @@ export const mountManagementSection = async ({ mountParams, serviceRegistry, }: MountParams) => { - const [coreStart, { data }, pluginStart] = await core.getStartServices(); + const [coreStart, { data, uiActions }, pluginStart] = await core.getStartServices(); const { element, history, setBreadcrumbs } = mountParams; if (allowedObjectTypes === undefined) { allowedObjectTypes = await getAllowedTypes(coreStart.http); @@ -88,6 +88,7 @@ export const mountManagementSection = async ({ <Suspense fallback={<EuiLoadingSpinner />}> <SavedObjectsEditionPage coreStart={coreStart} + uiActionsStart={uiActions} serviceRegistry={serviceRegistry} setBreadcrumbs={setBreadcrumbs} history={history} diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index 8197fd0a2b51..e5d37e0af8ff 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -43,6 +43,8 @@ import { ISavedObjectsManagementServiceRegistry } from '../../services'; import { Header, NotFoundErrors, Intro, Form } from './components'; import { canViewInApp } from '../../lib'; import { SubmittedFormData } from '../types'; +import { UiActionsStart } from '../../../../ui_actions/public'; +import { SAVED_OBJECT_DELETE_TRIGGER } from '../../triggers'; interface SavedObjectEditionProps { id: string; @@ -51,6 +53,7 @@ interface SavedObjectEditionProps { capabilities: Capabilities; overlays: OverlayStart; notifications: NotificationsStart; + uiActions: UiActionsStart; notFoundType?: string; savedObjectsClient: SavedObjectsClientContract; history: ScopedHistory; @@ -168,7 +171,11 @@ export class SavedObjectEdition extends Component< } ); if (confirmed) { + this.props.uiActions + .getTrigger(SAVED_OBJECT_DELETE_TRIGGER) + .exec({ type, savedObjectId: id }); await savedObjectsClient.delete(type, id); + notifications.toasts.addSuccess(`Deleted ${this.formatTitle(object)} ${type} object`); this.redirectToListing(); } diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx index 51868e3e12c8..49513e484272 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx @@ -33,16 +33,19 @@ import { useParams, useLocation } from 'react-router-dom'; import { parse } from 'query-string'; import { i18n } from '@osd/i18n'; import { CoreStart, ChromeBreadcrumb, ScopedHistory } from 'src/core/public'; +import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { ISavedObjectsManagementServiceRegistry } from '../services'; import { SavedObjectEdition } from './object_view'; const SavedObjectsEditionPage = ({ coreStart, + uiActionsStart, serviceRegistry, setBreadcrumbs, history, }: { coreStart: CoreStart; + uiActionsStart: UiActionsStart; serviceRegistry: ISavedObjectsManagementServiceRegistry; setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; history: ScopedHistory; @@ -79,6 +82,7 @@ const SavedObjectsEditionPage = ({ savedObjectsClient={coreStart.savedObjects.client} overlays={coreStart.overlays} notifications={coreStart.notifications} + uiActions={uiActionsStart} capabilities={capabilities} notFoundType={query.notFound as string} history={history} diff --git a/src/plugins/saved_objects_management/public/plugin.test.ts b/src/plugins/saved_objects_management/public/plugin.test.ts index e55760846a5f..c8e762f73dcc 100644 --- a/src/plugins/saved_objects_management/public/plugin.test.ts +++ b/src/plugins/saved_objects_management/public/plugin.test.ts @@ -32,6 +32,7 @@ import { coreMock } from '../../../core/public/mocks'; import { homePluginMock } from '../../home/public/mocks'; import { managementPluginMock } from '../../management/public/mocks'; import { dataPluginMock } from '../../data/public/mocks'; +import { uiActionsPluginMock } from '../../ui_actions/public/mocks'; import { SavedObjectsManagementPlugin } from './plugin'; describe('SavedObjectsManagementPlugin', () => { @@ -48,8 +49,13 @@ describe('SavedObjectsManagementPlugin', () => { }); const homeSetup = homePluginMock.createSetupContract(); const managementSetup = managementPluginMock.createSetupContract(); + const uiActionsSetup = uiActionsPluginMock.createSetupContract(); - await plugin.setup(coreSetup, { home: homeSetup, management: managementSetup }); + await plugin.setup(coreSetup, { + home: homeSetup, + management: managementSetup, + uiActions: uiActionsSetup, + }); expect(homeSetup.featureCatalogue.register).toHaveBeenCalledTimes(1); expect(homeSetup.featureCatalogue.register).toHaveBeenCalledWith( diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index b2bcb614c50a..43356eb8f9e5 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -33,6 +33,7 @@ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { VisBuilderStart } from '../../vis_builder/public'; import { ManagementSetup } from '../../management/public'; +import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public'; import { DataPublicPluginStart } from '../../data/public'; import { DashboardStart } from '../../dashboard/public'; import { DiscoverStart } from '../../discover/public'; @@ -53,6 +54,7 @@ import { ISavedObjectsManagementServiceRegistry, } from './services'; import { registerServices } from './register_services'; +import { bootstrap } from './ui_actions_bootstrap'; export interface SavedObjectsManagementPluginSetup { actions: SavedObjectsManagementActionServiceSetup; @@ -70,6 +72,7 @@ export interface SavedObjectsManagementPluginStart { export interface SetupDependencies { management: ManagementSetup; home?: HomePublicPluginSetup; + uiActions: UiActionsSetup; } export interface StartDependencies { @@ -79,6 +82,7 @@ export interface StartDependencies { visAugmenter?: VisAugmenterStart; discover?: DiscoverStart; visBuilder?: VisBuilderStart; + uiActions: UiActionsStart; } export class SavedObjectsManagementPlugin @@ -96,7 +100,7 @@ export class SavedObjectsManagementPlugin public setup( core: CoreSetup<StartDependencies, SavedObjectsManagementPluginStart>, - { home, management }: SetupDependencies + { home, management, uiActions }: SetupDependencies ): SavedObjectsManagementPluginSetup { const actionSetup = this.actionService.setup(); const columnSetup = this.columnService.setup(); @@ -136,6 +140,9 @@ export class SavedObjectsManagementPlugin }, }); + // sets up the context mappings and registers any triggers/actions for the plugin + bootstrap(uiActions); + // depends on `getStartServices`, should not be awaited registerServices(this.serviceRegistry, core.getStartServices); @@ -147,7 +154,7 @@ export class SavedObjectsManagementPlugin }; } - public start(core: CoreStart, { data }: StartDependencies) { + public start(core: CoreStart, { data, uiActions }: StartDependencies) { const actionStart = this.actionService.start(); const columnStart = this.columnService.start(); const namespaceStart = this.namespaceService.start(); diff --git a/src/plugins/saved_objects_management/public/triggers/index.ts b/src/plugins/saved_objects_management/public/triggers/index.ts new file mode 100644 index 000000000000..001864544cd3 --- /dev/null +++ b/src/plugins/saved_objects_management/public/triggers/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + SAVED_OBJECT_DELETE_TRIGGER, + savedObjectDeleteTrigger, +} from './saved_object_delete_trigger'; diff --git a/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts b/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts new file mode 100644 index 000000000000..9c3ced212d5d --- /dev/null +++ b/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts @@ -0,0 +1,18 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { Trigger } from '../../../ui_actions/public'; + +export const SAVED_OBJECT_DELETE_TRIGGER = 'SAVED_OBJECT_DELETE_TRIGGER'; +export const savedObjectDeleteTrigger: Trigger<'SAVED_OBJECT_DELETE_TRIGGER'> = { + id: SAVED_OBJECT_DELETE_TRIGGER, + title: i18n.translate('savedObjectsManagement.triggers.savedObjectDeleteTitle', { + defaultMessage: 'Saved object delete', + }), + description: i18n.translate('savedObjectsManagement.triggers.savedObjectDeleteDescription', { + defaultMessage: 'Perform additional actions after deleting a saved object', + }), +}; diff --git a/src/plugins/saved_objects_management/public/ui_actions_bootstrap.ts b/src/plugins/saved_objects_management/public/ui_actions_bootstrap.ts new file mode 100644 index 000000000000..e005ba660dee --- /dev/null +++ b/src/plugins/saved_objects_management/public/ui_actions_bootstrap.ts @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { UiActionsSetup } from '../../ui_actions/public'; +import { SAVED_OBJECT_DELETE_TRIGGER, savedObjectDeleteTrigger } from './triggers'; + +export interface SavedObjectDeleteContext { + type: string; + savedObjectId: string; +} + +declare module '../../ui_actions/public' { + export interface TriggerContextMapping { + [SAVED_OBJECT_DELETE_TRIGGER]: SavedObjectDeleteContext; + } +} + +export const bootstrap = (uiActions: UiActionsSetup) => { + uiActions.registerTrigger(savedObjectDeleteTrigger); +}; diff --git a/src/plugins/vis_augmenter/opensearch_dashboards.json b/src/plugins/vis_augmenter/opensearch_dashboards.json index 007cae78290b..9026bdd24859 100644 --- a/src/plugins/vis_augmenter/opensearch_dashboards.json +++ b/src/plugins/vis_augmenter/opensearch_dashboards.json @@ -12,5 +12,5 @@ "uiActions", "embeddable" ], - "requiredBundles": ["opensearchDashboardsReact"] + "requiredBundles": ["opensearchDashboardsReact", "savedObjectsManagement"] } diff --git a/src/plugins/vis_augmenter/public/actions/index.ts b/src/plugins/vis_augmenter/public/actions/index.ts new file mode 100644 index 000000000000..893032f86813 --- /dev/null +++ b/src/plugins/vis_augmenter/public/actions/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + PLUGIN_RESOURCE_DELETE_ACTION, + PluginResourceDeleteAction, +} from './plugin_resource_delete_action'; +export { SAVED_OBJECT_DELETE_ACTION, SavedObjectDeleteAction } from './saved_object_delete_action'; diff --git a/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts b/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts new file mode 100644 index 000000000000..0060b7837f1f --- /dev/null +++ b/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createPointInTimeEventsVisLayer } from '../mocks'; +import { generateAugmentVisSavedObject } from '../saved_augment_vis'; +import { PluginResourceDeleteAction } from './plugin_resource_delete_action'; + +const sampleSavedObj = generateAugmentVisSavedObject( + 'test-id', + { + type: 'PointInTimeEvents', + name: 'test-fn-name', + args: {}, + }, + 'test-vis-id', + 'test-origin-plugin', + { + type: 'test-resource-type', + id: 'test-resource-id', + } +); + +const sampleVisLayer = createPointInTimeEventsVisLayer(); + +describe('SavedObjectDeleteAction', () => { + it('is incompatible with invalid saved obj list', async () => { + const action = new PluginResourceDeleteAction(); + const visLayers = [sampleVisLayer]; + // @ts-ignore + expect(await action.isCompatible({ savedObjs: null, visLayers })).toBe(false); + // @ts-ignore + expect(await action.isCompatible({ savedObjs: undefined, visLayers })).toBe(false); + expect(await action.isCompatible({ savedObjs: [], visLayers })).toBe(false); + }); + + it('is incompatible with invalid vislayer list', async () => { + const action = new PluginResourceDeleteAction(); + const savedObjs = [sampleSavedObj]; + // @ts-ignore + expect(await action.isCompatible({ savedObjs, visLayers: null })).toBe(false); + // @ts-ignore + expect(await action.isCompatible({ savedObjs, visLayers: undefined })).toBe(false); + expect(await action.isCompatible({ savedObjs, visLayers: [] })).toBe(false); + }); + + it('execute throws error if incompatible saved objs list', async () => { + const action = new PluginResourceDeleteAction(); + async function check(savedObjs: any, visLayers: any) { + await action.execute({ savedObjs, visLayers }); + } + await expect(check(null, [sampleVisLayer])).rejects.toThrow(Error); + }); + + it('execute throws error if incompatible vis layer list', async () => { + const action = new PluginResourceDeleteAction(); + async function check(savedObjs: any, visLayers: any) { + await action.execute({ savedObjs, visLayers }); + } + await expect(check([sampleSavedObj], null)).rejects.toThrow(Error); + }); + + it('execute is successful if valid saved obj and vis layer lists', async () => { + const action = new PluginResourceDeleteAction(); + async function check(savedObjs: any, visLayers: any) { + await action.execute({ savedObjs, visLayers }); + } + await expect(check([sampleSavedObj], [sampleVisLayer])).resolves; + }); + + it('Returns display name', async () => { + const action = new PluginResourceDeleteAction(); + expect(action.getDisplayName()).toBeDefined(); + }); + + it('Returns icon type', async () => { + const action = new PluginResourceDeleteAction(); + expect(action.getIconType()).toBeDefined(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.ts b/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.ts new file mode 100644 index 000000000000..6e3939820d28 --- /dev/null +++ b/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.ts @@ -0,0 +1,47 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isEmpty } from 'lodash'; +import { i18n } from '@osd/i18n'; +import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; +import { Action, IncompatibleActionError } from '../../../ui_actions/public'; +import { getSavedAugmentVisLoader } from '../services'; +import { PluginResourceDeleteContext } from '../ui_actions_bootstrap'; +import { cleanupStaleObjects } from '../utils'; + +export const PLUGIN_RESOURCE_DELETE_ACTION = 'PLUGIN_RESOURCE_DELETE_ACTION'; + +export class PluginResourceDeleteAction implements Action<PluginResourceDeleteContext> { + public readonly type = PLUGIN_RESOURCE_DELETE_ACTION; + public readonly id = PLUGIN_RESOURCE_DELETE_ACTION; + public order = 1; + + public getIconType(): EuiIconType { + return `trash`; + } + + public getDisplayName() { + return i18n.translate('dashboard.actions.deleteSavedObject.name', { + defaultMessage: + 'Clean up all augment-vis saved objects associated to the deleted visualization', + }); + } + + public async isCompatible({ savedObjs, visLayers }: PluginResourceDeleteContext) { + return !isEmpty(savedObjs) && !isEmpty(visLayers); + } + + /** + * If we have just collected all of the saved objects and generated vis layers, + * sweep through them all and if any of the resources are deleted, delete those + * corresponding saved objects + */ + public async execute({ savedObjs, visLayers }: PluginResourceDeleteContext) { + if (!(await this.isCompatible({ savedObjs, visLayers }))) { + throw new IncompatibleActionError(); + } + cleanupStaleObjects(savedObjs, visLayers, getSavedAugmentVisLoader()); + } +} diff --git a/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.test.ts b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.test.ts new file mode 100644 index 000000000000..cec742a218cc --- /dev/null +++ b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectDeleteAction } from './saved_object_delete_action'; +import services from '../services'; + +jest.mock('src/plugins/vis_augmenter/public/services.ts', () => { + return { + getSavedAugmentVisLoader: () => { + return { + delete: () => {}, + findAll: () => { + return { + hits: [], + }; + }, + }; + }, + }; +}); + +describe('SavedObjectDeleteAction', () => { + it('is incompatible with invalid types', async () => { + const action = new SavedObjectDeleteAction(); + const savedObjectId = '1234'; + // @ts-ignore + expect(await action.isCompatible({ type: null, savedObjectId })).toBe(false); + // @ts-ignore + expect(await action.isCompatible({ type: undefined, savedObjectId })).toBe(false); + expect(await action.isCompatible({ type: '', savedObjectId })).toBe(false); + expect(await action.isCompatible({ type: 'not-visualization-type', savedObjectId })).toBe( + false + ); + expect(await action.isCompatible({ type: 'savedSearch', savedObjectId })).toBe(false); + }); + + it('is incompatible with invalid saved obj ids', async () => { + const action = new SavedObjectDeleteAction(); + const type = 'visualization'; + // @ts-ignore + expect(await action.isCompatible({ type, savedObjectId: null })).toBe(false); + // @ts-ignore + expect(await action.isCompatible({ type, savedObjectId: undefined })).toBe(false); + expect(await action.isCompatible({ type, savedObjectId: '' })).toBe(false); + }); + + it('execute throws error if incompatible type', async () => { + const action = new SavedObjectDeleteAction(); + async function check(type: any, id: any) { + await action.execute({ type, savedObjectId: id }); + } + await expect(check(null, '1234')).rejects.toThrow(Error); + }); + + it('execute throws error if incompatible saved obj id', async () => { + const action = new SavedObjectDeleteAction(); + async function check(type: any, id: any) { + await action.execute({ type, savedObjectId: id }); + } + await expect(check('visualization', null)).rejects.toThrow(Error); + }); + + it('execute is successful if valid type and saved obj id', async () => { + const getLoaderSpy = jest.spyOn(services, 'getSavedAugmentVisLoader'); + const action = new SavedObjectDeleteAction(); + async function check(type: any, id: any) { + await action.execute({ type, savedObjectId: id }); + } + await expect(check('visualization', 'test-id')).resolves; + expect(getLoaderSpy).toHaveBeenCalledTimes(1); + }); + + it('Returns display name', async () => { + const action = new SavedObjectDeleteAction(); + expect(action.getDisplayName()).toBeDefined(); + }); + + it('Returns icon type', async () => { + const action = new SavedObjectDeleteAction(); + expect(action.getIconType()).toBeDefined(); + }); +}); diff --git a/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts new file mode 100644 index 000000000000..2db6d1210448 --- /dev/null +++ b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts @@ -0,0 +1,53 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isEmpty } from 'lodash'; +import { i18n } from '@osd/i18n'; +import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; +import { Action, IncompatibleActionError } from '../../../ui_actions/public'; +import { getAllAugmentVisSavedObjs } from '../utils'; +import { getSavedAugmentVisLoader } from '../services'; +import { SavedObjectDeleteContext } from '../ui_actions_bootstrap'; + +export const SAVED_OBJECT_DELETE_ACTION = 'SAVED_OBJECT_DELETE_ACTION'; + +export class SavedObjectDeleteAction implements Action<SavedObjectDeleteContext> { + public readonly type = SAVED_OBJECT_DELETE_ACTION; + public readonly id = SAVED_OBJECT_DELETE_ACTION; + public order = 1; + + public getIconType(): EuiIconType { + return `trash`; + } + + public getDisplayName() { + return i18n.translate('dashboard.actions.deleteSavedObject.name', { + defaultMessage: 'Clean up augment-vis saved objects associated to a deleted vis', + }); + } + + public async isCompatible({ type, savedObjectId }: SavedObjectDeleteContext) { + return type === 'visualization' && (savedObjectId ? true : false); + } + + /** + * If deletion of a vis saved object has been triggered, we want to clean up + * any augment-vis saved objects that have a reference to this vis since it + * is now stale. + */ + public async execute({ type, savedObjectId }: SavedObjectDeleteContext) { + if (!(await this.isCompatible({ type, savedObjectId }))) { + throw new IncompatibleActionError(); + } + + const loader = getSavedAugmentVisLoader(); + const allAugmentVisObjs = await getAllAugmentVisSavedObjs(loader); + const augmentVisIdsToDelete = allAugmentVisObjs + .filter((augmentVisObj) => augmentVisObj.visId === savedObjectId) + .map((augmentVisObj) => augmentVisObj.id as string); + + if (!isEmpty(augmentVisIdsToDelete)) loader.delete(augmentVisIdsToDelete); + } +} diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index 9a4a5faeb803..3fc82ecc0267 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -34,3 +34,5 @@ export * from './constants'; export * from './vega'; export * from './saved_augment_vis'; export * from './test_constants'; +export * from './triggers'; +export * from './actions'; diff --git a/src/plugins/vis_augmenter/public/mocks.ts b/src/plugins/vis_augmenter/public/mocks.ts index 34d6d81a1ef6..d3ff8422287d 100644 --- a/src/plugins/vis_augmenter/public/mocks.ts +++ b/src/plugins/vis_augmenter/public/mocks.ts @@ -90,6 +90,7 @@ const PLUGIN_RESOURCE = { } as PluginResource; const EVENT_COUNT = 3; const ERROR_MESSAGE = 'test-error-message'; +const EVENT_TYPE = 'test-event-type'; export const createPluginResource = ( type: string = PLUGIN_RESOURCE.type, @@ -171,6 +172,7 @@ export const createPointInTimeEventsVisLayer = ( type: VisLayerTypes.PointInTimeEvents, pluginResource, events, + pluginEventType: EVENT_TYPE, error: error ? { type: VisLayerErrorTypes.FETCH_FAILURE, diff --git a/src/plugins/vis_augmenter/public/plugin.ts b/src/plugins/vis_augmenter/public/plugin.ts index d7b09d57d790..9760bfd75b2d 100644 --- a/src/plugins/vis_augmenter/public/plugin.ts +++ b/src/plugins/vis_augmenter/public/plugin.ts @@ -8,7 +8,6 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../.. import { visLayers } from './expressions'; import { setSavedAugmentVisLoader, setUISettings } from './services'; import { createSavedAugmentVisLoader, SavedAugmentVisLoader } from './saved_augment_vis'; -import { registerTriggersAndActions } from './ui_actions_bootstrap'; import { UiActionsStart } from '../../ui_actions/public'; import { setUiActions, @@ -21,6 +20,7 @@ import { EmbeddableStart } from '../../embeddable/public'; import { DataPublicPluginStart } from '../../data/public'; import { VisualizationsStart } from '../../visualizations/public'; import { VIEW_EVENTS_FLYOUT_STATE, setFlyoutState } from './view_events_flyout'; +import { bootstrapUiActions } from './ui_actions_bootstrap'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface VisAugmenterSetup {} @@ -51,6 +51,7 @@ export class VisAugmenterPlugin ): VisAugmenterSetup { expressions.registerType(visLayers); setUISettings(core.uiSettings); + return {}; } @@ -65,8 +66,6 @@ export class VisAugmenterPlugin setCore(core); setFlyoutState(VIEW_EVENTS_FLYOUT_STATE.CLOSED); - registerTriggersAndActions(core); - const savedAugmentVisLoader = createSavedAugmentVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, @@ -75,6 +74,10 @@ export class VisAugmenterPlugin overlays: core.overlays, }); setSavedAugmentVisLoader(savedAugmentVisLoader); + + // sets up the context mappings and registers any triggers/actions for the plugin + bootstrapUiActions(uiActions); + return { savedAugmentVisLoader }; } diff --git a/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts index 88178a7a08cd..d40382b3a104 100644 --- a/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts +++ b/src/plugins/vis_augmenter/public/saved_augment_vis/_saved_augment_vis.ts @@ -24,9 +24,23 @@ export function createSavedAugmentVisClass(services: SavedObjectOpenSearchDashbo class SavedAugmentVis extends SavedObjectClass { public static type: string = name; - public static mapping: AugmentVisSavedObjectAttributes; + public static mapping: Record<string, string> = { + title: 'text', + description: 'text', + originPlugin: 'text', + pluginResource: 'object', + visLayerExpressionFn: 'object', + visId: 'keyword,', + version: 'integer', + }; - constructor(opts: AugmentVisSavedObjectAttributes) { + constructor(opts: Record<string, unknown> | string = {}) { + // The default delete() fn from the saved object loader will only + // pass a string ID. To handle that case here, we embed it within + // an opts object. + if (typeof opts !== 'object') { + opts = { id: opts }; + } super({ type: SavedAugmentVis.type, mapping: SavedAugmentVis.mapping, diff --git a/src/plugins/vis_augmenter/public/services.ts b/src/plugins/vis_augmenter/public/services.ts index 48a3233714e6..1d7f3e2111db 100644 --- a/src/plugins/vis_augmenter/public/services.ts +++ b/src/plugins/vis_augmenter/public/services.ts @@ -17,6 +17,7 @@ export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetter >('savedAugmentVisLoader'); export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); + export const [getUiActions, setUiActions] = createGetterSetter<UiActionsStart>('UIActions'); export const [getEmbeddable, setEmbeddable] = createGetterSetter<EmbeddableStart>('embeddable'); @@ -30,3 +31,15 @@ export const [getVisualizations, setVisualizations] = createGetterSetter<Visuali ); export const [getCore, setCore] = createGetterSetter<CoreStart>('Core'); + +// This is primarily used for mocking this module and each of its fns in tests. +// eslint-disable-next-line import/no-default-export +export default { + getSavedAugmentVisLoader, + getUISettings, + getUiActions, + getEmbeddable, + getQueryService, + getVisualizations, + getCore, +}; diff --git a/src/plugins/vis_augmenter/public/triggers/index.ts b/src/plugins/vis_augmenter/public/triggers/index.ts new file mode 100644 index 000000000000..5b1833e38d62 --- /dev/null +++ b/src/plugins/vis_augmenter/public/triggers/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + PLUGIN_RESOURCE_DELETE_TRIGGER, + pluginResourceDeleteTrigger, +} from './plugin_resource_delete_trigger'; diff --git a/src/plugins/vis_augmenter/public/triggers/plugin_resource_delete_trigger.ts b/src/plugins/vis_augmenter/public/triggers/plugin_resource_delete_trigger.ts new file mode 100644 index 000000000000..249bb61132eb --- /dev/null +++ b/src/plugins/vis_augmenter/public/triggers/plugin_resource_delete_trigger.ts @@ -0,0 +1,18 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { Trigger } from '../../../ui_actions/public'; + +export const PLUGIN_RESOURCE_DELETE_TRIGGER = 'PLUGIN_RESOURCE_DELETE_TRIGGER'; +export const pluginResourceDeleteTrigger: Trigger<'PLUGIN_RESOURCE_DELETE_TRIGGER'> = { + id: PLUGIN_RESOURCE_DELETE_TRIGGER, + title: i18n.translate('visAugmenter.triggers.pluginResourceDeleteTitle', { + defaultMessage: 'Plugin resource delete', + }), + description: i18n.translate('visAugmenter.triggers.pluginResourceDeleteDescription', { + defaultMessage: 'Delete augment-vis saved objs associated to the deleted plugin resource', + }), +}; diff --git a/src/plugins/vis_augmenter/public/types.ts b/src/plugins/vis_augmenter/public/types.ts index 27fd9d7c241f..35c00ea932a7 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -10,6 +10,7 @@ export enum VisLayerTypes { export enum VisLayerErrorTypes { PERMISSIONS_FAILURE = 'PERMISSIONS_FAILURE', FETCH_FAILURE = 'FETCH_FAILURE', + RESOURCE_DELETED = 'RESOURCE_DELETED', } export enum VisFlyoutContext { diff --git a/src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts b/src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts index aae3f237a28f..27bd6284e71b 100644 --- a/src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts +++ b/src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts @@ -3,45 +3,76 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { CoreStart } from 'opensearch-dashboards/public'; import { OpenEventsFlyoutAction, ViewEventsOptionAction, OPEN_EVENTS_FLYOUT_ACTION, VIEW_EVENTS_OPTION_ACTION, } from './view_events_flyout'; -import { externalActionTrigger, EXTERNAL_ACTION_TRIGGER } from '../../ui_actions/public'; import { CONTEXT_MENU_TRIGGER, EmbeddableContext } from '../../embeddable/public'; -import { getUiActions } from './services'; +import { SAVED_OBJECT_DELETE_TRIGGER } from '../../saved_objects_management/public'; +import { + externalActionTrigger, + EXTERNAL_ACTION_TRIGGER, + UiActionsSetup, +} from '../../ui_actions/public'; +import { ISavedAugmentVis } from './saved_augment_vis'; +import { VisLayer } from './types'; +import { + PLUGIN_RESOURCE_DELETE_ACTION, + PluginResourceDeleteAction, + SAVED_OBJECT_DELETE_ACTION, + SavedObjectDeleteAction, +} from './actions'; +import { PLUGIN_RESOURCE_DELETE_TRIGGER, pluginResourceDeleteTrigger } from './triggers'; export interface AugmentVisContext { savedObjectId: string; } +export interface SavedObjectDeleteContext { + type: string; + savedObjectId: string; +} + +export interface PluginResourceDeleteContext { + savedObjs: ISavedAugmentVis[]; + visLayers: VisLayer[]; +} + // Overriding the mappings defined in UIActions plugin so that // the new trigger and action definitions resolve. // This is a common pattern among internal Dashboards plugins. declare module '../../ui_actions/public' { export interface TriggerContextMapping { [EXTERNAL_ACTION_TRIGGER]: AugmentVisContext; + [PLUGIN_RESOURCE_DELETE_TRIGGER]: PluginResourceDeleteContext; } export interface ActionContextMapping { [OPEN_EVENTS_FLYOUT_ACTION]: AugmentVisContext; [VIEW_EVENTS_OPTION_ACTION]: EmbeddableContext; + [SAVED_OBJECT_DELETE_ACTION]: SavedObjectDeleteContext; + [PLUGIN_RESOURCE_DELETE_ACTION]: PluginResourceDeleteContext; } } -export const registerTriggersAndActions = (core: CoreStart) => { - const openEventsFlyoutAction = new OpenEventsFlyoutAction(core); - const viewEventsOptionAction = new ViewEventsOptionAction(core); +export const bootstrapUiActions = (uiActions: UiActionsSetup) => { + const openEventsFlyoutAction = new OpenEventsFlyoutAction(); + const viewEventsOptionAction = new ViewEventsOptionAction(); + const savedObjectDeleteAction = new SavedObjectDeleteAction(); + const pluginResourceDeleteAction = new PluginResourceDeleteAction(); + + uiActions.registerAction(openEventsFlyoutAction); + uiActions.registerAction(viewEventsOptionAction); + uiActions.registerAction(savedObjectDeleteAction); + uiActions.registerAction(pluginResourceDeleteAction); - getUiActions().registerAction(openEventsFlyoutAction); - getUiActions().registerAction(viewEventsOptionAction); - getUiActions().registerTrigger(externalActionTrigger); + uiActions.registerTrigger(externalActionTrigger); + uiActions.registerTrigger(pluginResourceDeleteTrigger); - // Opening View Events flyout from the chart - getUiActions().addTriggerAction(EXTERNAL_ACTION_TRIGGER, openEventsFlyoutAction); - // Opening View Events flyout from the context menu - getUiActions().addTriggerAction(CONTEXT_MENU_TRIGGER, viewEventsOptionAction); + uiActions.addTriggerAction(EXTERNAL_ACTION_TRIGGER, openEventsFlyoutAction); + uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, viewEventsOptionAction); + uiActions.addTriggerAction(SAVED_OBJECT_DELETE_TRIGGER, savedObjectDeleteAction); + uiActions.addTriggerAction(PLUGIN_RESOURCE_DELETE_TRIGGER, pluginResourceDeleteAction); }; diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index 707c36f87073..250e17e8d595 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -16,10 +16,14 @@ import { ISavedAugmentVis, VisLayerTypes, VisLayerExpressionFn, + cleanupStaleObjects, + VisLayer, + PluginResource, + VisLayerErrorTypes, + SavedObjectLoaderAugmentVis, } from '../'; import { PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common/constants'; -import { AggConfigs, AggTypesRegistryStart, IndexPattern } from '../../../data/common'; -import { mockAggTypesRegistry } from '../../../data/common/search/aggs/test_helpers'; +import { AggConfigs } from '../../../data/common'; import { uiSettingsServiceMock } from '../../../../core/public/mocks'; import { setUISettings } from '../services'; import { @@ -28,6 +32,7 @@ import { VALID_AGGS, VALID_CONFIG_STATES, VALID_VIS, + createPointInTimeEventsVisLayer, createVisLayer, } from '../mocks'; @@ -429,4 +434,129 @@ describe('utils', () => { expect(err?.stack).toStrictEqual(`-----resource-type-1-----\nID: 1234\nMessage: "uh-oh!"`); }); }); + + describe('cleanupStaleObjects', () => { + const fn = { + type: VisLayerTypes.PointInTimeEvents, + name: 'test-fn', + args: { + testArg: 'test-value', + }, + } as VisLayerExpressionFn; + const originPlugin = 'test-plugin'; + const resourceId1 = 'resource-1'; + const resourceId2 = 'resource-2'; + const resourceType1 = 'resource-type-1'; + const augmentVisObj1 = generateAugmentVisSavedObject('id-1', fn, 'vis-id-1', originPlugin, { + type: resourceType1, + id: resourceId1, + }); + const augmentVisObj2 = generateAugmentVisSavedObject('id-2', fn, 'vis-id-1', originPlugin, { + type: resourceType1, + id: resourceId2, + }); + const resource1 = { + type: 'test-resource-type-1', + id: resourceId1, + name: 'resource-1', + urlPath: 'test-path', + } as PluginResource; + const resource2 = { + type: 'test-resource-type-1', + id: resourceId2, + name: 'resource-2', + urlPath: 'test-path', + } as PluginResource; + const validVisLayer1 = createPointInTimeEventsVisLayer(originPlugin, resource1, 1, false); + const staleVisLayer1 = { + ...createPointInTimeEventsVisLayer(originPlugin, resource1, 0, true), + error: { + type: VisLayerErrorTypes.RESOURCE_DELETED, + message: 'resource is deleted', + }, + }; + const staleVisLayer2 = { + ...createPointInTimeEventsVisLayer(originPlugin, resource2, 0, true), + error: { + type: VisLayerErrorTypes.RESOURCE_DELETED, + message: 'resource is deleted', + }, + }; + + it('no augment-vis objs, no vislayers', async () => { + const mockDeleteFn = jest.fn(); + const augmentVisObjs = [] as ISavedAugmentVis[]; + const visLayers = [] as VisLayer[]; + const augmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: { + ...getMockAugmentVisSavedObjectClient(augmentVisObjs), + delete: mockDeleteFn, + }, + } as any) as SavedObjectLoaderAugmentVis; + + cleanupStaleObjects(augmentVisObjs, visLayers, augmentVisLoader); + + expect(mockDeleteFn).toHaveBeenCalledTimes(0); + }); + it('no stale vislayers', async () => { + const mockDeleteFn = jest.fn(); + const augmentVisObjs = [augmentVisObj1]; + const visLayers = [validVisLayer1]; + const augmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: { + ...getMockAugmentVisSavedObjectClient(augmentVisObjs), + delete: mockDeleteFn, + }, + } as any) as SavedObjectLoaderAugmentVis; + + cleanupStaleObjects(augmentVisObjs, visLayers, augmentVisLoader); + + expect(mockDeleteFn).toHaveBeenCalledTimes(0); + }); + it('1 stale vislayer', async () => { + const mockDeleteFn = jest.fn(); + const augmentVisObjs = [augmentVisObj1]; + const visLayers = [staleVisLayer1]; + const augmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: { + ...getMockAugmentVisSavedObjectClient(augmentVisObjs), + delete: mockDeleteFn, + }, + } as any) as SavedObjectLoaderAugmentVis; + + cleanupStaleObjects(augmentVisObjs, visLayers, augmentVisLoader); + + expect(mockDeleteFn).toHaveBeenCalledTimes(1); + }); + it('multiple stale vislayers', async () => { + const mockDeleteFn = jest.fn(); + const augmentVisObjs = [augmentVisObj1, augmentVisObj2]; + const visLayers = [staleVisLayer1, staleVisLayer2]; + const augmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: { + ...getMockAugmentVisSavedObjectClient(augmentVisObjs), + delete: mockDeleteFn, + }, + } as any) as SavedObjectLoaderAugmentVis; + + cleanupStaleObjects(augmentVisObjs, visLayers, augmentVisLoader); + + expect(mockDeleteFn).toHaveBeenCalledTimes(2); + }); + it('stale and valid vislayers', async () => { + const mockDeleteFn = jest.fn(); + const augmentVisObjs = [augmentVisObj1, augmentVisObj2]; + const visLayers = [validVisLayer1, staleVisLayer2]; + const augmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: { + ...getMockAugmentVisSavedObjectClient(augmentVisObjs), + delete: mockDeleteFn, + }, + } as any) as SavedObjectLoaderAugmentVis; + + cleanupStaleObjects(augmentVisObjs, visLayers, augmentVisLoader); + + expect(mockDeleteFn).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index 9d683ff962c1..907867630cbe 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -11,12 +11,14 @@ import { buildExpression, ExpressionAstFunctionBuilder, } from '../../../../plugins/expressions/public'; +import { SavedObjectLoader } from '../../../../plugins/saved_objects/public'; import { ISavedAugmentVis, SavedAugmentVisLoader, VisLayerFunctionDefinition, VisLayer, isVisLayerWithError, + VisLayerErrorTypes, } from '../'; import { PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common/constants'; import { getUISettings } from '../services'; @@ -48,9 +50,8 @@ export const isEligibleForVisLayers = (vis: Vis): boolean => { }; /** - * Using a SavedAugmentVisLoader, fetch all saved objects that are of 'augment-vis' type - * and filter out to return the ones associated to the particular vis via - * matching vis ID. + * Using a SavedAugmentVisLoader, fetch all saved objects that are of 'augment-vis' type. + * Filter by vis ID. */ export const getAugmentVisSavedObjs = async ( visId: string | undefined, @@ -66,14 +67,27 @@ export const getAugmentVisSavedObjs = async ( ); } try { - const resp = await loader?.findAll(); - const allSavedObjects = (get(resp, 'hits', []) as any[]) as ISavedAugmentVis[]; + const allSavedObjects = await getAllAugmentVisSavedObjs(loader); return allSavedObjects.filter((hit: ISavedAugmentVis) => hit.visId === visId); } catch (e) { return [] as ISavedAugmentVis[]; } }; +/** + * Using a SavedAugmentVisLoader, fetch all saved objects that are of 'augment-vis' type. + */ +export const getAllAugmentVisSavedObjs = async ( + loader: SavedAugmentVisLoader | undefined +): Promise<ISavedAugmentVis[]> => { + try { + const resp = await loader?.findAll(); + return (get(resp, 'hits', []) as any[]) as ISavedAugmentVis[]; + } catch (e) { + return [] as ISavedAugmentVis[]; + } +}; + /** * Given an array of augment-vis saved objects that contain expression function details, * construct a pipeline that will execute each of these expression functions. @@ -130,3 +144,36 @@ export const getAnyErrors = (visLayers: VisLayer[], visTitle: string): Error | u return undefined; } }; + +/** + * Cleans up any stale saved objects caused by plugin resources being deleted. Kicks + * off an async call to delete the stale objs. + * + * @param augmentVisSavedObs the original augment-vis saved objs for this particular vis + * @param visLayers the produced VisLayers containing details if the resource has been deleted + * @param visualizationsLoader the visualizations saved object loader to handle deletion + */ + +export const cleanupStaleObjects = ( + augmentVisSavedObjs: ISavedAugmentVis[], + visLayers: VisLayer[], + loader: SavedAugmentVisLoader | undefined +): void => { + const staleVisLayers = visLayers + .filter((visLayer) => isVisLayerWithError(visLayer)) + .filter( + (visLayerWithError) => visLayerWithError.error?.type === VisLayerErrorTypes.RESOURCE_DELETED + ); + if (!isEmpty(staleVisLayers)) { + const objIdsToDelete = [] as string[]; + staleVisLayers.forEach((staleVisLayer) => { + // Match the VisLayer to its origin saved obj to extract the to-be-deleted saved obj ID + const deletedPluginResourceId = staleVisLayer.pluginResource.id; + const savedObjId = augmentVisSavedObjs.find( + (savedObj) => savedObj.pluginResource.id === deletedPluginResourceId + )?.id; + if (savedObjId !== undefined) objIdsToDelete.push(savedObjId); + }); + loader?.delete(objIdsToDelete); + } +}; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx index cd7d90aedf11..c9f6d75e1190 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout.tsx @@ -3,19 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ import React from 'react'; -import { CoreStart } from 'src/core/public'; import { toMountPoint } from '../../../../opensearch_dashboards_react/public'; import { ViewEventsFlyout } from '../components'; import { VIEW_EVENTS_FLYOUT_STATE, setFlyoutState } from '../flyout_state'; +import { getCore } from '../../services'; interface Props { - core: CoreStart; savedObjectId: string; } export async function openViewEventsFlyout(props: Props) { setFlyoutState(VIEW_EVENTS_FLYOUT_STATE.OPEN); - const flyoutSession = props.core.overlays.openFlyout( + const flyoutSession = getCore().overlays.openFlyout( toMountPoint( <ViewEventsFlyout onClose={() => { diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts index 381cbcb2e453..e6cb654ab422 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.test.ts @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { coreMock } from '../../../../../core/public/mocks'; -import { CoreStart } from 'opensearch-dashboards/public'; import { OpenEventsFlyoutAction } from './open_events_flyout_action'; import flyoutStateModule from '../flyout_state'; +import servicesModule from '../../services'; // Mocking the flyout state service. Defaulting to CLOSED. May override // getFlyoutState() in below individual tests to test out different scenarios. @@ -21,37 +20,46 @@ jest.mock('src/plugins/vis_augmenter/public/view_events_flyout/flyout_state', () }; }); -let coreStart: CoreStart; -beforeEach(async () => { - coreStart = coreMock.createStart(); +// Mocking core service as needed when making calls to the core's overlays service +jest.mock('src/plugins/vis_augmenter/public/services.ts', () => { + return { + getCore: () => { + return { + overlays: { + openFlyout: () => {}, + }, + }; + }, + }; }); + afterEach(async () => { jest.clearAllMocks(); }); describe('OpenEventsFlyoutAction', () => { it('is incompatible with null saved obj id', async () => { - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); const savedObjectId = null; // @ts-ignore expect(await action.isCompatible({ savedObjectId })).toBe(false); }); it('is incompatible with undefined saved obj id', async () => { - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); const savedObjectId = undefined; // @ts-ignore expect(await action.isCompatible({ savedObjectId })).toBe(false); }); it('is incompatible with empty saved obj id', async () => { - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); const savedObjectId = ''; expect(await action.isCompatible({ savedObjectId })).toBe(false); }); it('execute throws error if incompatible saved obj id', async () => { - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); async function check(id: any) { await action.execute({ savedObjectId: id }); } @@ -64,10 +72,13 @@ describe('OpenEventsFlyoutAction', () => { const getFlyoutStateSpy = jest .spyOn(flyoutStateModule, 'getFlyoutState') .mockImplementation(() => 'CLOSED'); + // openFlyout exists within core.overlays service. We spy on the initial getCore() fn call indicating + // that openFlyout is getting called. + const openFlyoutStateSpy = jest.spyOn(servicesModule, 'getCore'); const savedObjectId = 'test-id'; - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); await action.execute({ savedObjectId }); - expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(1); + expect(openFlyoutStateSpy).toHaveBeenCalledTimes(1); expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); }); @@ -75,20 +86,21 @@ describe('OpenEventsFlyoutAction', () => { const getFlyoutStateSpy = jest .spyOn(flyoutStateModule, 'getFlyoutState') .mockImplementation(() => 'OPEN'); + const openFlyoutStateSpy = jest.spyOn(servicesModule, 'getCore'); const savedObjectId = 'test-id'; - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); await action.execute({ savedObjectId }); - expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(0); + expect(openFlyoutStateSpy).toHaveBeenCalledTimes(0); expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); }); it('Returns display name', async () => { - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); expect(action.getDisplayName()).toBeDefined(); }); it('Returns undefined icon type', async () => { - const action = new OpenEventsFlyoutAction(coreStart); + const action = new OpenEventsFlyoutAction(); expect(action.getIconType()).toBeUndefined(); }); }); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts index c141a361048a..cb47e5d6a85c 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/open_events_flyout_action.ts @@ -25,7 +25,7 @@ export class OpenEventsFlyoutAction implements Action<AugmentVisContext> { public readonly id = OPEN_EVENTS_FLYOUT_ACTION; public order = 1; - constructor(private core: CoreStart) {} + constructor() {} public getIconType() { return undefined; @@ -53,7 +53,6 @@ export class OpenEventsFlyoutAction implements Action<AugmentVisContext> { // re-opening it. if (getFlyoutState() === VIEW_EVENTS_FLYOUT_STATE.CLOSED) { openViewEventsFlyout({ - core: this.core, savedObjectId, }); } diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts index 64b75547d019..451087e48a15 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts @@ -3,11 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { coreMock } from '../../../../../core/public/mocks'; -import { CoreStart } from 'opensearch-dashboards/public'; import { ViewEventsOptionAction } from './view_events_option_action'; import { createMockErrorEmbeddable, createMockVisEmbeddable } from '../../mocks'; import flyoutStateModule from '../flyout_state'; +import servicesModule from '../../services'; // Mocking the flyout state service. Defaulting to CLOSED. May override // getFlyoutState() in below individual tests to test out different scenarios. @@ -24,6 +23,7 @@ jest.mock('src/plugins/vis_augmenter/public/view_events_flyout/flyout_state', () // Mocking the UISettings service. This is needed when making eligibility checks for the actions, // which does UISettings checks to ensure the feature is enabled. +// Also mocking core service as needed when making calls to the core's overlays service jest.mock('src/plugins/vis_augmenter/public/services.ts', () => { return { getUISettings: () => { @@ -40,40 +40,42 @@ jest.mock('src/plugins/vis_augmenter/public/services.ts', () => { }, }; }, + getCore: () => { + return { + overlays: { + openFlyout: () => {}, + }, + }; + }, }; }); -let coreStart: CoreStart; - -beforeEach(async () => { - coreStart = coreMock.createStart(); -}); afterEach(async () => { jest.clearAllMocks(); }); describe('ViewEventsOptionAction', () => { it('is incompatible with ErrorEmbeddables', async () => { - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); const errorEmbeddable = createMockErrorEmbeddable(); expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false); }); it('is incompatible with VisualizeEmbeddable with invalid vis', async () => { const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title', false); - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); expect(await action.isCompatible({ embeddable: visEmbeddable })).toBe(false); }); it('is compatible with VisualizeEmbeddable with valid vis', async () => { const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); expect(await action.isCompatible({ embeddable: visEmbeddable })).toBe(true); }); it('execute throws error if incompatible embeddable', async () => { const errorEmbeddable = createMockErrorEmbeddable(); - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); async function check() { await action.execute({ embeddable: errorEmbeddable }); } @@ -84,10 +86,13 @@ describe('ViewEventsOptionAction', () => { const getFlyoutStateSpy = jest .spyOn(flyoutStateModule, 'getFlyoutState') .mockImplementation(() => 'CLOSED'); + // openFlyout exists within core.overlays service. We spy on the initial getCore() fn call indicating + // that openFlyout is getting called. + const openFlyoutStateSpy = jest.spyOn(servicesModule, 'getCore'); const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); await action.execute({ embeddable: visEmbeddable }); - expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(1); + expect(openFlyoutStateSpy).toHaveBeenCalledTimes(1); expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); }); @@ -95,20 +100,21 @@ describe('ViewEventsOptionAction', () => { const getFlyoutStateSpy = jest .spyOn(flyoutStateModule, 'getFlyoutState') .mockImplementation(() => 'OPEN'); + const openFlyoutStateSpy = jest.spyOn(servicesModule, 'getCore'); const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); await action.execute({ embeddable: visEmbeddable }); - expect(coreStart.overlays.openFlyout).toHaveBeenCalledTimes(0); + expect(openFlyoutStateSpy).toHaveBeenCalledTimes(0); expect(getFlyoutStateSpy).toHaveBeenCalledTimes(1); }); it('Returns display name', async () => { - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); expect(action.getDisplayName()).toBeDefined(); }); it('Returns an icon type', async () => { - const action = new ViewEventsOptionAction(coreStart); + const action = new ViewEventsOptionAction(); expect(action.getIconType()).toBeDefined(); }); }); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx index 1ad2a9c57fe2..975f6ac0cc4c 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx @@ -21,7 +21,7 @@ export class ViewEventsOptionAction implements Action<EmbeddableContext> { public readonly id = VIEW_EVENTS_OPTION_ACTION; public order = 1; - constructor(private core: CoreStart) {} + constructor() {} public getIconType(): EuiIconType { return 'apmTrace'; @@ -52,7 +52,6 @@ export class ViewEventsOptionAction implements Action<EmbeddableContext> { // re-opening it. if (getFlyoutState() === VIEW_EVENTS_FLYOUT_STATE.CLOSED) { openViewEventsFlyout({ - core: this.core, savedObjectId, }); } diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index be6c832d3385..505c7b045cb4 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -72,16 +72,12 @@ import { getAugmentVisSavedObjs, buildPipelineFromAugmentVisSavedObjs, getAnyErrors, - VisLayerErrorTypes, AugmentVisContext, -} from '../../../vis_augmenter/public'; -import { VisSavedObject } from '../types'; -import { - PointInTimeEventsVisLayer, VisLayer, - VisLayerTypes, VisAugmenterEmbeddableConfig, + PLUGIN_RESOURCE_DELETE_TRIGGER, } from '../../../vis_augmenter/public'; +import { VisSavedObject } from '../types'; const getKeys = <T extends {}>(o: T): Array<keyof T> => Object.keys(o) as Array<keyof T>; @@ -519,11 +515,6 @@ export class VisualizeEmbeddable * Collects any VisLayers from plugin expressions functions * by fetching all AugmentVisSavedObjects that match the vis * saved object ID. - * - * TODO: final eligibility will be defined as part of a separate effort. - * Right now we have a placeholder function isEligibleForVisLayers() which - * is used below. For more details, see - * https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 */ fetchVisLayers = async (): Promise<VisLayers> => { try { @@ -558,6 +549,15 @@ export class VisualizeEmbeddable expressionParams as Record<string, unknown> )) as ExprVisLayers; const visLayers = exprVisLayers.layers; + + // There may be some stale saved objs if any plugin resources have been deleted since last time + // data was fetched from them via the expression functions. Execute this trigger so any listening + // action can perform cleanup. + getUiActions().getTrigger(PLUGIN_RESOURCE_DELETE_TRIGGER).exec({ + savedObjs: augmentVisSavedObjs, + visLayers, + }); + const err = getAnyErrors(visLayers, this.vis.title); // This is only true when one or more VisLayers has an error if (err !== undefined) { diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 957c9d8c80cd..6c8cf4ec51d2 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -76,3 +76,4 @@ export { export { ExprVisAPIEvents } from './expressions/vis'; export { VisualizationListItem } from './vis_types/vis_type_alias_registry'; export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants'; +export { createSavedVisLoader } from './saved_visualizations'; diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 07b40066018c..9b094c9ef754 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -182,6 +182,14 @@ export class VisualizationsPlugin { data, expressions, uiActions, embeddable, dashboard }: VisualizationsStartDeps ): VisualizationsStart { const types = this.types.start(); + const savedAugmentVisLoader = createSavedAugmentVisLoader({ + savedObjectsClient: core.savedObjects.client, + indexPatterns: data.indexPatterns, + search: data.search, + chrome: core.chrome, + overlays: core.overlays, + }); + setSavedAugmentVisLoader(savedAugmentVisLoader); setI18n(core.i18n); setTypes(types); setEmbeddable(embeddable); @@ -205,6 +213,7 @@ export class VisualizationsPlugin chrome: core.chrome, overlays: core.overlays, visualizationTypes: types, + savedAugmentVisLoader, }); setSavedVisualizationsLoader(savedVisualizationsLoader); const savedSearchLoader = createSavedSearchesLoader({ @@ -214,14 +223,6 @@ export class VisualizationsPlugin chrome: core.chrome, overlays: core.overlays, }); - const savedAugmentVisLoader = createSavedAugmentVisLoader({ - savedObjectsClient: core.savedObjects.client, - indexPatterns: data.indexPatterns, - search: data.search, - chrome: core.chrome, - overlays: core.overlays, - }); - setSavedAugmentVisLoader(savedAugmentVisLoader); setSavedSearchLoader(savedSearchLoader); setNotifications(core.notifications); return { diff --git a/src/plugins/visualize/opensearch_dashboards.json b/src/plugins/visualize/opensearch_dashboards.json index c898f7da3779..47573b58b9d2 100644 --- a/src/plugins/visualize/opensearch_dashboards.json +++ b/src/plugins/visualize/opensearch_dashboards.json @@ -19,6 +19,7 @@ "opensearchDashboardsReact", "home", "discover", - "visDefaultEditor" + "visDefaultEditor", + "savedObjectsManagement" ] } diff --git a/src/plugins/visualize/public/application/components/visualize_listing.tsx b/src/plugins/visualize/public/application/components/visualize_listing.tsx index 46db44bb066d..ec768e19885a 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.tsx +++ b/src/plugins/visualize/public/application/components/visualize_listing.tsx @@ -29,7 +29,7 @@ */ import './visualize_listing.scss'; - +import { get } from 'lodash'; import React, { useCallback, useRef, useMemo, useEffect } from 'react'; import { i18n } from '@osd/i18n'; import { useUnmount, useMount } from 'react-use'; @@ -43,6 +43,8 @@ import { VISUALIZE_ENABLE_LABS_SETTING } from '../../../../visualizations/public import { VisualizeServices } from '../types'; import { VisualizeConstants } from '../visualize_constants'; import { getTableColumns, getNoItemsMessage } from '../utils'; +import { getUiActions } from '../../services'; +import { SAVED_OBJECT_DELETE_TRIGGER } from '../../../../saved_objects_management/public'; export const VisualizeListing = () => { const { @@ -134,15 +136,25 @@ export const VisualizeListing = () => { const deleteItems = useCallback( async (selectedItems: object[]) => { + const uiActions = getUiActions(); await Promise.all( - selectedItems.map((item: any) => savedObjects.client.delete(item.savedObjectType, item.id)) - ).catch((error) => { - toastNotifications.addError(error, { - title: i18n.translate('visualize.visualizeListingDeleteErrorTitle', { - defaultMessage: 'Error deleting visualization', - }), - }); - }); + selectedItems.map((item: any) => + savedObjects.client + .delete(item.savedObjectType, item.id) + .then(() => { + uiActions + .getTrigger(SAVED_OBJECT_DELETE_TRIGGER) + .exec({ type: item.savedObjectType, savedObjectId: item.id }); + }) + .catch((error) => { + toastNotifications.addError(error, { + title: i18n.translate('visualize.visualizeListingDeleteErrorTitle', { + defaultMessage: 'Error deleting visualization', + }), + }); + }) + ) + ); }, [savedObjects.client, toastNotifications] ); diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 297db26c48de..c146efef1fab 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -60,13 +60,14 @@ import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { SavedObjectsStart } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; import { DashboardStart } from '../../dashboard/public'; -import { UiActionsSetup, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public'; +import { UiActionsSetup, UiActionsStart, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public'; import { setUISettings, setApplication, setIndexPatterns, setQueryService, setShareService, + setUiActions, } from './services'; import { visualizeFieldAction } from './actions/visualize_field_action'; import { createVisualizeUrlGenerator } from './url_generator'; @@ -80,6 +81,7 @@ export interface VisualizePluginStartDependencies { urlForwarding: UrlForwardingStart; savedObjects: SavedObjectsStart; dashboard: DashboardStart; + uiActions: UiActionsStart; } export interface VisualizePluginSetupDependencies { @@ -248,6 +250,7 @@ export class VisualizePlugin if (plugins.share) { setShareService(plugins.share); } + setUiActions(plugins.uiActions); } stop() { diff --git a/src/plugins/visualize/public/services.ts b/src/plugins/visualize/public/services.ts index c0f359e8a002..ac367522ab7e 100644 --- a/src/plugins/visualize/public/services.ts +++ b/src/plugins/visualize/public/services.ts @@ -32,6 +32,7 @@ import { ApplicationStart, IUiSettingsClient } from '../../../core/public'; import { createGetterSetter } from '../../../plugins/opensearch_dashboards_utils/public'; import { IndexPatternsContract, DataPublicPluginStart } from '../../../plugins/data/public'; import { SharePluginStart } from '../../share/public'; +import { UiActionsStart } from '../../ui_actions/public'; export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings'); @@ -46,3 +47,5 @@ export const [getIndexPatterns, setIndexPatterns] = createGetterSetter<IndexPatt export const [getQueryService, setQueryService] = createGetterSetter< DataPublicPluginStart['query'] >('Query'); + +export const [getUiActions, setUiActions] = createGetterSetter<UiActionsStart>('UIActions'); From 6eda3ef0503de39ff850fca94add1a430060c158 Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Thu, 1 Jun 2023 10:32:47 -0700 Subject: [PATCH 22/37] Rebase Feature-Anywhere branch with main (#4192) * Fix header icon (#3910) (#3915) * fixes header change * Update src/core/public/chrome/ui/header/header_help_menu.tsx * fixes snapshots --------- (cherry picked from commit 3cca08851cb1527705c60bbb4202d50174f034af) Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> * Add server side private IP blocking for data source endpoints validation (#3912) Signed-off-by: Kristen Tian <tyarong@amazon.com> * Docs (Jest): Update jest documentation links (#3931) Signed-off-by: Josh Romero <rmerqg@amazon.com> * Revert "[CCI] Replace jquery usage in console plugin with native methods (#3733)" (#3929) This reverts commit ffe455615eef10d1719884fd131f6953976583ff. * [BUG][Dashboard listing] push to history if dashboard otherwise nav (#3922) History push will just to the current route. However, dashboardsProvider was implemented with the expectation that it was a different app. So when a plugin registered it was attempting to navigate to `app/dashboard#/app/{url}` Add tests and extra data test subject. Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * remove jquery console release note for https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3929 revert (#3930) Signed-off-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Ashwin P Chandran <ashwinpc@amazon.com> * [CCI] Update js-yaml to v4.0.5 (#3770) * Update js-yaml to 4.0.5 (#3659) * Update CHANGELOG.md (#3659) Co-authored-by: Sergey Myssak <sergey.myssak@gmail.com> Signed-off-by: Andrey Myssak <andreymyssak@gmail.com> --------- Signed-off-by: Andrey Myssak <andreymyssak@gmail.com> Signed-off-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Sergey Myssak <sergey.myssak@gmail.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> * Update README.md (#3788) * Update README.md Signed-off-by: Melissa Vagi <vagimeli@amazon.com> * Update README.md Co-authored-by: Miki <amoo_miki@yahoo.com> --------- Signed-off-by: Melissa Vagi <vagimeli@amazon.com> Co-authored-by: Miki <miki@amazon.com> Co-authored-by: Miki <amoo_miki@yahoo.com> * Bump yaml to 2.2.2 (#3947) Signed-off-by: Manasvini B Suryanarayana <manasvis@amazon.com> Co-authored-by: Sean Neumann <1413295+seanneumann@users.noreply.github.com> * Bump `joi` to v14 to avoid the possibility of prototype poisoning in a nested dependency (#3952) Signed-off-by: Miki <miki@amazon.com> * [Doc] Add communication guide (#3837) * docs(COMMUNICATION): Add communication guide with info on slack, forum, and developer office hours link from README, CONTRIBUTING, DEVELOPER_GUIDE Signed-off-by: Josh Romero <rmerqg@amazon.com> --------- Signed-off-by: Josh Romero <rmerqg@amazon.com> * Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests (#3976) The latest version of chromedriver is 112.0.1 which does not support node 14. This PR hardcodes chromedriver to 112.0.0 temporarily. Pls revert it once we bump to node 18. Issue Resolved https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3975 Signed-off-by: ananzh <ananzh@amazon.com> * Fix wording and duplicate code in embeddable example plugin (#3911) * Fix wording and duplicate code in embeddable example plugin Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> * Fix some wording in the embeddable readme Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> --------- Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> * [CI] setup Chrome and utilize binary path (#3997) Within the CI, the virtual runner that we are utilizing has Chrome installed already. The version of Chrome is installed periodically. The most recent version of Chrome requires updates to dependencies that drop support for Node 14. This downloads chrome in the CI and then checks the chromedriver from the environment variable `TEST_BROWSER_BINARY_PATH`. Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * [Dashboards listing] fix listing limit (#4021) Initial page size was passed to the search function instead of the listing limit causing the max amount received to be significantly less than the previously implementation. Saved objects per page is `20` by default and the listing limit per page is `1000` by default. Issue: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4017 Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * [CCI] Fix EUI/OUI type errors (#3798) * Update find_test_subject imports for tests Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Update to available imports for findTestSubject Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Fix available import for Query and custom icon Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Add changelog entry Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Add ts-ignore Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> --------- Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> Co-authored-by: Qingyang(Abby) Hu <abigailhu2000@gmail.com> * Fix bottom bar visibility using create portal (#3336) (#3978) Signed-off-by: Sergey Myssak <sergey.myssak@gmail.com> Co-authored-by: Andrey Myssak <andreymyssak@gmail.com> * Adds threshold to code coverage changes for project (#4040) * Fixes code coverage workflow failures for the project test due to inderect flakey changes Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Adds changelog Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> --------- Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Updates PR template for screenshots and test instructions (#4042) Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Replace re2 with RegExp in timeline and add unit tests (#3908) Remove re2 usage and replace it with JavaScript built-in RegExp object. Also add more unit tests to make sure that using RegExp has same expressions as using re2 library. Issue Resolve https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3901 Signed-off-by: Anan Zhuang <ananzh@amazon.com> * [Console] [CCI] Remove unused ul element and its custom styling. (#3993) * remove unused ul element Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> * Update CHANGELOG.md Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> --------- Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> * Add 1.3.10 release note (#4060) (#4063) * Add release note for 1.3.10 * Address comments and add one CVE PR --------- (cherry picked from commit 43715871f3a9fc9b2a0fcac765805a33b4b305d5) Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [Multiple Datasource] Support Amazon OpenSearch Serverless (#3957) * [Multiple Datasource]Support Amazon OpenSearch Serverless in SigV4 * remove experimental text in yml * Refactor create data source form for authentication Signed-off-by: Su <szhongna@amazon.com> * Remove Sass from `tile_map` plugin (#4110) * Remove Sass from tile_map plugin Signed-off-by: Matt Provost <provomat@amazon.com> * Update changelog Signed-off-by: Matt Provost <provomat@amazon.com> --------- Signed-off-by: Matt Provost <provomat@amazon.com> * Design for New Saved Object Service Interface for Custom Repository (#3954) * Adds design document for new saved object service interface for custom repository Signed-off-by: Bandini Bhopi <bandinib@amazon.com> * enhance grouping for context menu options (#3924) * enhance grouping for context menu options * build panels tests and more comments Signed-off-by: David Sinclair <dsincla@rei.com> --------- Signed-off-by: David Sinclair <david@sinclair.tech> Signed-off-by: David Sinclair <dsincla@rei.com> Signed-off-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> * Adding Tao and Zilong to MAINTAINERS (#4137) * Adding Tao and Zilong to MAINTAINERS Signed-off-by: Yan Zeng <zengyan@amazon.com> * [MD]Update data-test-subj for functional tests & fix bug in edit flow (#4126) Signed-off-by: Su <szhongna@amazon.com> * Add support for Node.js >=14.20.1 <19 (#4071) * Bump Node.js requirements to 18 Signed-off-by: Miki <miki@amazon.com> * Replace `lmdb-store` with `lmdb` Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Bump `elastic-apm-node` to the latest minor Signed-off-by: Miki <miki@amazon.com> * Replace webpack and plugins with a patched version that uses xxhash64 * Use `xxhash64` as the hashing algorithm of webpack * Upgrade `globby` * Remove `fibers` Signed-off-by: Miki <miki@amazon.com> * Replace `fs.rmdir` with `fs.rm` in cross-platform tests Signed-off-by: Miki <miki@amazon.com> * Increase listener limit Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Add promise-stripping serializer Signed-off-by: Miki <miki@amazon.com> * Bump heap for CI Signed-off-by: Miki <miki@amazon.com> * Correct use of fs/promises in @osd/pm Signed-off-by: Miki <miki@amazon.com> * Use fs/promise in plugin post-install cleanup Signed-off-by: Miki <miki@amazon.com> * Set the test server's host to `0.0.0.0` Signed-off-by: Miki <miki@amazon.com> * Sync `.node-version` file Signed-off-by: Miki <miki@amazon.com> * Support both `isPrimary`, for Node 18, and `isMaster`, for Node 14 Signed-off-by: Miki <miki@amazon.com> * Add types when using `isDeepStrictEqual` Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Add names to `SchemaError` to log more specific errors Signed-off-by: Miki <miki@amazon.com> * Fix failing vega visualization tests outside the CI Signed-off-by: Miki <miki@amazon.com> * Fix snapshot of errors thrown for undefined accessors Signed-off-by: Miki <miki@amazon.com> * Fix flakiness of log_rotator Signed-off-by: Miki <miki@amazon.com> * Fix asynchronous `fs` usafe in plugin discover Signed-off-by: Miki <miki@amazon.com> * Fix mocks in @osd/optimizer Signed-off-by: Miki <miki@amazon.com> * Fix memory leaks caused by setting states on unloaded components Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Bump Node in Dockerfile Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Remove the response `close` event as an indicator of the requesting finishing https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3601#issuecomment-1515110477 Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * [BWC] Timeout after 3 mins of waiting for OSD to be running in tests Signed-off-by: Miki <miki@amazon.com> * Make build use the same node version that tests are run against Signed-off-by: Miki <miki@amazon.com> * Make Node resolve DNS by IPv4 first * This is helpful to resolve `locahost` to `127.0.0.1` Signed-off-by: Miki <miki@amazon.com> * Standardize patterns used by plugin discovery * Enhance absolute path serialization on Windows Signed-off-by: Miki <amoo_miki@yahoo.com> * Mock fetch in SenseEditor tests Signed-off-by: Miki <amoo_miki@yahoo.com> * Restore node-sass usage to fix build performance * `sass-loader@10` is the last version that supports webpack@4 * `sass` is extremely slow when using the legacy API (`render`) and to use the "Modern API" (`compileStringAsync`), `sass-loader@13` would be needed. * The performance of `sass@10` is made acceptable only with `fibers` but that is deprecated and doesn't work on Node 18 Signed-off-by: Anan Zhuang <ananzh@amazon.com> Signed-off-by: Miki <miki@amazon.com> * Revert "[CI] setup Chrome and utilize binary path (#3997)" This reverts commit 0188d052 Signed-off-by: Miki <miki@amazon.com> * Prevent fast-fail while running functional test in CI Signed-off-by: Miki <miki@amazon.com> * Revert "Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests (#3976)" This reverts commit 5ea0cbe25223e24badfe27f79813ee313c9af0b0. Signed-off-by: Miki <miki@amazon.com> * Save Cypress results artifacts during CI Signed-off-by: Miki <miki@amazon.com> * Add missing required dependency on `set-value` * Also force all to ^4.1.0 due to a vulnerability fixed in 3.1.0. Signed-off-by: Miki <miki@amazon.com> * Prevent multiple calls to bootstrap's shutdown Signed-off-by: Miki <miki@amazon.com> * Use Node 18.16.0 in distributions * Bump jest-canvas-mock to fix failing tests * Extend Node engines versions Signed-off-by: Miki <miki@amazon.com> * Normalize test snapshots across Node 14, 16, and 18 Signed-off-by: Miki <miki@amazon.com> * Update CHANGELOG for Node.js >=14.20.1 <19 support Signed-off-by: Miki <miki@amazon.com> --------- Signed-off-by: Miki <miki@amazon.com> Signed-off-by: Miki <amoo_miki@yahoo.com> Signed-off-by: Anan Zhuang <ananzh@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Remove timeline application (#3971) * Remove timeline application In this PR, we made the following changes: First of all, clean out some advanced settings specific to timeline application and tests. * Remove timelion:default_rows: This setting defines the default number of rows that a new Timelion sheet should have. * Remove timelion:default_rows: This setting defines the default number of columns that a new Timelion sheet should have. * Remove timelion:showTutorial. Second, remove src/plugin/timeline completely and modify timeline vis. Third, remove all the functional tests related to timeline application. Issue resolve https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3519 https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3593 Signed-off-by: ananzh <ananzh@amazon.com> --------- Signed-off-by: Anan Zhuang <ananzh@amazon.com> Signed-off-by: ananzh <ananzh@amazon.com> * Use `exec` in the CLI shell scripts to prevent new process creation (#3955) Signed-off-by: Miki <miki@amazon.com> * chore (lychee): Add company.net to exclusion list (#4171) Signed-off-by: Josh Romero <rmerqg@amazon.com> * Bundle Node 14 as a fallback for operating systems that cannot run Node 18 (#4151) Signed-off-by: ananzh <ananzh@amazon.com> Signed-off-by: Miki <miki@amazon.com> * Refactor authentication description message (#4179) resolves https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4173 Signed-off-by: Su <szhongna@amazon.com> --------- Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Signed-off-by: Kristen Tian <tyarong@amazon.com> Signed-off-by: Josh Romero <rmerqg@amazon.com> Signed-off-by: Kawika Avilla <kavilla414@gmail.com> Signed-off-by: Andrey Myssak <andreymyssak@gmail.com> Signed-off-by: Melissa Vagi <vagimeli@amazon.com> Signed-off-by: Manasvini B Suryanarayana <manasvis@amazon.com> Signed-off-by: Miki <miki@amazon.com> Signed-off-by: ananzh <ananzh@amazon.com> Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> Signed-off-by: Sergey Myssak <sergey.myssak@gmail.com> Signed-off-by: Anan Zhuang <ananzh@amazon.com> Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> Signed-off-by: Su <szhongna@amazon.com> Signed-off-by: Matt Provost <provomat@amazon.com> Signed-off-by: Bandini Bhopi <bandinib@amazon.com> Signed-off-by: David Sinclair <david@sinclair.tech> Signed-off-by: David Sinclair <dsincla@rei.com> Signed-off-by: Yan Zeng <zengyan@amazon.com> Signed-off-by: Miki <amoo_miki@yahoo.com> Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> Co-authored-by: opensearch-trigger-bot[bot] <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Kristen Tian <105667444+kristenTian@users.noreply.github.com> Co-authored-by: Kawika Avilla <kavilla414@gmail.com> Co-authored-by: Ashwin P Chandran <ashwinpc@amazon.com> Co-authored-by: Andrey Myssak <40265277+andreymyssak@users.noreply.github.com> Co-authored-by: Sergey Myssak <sergey.myssak@gmail.com> Co-authored-by: Melissa Vagi <vagimeli@amazon.com> Co-authored-by: Miki <miki@amazon.com> Co-authored-by: Miki <amoo_miki@yahoo.com> Co-authored-by: Manasvini B Suryanarayana <manasvis@amazon.com> Co-authored-by: Sean Neumann <1413295+seanneumann@users.noreply.github.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> Co-authored-by: Qingyang(Abby) Hu <abigailhu2000@gmail.com> Co-authored-by: Alexei Karikov <karikov.alist.ru@gmail.com> Co-authored-by: Andrey Myssak <andreymyssak@gmail.com> Co-authored-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> Co-authored-by: Zhongnan Su <szhongna@amazon.com> Co-authored-by: Matt Provost <provomat@amazon.com> Co-authored-by: Bandini <63824432+bandinib-amzn@users.noreply.github.com> Co-authored-by: David Sinclair <24573542+sikhote@users.noreply.github.com> Co-authored-by: Yan Zeng <46499415+zengyan-amazon@users.noreply.github.com> --- .github/.codecov.yml | 8 +- .github/pull_request_template.md | 16 +- .github/workflows/build_and_test_workflow.yml | 8 +- .i18nrc.json | 2 +- .lycheeexclude | 4 + .node-version | 2 +- .nvmrc | 2 +- CHANGELOG.md | 24 +- COMMUNICATIONS.md | 83 + CONTRIBUTING.md | 6 + DEVELOPER_GUIDE.md | 4 + Dockerfile | 2 +- MAINTAINERS.md | 2 + README.md | 10 +- config/opensearch_dashboards.yml | 28 +- .../with-security/check_advanced_settings.js | 7 - .../with-security/helpers/generate_data.js | 3 - .../check_advanced_settings.js | 7 - .../without-security/helpers/generate_data.js | 3 - .../current_saved_object_service_workflow.png | Bin 0 -> 26471 bytes ...proposed_saved_object_service_workflow.png | Bin 0 -> 53121 bytes ...current_saved_object_service_workflow.puml | 19 + ...roposed_saved_object_service_workflow.puml | 38 + .../saved_object_repository_factory_design.md | 171 + .../searchable_list_container_component.tsx | 1 - .../public/list_container_example.tsx | 9 + .../context_menu_examples.tsx | 2 + package.json | 31 +- .../README.md | 6 +- packages/osd-ace/package.json | 2 +- packages/osd-apm-config-loader/package.json | 2 +- .../src/utils/read_config.ts | 4 +- packages/osd-config-schema/package.json | 2 +- .../__snapshots__/schema_error.test.ts.snap | 5 +- .../src/errors/schema_error.test.ts | 12 +- .../src/errors/schema_error.ts | 1 + packages/osd-config/package.json | 2 +- .../osd-config/src/config_service.test.ts | 18 +- packages/osd-config/src/raw/read_config.ts | 4 +- packages/osd-cross-platform/src/path.test.ts | 8 +- packages/osd-dev-utils/package.json | 2 +- .../osd-dev-utils/src/proc_runner/proc.ts | 4 +- .../serializers/absolute_path_serializer.ts | 4 + .../osd-dev-utils/src/serializers/index.ts | 1 + .../serializers/strip_promises_serizlizer.ts | 9 + ...ch_dashboards_platform_plugin_discovery.ts | 26 +- .../package.json | 2 +- packages/osd-interpreter/package.json | 4 +- packages/osd-monaco/package.json | 3 +- packages/osd-monaco/webpack.config.js | 1 + packages/osd-opensearch/package.json | 2 +- packages/osd-optimizer/limits.yml | 1 - packages/osd-optimizer/package.json | 16 +- packages/osd-optimizer/src/__mocks__/lmdb.ts | 17 + .../basic_optimization.test.ts.snap | 2 +- .../basic_optimization.test.ts | 58 +- packages/osd-optimizer/src/limits.ts | 4 +- packages/osd-optimizer/src/node/cache.ts | 24 +- .../src/optimizer/optimizer_config.test.ts | 276 +- .../src/worker/webpack.config.ts | 6 +- packages/osd-pm/dist/index.js | 5088 ++++++++--------- packages/osd-pm/package.json | 7 +- packages/osd-pm/src/utils/fs.ts | 10 +- .../osd-pm/src/utils/project_checksums.ts | 7 +- packages/osd-pm/webpack.config.js | 1 + packages/osd-test/package.json | 2 +- .../lib/suite_tracker.test.ts | 14 +- packages/osd-ui-framework/Gruntfile.js | 2 +- packages/osd-ui-framework/package.json | 2 +- packages/osd-ui-shared-deps/package.json | 5 +- packages/osd-ui-shared-deps/webpack.config.js | 1 + ...nsearch-dashboards.release-notes-1.3.10.md | 33 + ...ensearch-dashboards.release-notes-2.7.0.md | 1 - scripts/bwc/opensearch_dashboards_service.sh | 2 +- scripts/bwc/utils.sh | 26 +- scripts/jest.js | 2 +- scripts/jest_integration.js | 7 +- scripts/use_node | 14 +- src/cli/cluster/cluster_manager.test.ts | 1 + .../integration_tests/invalid_config.test.ts | 2 +- .../reload_logging_config.test.ts | 4 +- src/cli_plugin/install/cleanup.js | 42 +- src/cli_plugin/install/cleanup.test.js | 161 +- src/core/public/chrome/chrome_service.test.ts | 3 + .../recently_accessed_service.test.ts | 3 + .../header/__snapshots__/header.test.tsx.snap | 40 +- .../header_help_menu.test.tsx.snap | 40 +- .../chrome/ui/header/header_help_menu.tsx | 3 +- .../public/core_app/status/status_app.tsx | 12 + .../public/doc_links/doc_links_service.ts | 3 - src/core/public/rendering/_base.scss | 1 - .../public/rendering/app_containers.test.tsx | 3 + src/core/public/rendering/app_containers.tsx | 6 +- src/core/server/bootstrap.ts | 28 +- src/core/server/http/router/request.ts | 6 +- .../http/router/validator/validator.test.ts | 7 +- src/core/server/legacy/legacy_service.test.ts | 2 + .../plugin_manifest_parser.test.mocks.ts | 9 + .../discovery/plugin_manifest_parser.test.ts | 265 +- .../discovery/plugin_manifest_parser.ts | 10 +- .../get_sorted_objects_for_export.test.ts | 429 +- .../inject_nested_depdendencies.test.ts | 262 +- .../import/validate_references.test.ts | 134 +- src/core/server/status/plugins_status.ts | 4 +- src/core/server/status/status_service.ts | 4 +- .../ui_settings/ui_settings_client.test.ts | 10 +- .../ui_settings/ui_settings_service.test.ts | 4 +- src/core/test_helpers/osd_server.ts | 1 + src/dev/build/lib/build.test.ts | 2 + src/dev/build/lib/config.ts | 15 +- .../tasks/bin/scripts/opensearch-dashboards | 2 +- .../bin/scripts/opensearch-dashboards-plugin | 2 +- .../tasks/create_archives_sources_task.ts | 16 +- .../nodejs/download_node_builds_task.test.ts | 50 +- .../tasks/nodejs/download_node_builds_task.ts | 39 +- .../nodejs/extract_node_builds_task.test.ts | 32 +- .../tasks/nodejs/extract_node_builds_task.ts | 28 +- .../build/tasks/nodejs/node_download_info.ts | 33 +- .../verify_existing_node_builds_task.test.ts | 6 +- .../verify_existing_node_builds_task.ts | 6 +- .../bin/opensearch-dashboards-docker | 1 - .../tasks/patch_native_modules_task.test.ts | 116 +- .../build/tasks/patch_native_modules_task.ts | 69 +- src/dev/jest/junit_reporter.js | 2 +- src/dev/stylelint/lint_files.js | 4 +- .../server/logging/rotate/log_rotator.test.ts | 5 +- .../server/logging/rotate/log_rotator.ts | 10 +- .../management_app/_advanced_settings.scss | 4 - .../components/field/field.test.tsx | 2 +- .../components/form/form.test.tsx | 11 +- .../management_app/components/form/form.tsx | 121 +- .../components/search/search.test.tsx | 2 +- .../components/search/search.tsx | 2 +- .../editor/legacy/console_editor/editor.tsx | 1 - .../__tests__/input.test.js | 5 +- .../__tests__/output_tokenization.test.js | 5 +- .../legacy_core_editor/legacy_core_editor.ts | 31 +- .../__tests__/integration.test.js | 4 +- .../__tests__/sense_editor.test.js | 5 +- .../sense_editor/sense_editor.test.mocks.ts | 6 + .../ace_token_provider/token_provider.test.ts | 6 +- src/plugins/console/public/lib/osd/osd.js | 20 +- src/plugins/console/public/styles/_app.scss | 12 - .../dashboard_empty_screen.test.tsx | 2 +- .../public/application/legacy_app.js | 18 +- .../listing/create_button.test.tsx | 2 +- .../application/listing/create_button.tsx | 2 +- .../tests/dashboard_container.test.tsx | 2 +- .../shard_failure_open_modal_button.test.tsx | 2 +- .../data_source/common/data_sources/types.ts | 6 + src/plugins/data_source/config.ts | 1 + .../data_source/opensearch_dashboards.json | 3 +- .../server/client/configure_client.test.ts | 24 + .../server/client/configure_client.ts | 3 +- .../server/client/configure_client_utils.ts | 3 +- .../legacy/configure_legacy_client.test.ts | 45 +- .../server/legacy/configure_legacy_client.ts | 8 +- src/plugins/data_source/server/plugin.ts | 3 +- .../data_source_connection_validator.ts | 12 +- .../server/routes/test_connection.ts | 15 +- ...ata_source_saved_objects_client_wrapper.ts | 33 +- .../server/util/endpoint_validator.test.js | 34 + .../server/util/endpoint_validator.ts | 59 + .../create_data_source_form.test.tsx | 2 + .../create_form/create_data_source_form.tsx | 386 +- .../create_data_source_wizard.test.tsx | 13 + .../create_data_source_wizard.tsx | 1 + .../edit_form/edit_data_source_form.tsx | 93 +- .../update_aws_credential_modal.tsx | 14 + .../validation/datasource_form_validation.ts | 7 + .../data_source_management/public/types.ts | 17 + src/plugins/dev_tools/public/plugin.ts | 2 +- .../components/action_bar/action_bar.test.tsx | 2 +- .../pager/tool_bar_pager_buttons.test.tsx | 2 +- .../table_header/table_header.test.tsx | 2 +- .../context_error_message.test.tsx | 2 +- .../application/components/doc/doc.test.tsx | 2 +- .../components/doc_viewer/doc_viewer.test.tsx | 2 +- .../hits_counter/hits_counter.test.tsx | 2 +- .../loading_spinner/loading_spinner.test.tsx | 2 +- .../sidebar/discover_field_search.test.tsx | 2 +- .../sidebar/discover_sidebar.test.tsx | 2 +- .../components/table/table.test.tsx | 2 +- .../timechart_header.test.tsx | 2 +- .../embeddable/docs/input_and_output_state.md | 5 +- .../lib/embeddables/embeddable_root.test.tsx | 2 +- .../lib/panel/embeddable_panel.test.tsx | 2 +- .../add_panel/add_panel_flyout.test.tsx | 2 +- .../components/editor/controls_tab.test.tsx | 2 +- .../editor/list_control_editor.test.tsx | 2 +- .../editor/range_control_editor.test.tsx | 2 +- .../components/vis/input_control_vis.test.tsx | 2 +- .../collectors/application_usage/schema.ts | 1 - .../server/collectors/management/schema.ts | 3 - .../get_saved_object_counts.test.ts | 3 - .../get_saved_object_counts.ts | 10 +- .../opensearch_dashboards/index.test.ts | 2 - .../server/collectors/ui_metric/schema.ts | 1 - src/plugins/telemetry/schema/oss_plugins.json | 57 - .../usage/telemetry_usage_collector.ts | 4 +- .../components/opt_in_example_flyout.tsx | 11 + src/plugins/tile_map/public/_tile_map.scss | 15 - src/plugins/tile_map/public/index.scss | 8 - src/plugins/timeline/README.md | 2 - .../timeline/opensearch_dashboards.json | 19 - src/plugins/timeline/public/_app.scss | 21 - src/plugins/timeline/public/_base.scss | 18 - src/plugins/timeline/public/app.js | 678 --- src/plugins/timeline/public/application.ts | 164 - src/plugins/timeline/public/breadcrumbs.js | 59 - .../components/timeline_deprecation.tsx | 64 - .../timeline_deprecation_directive.js | 53 - .../public/components/timelinehelp_tabs.js | 70 - .../components/timelinehelp_tabs_directive.js | 54 - .../timeline/public/directives/_form.scss | 36 - .../timeline/public/directives/_index.scss | 7 - .../directives/_saved_object_finder.scss | 95 - .../_timeline_expression_input.scss | 15 - .../public/directives/cells/_cells.scss | 62 - .../public/directives/cells/_index.scss | 1 - .../public/directives/cells/cells.html | 52 - .../timeline/public/directives/cells/cells.js | 63 - .../public/directives/cells/collection.ts | 87 - .../timeline/public/directives/chart/chart.js | 77 - .../public/directives/fixed_element.js | 61 - .../directives/fullscreen/fullscreen.html | 14 - .../directives/fullscreen/fullscreen.js | 47 - .../timeline/public/directives/input_focus.js | 46 - .../timeline/public/directives/key_map.ts | 132 - .../directives/saved_object_finder.html | 118 - .../public/directives/saved_object_finder.js | 327 -- .../saved_object_save_as_checkbox.html | 28 - .../saved_object_save_as_checkbox.js | 44 - .../directives/timeline_expression_input.html | 19 - .../directives/timeline_expression_input.js | 293 - .../timeline_expression_input_helpers.js | 291 - .../_index.scss | 1 - .../_timeline_expression_suggestions.scss | 36 - .../timeline_expression_suggestions.html | 109 - .../timeline_expression_suggestions.js | 50 - .../public/directives/timeline_grid.js | 79 - .../directives/timeline_help/_index.scss | 1 - .../timeline_help/_timeline_help.scss | 24 - .../timeline_help/timeline_help.html | 507 -- .../directives/timeline_help/timeline_help.js | 177 - .../directives/timeline_interval/_index.scss | 1 - .../timeline_interval/_timeline_interval.scss | 24 - .../timeline_interval/timeline_interval.html | 22 - .../timeline_interval/timeline_interval.js | 94 - .../public/directives/timeline_load_sheet.js | 41 - .../directives/timeline_options_sheet.js | 41 - .../public/directives/timeline_save_sheet.js | 41 - src/plugins/timeline/public/index.html | 95 - src/plugins/timeline/public/index.scss | 18 - src/plugins/timeline/public/index.ts | 36 - .../timeline/public/lib/observe_resize.js | 55 - src/plugins/timeline/public/panels/panel.ts | 56 - .../public/panels/timechart/schema.ts | 414 -- .../public/panels/timechart/timechart.ts | 39 - .../timeline/public/partials/load_sheet.html | 12 - .../timeline/public/partials/save_sheet.html | 106 - .../public/partials/sheet_options.html | 36 - src/plugins/timeline/public/plugin.ts | 162 - .../timeline/public/services/_saved_sheet.ts | 92 - .../timeline/public/services/saved_sheets.ts | 61 - .../timeline/public/timeline_app_state.ts | 88 - src/plugins/timeline/public/types.ts | 46 - src/plugins/timeline/server/config.ts | 43 - src/plugins/timeline/server/index.ts | 40 - src/plugins/timeline/server/plugin.ts | 114 - .../timeline/server/saved_objects/index.ts | 31 - .../server/saved_objects/timeline_sheet.ts | 56 - .../ui_actions/public/actions/action.ts | 2 +- .../public/actions/action_internal.ts | 2 +- .../build_eui_context_menu_panels.test.ts | 202 +- .../build_eui_context_menu_panels.tsx | 57 +- src/plugins/vis_type_timeline/config.ts | 15 +- .../vis_type_timeline/public/plugin.ts | 11 +- src/plugins/vis_type_timeline/server/index.ts | 5 - .../vis_type_timeline/server/plugin.ts | 11 +- .../server/series_functions/label.js | 6 +- .../server/series_functions/label.test.js | 20 + .../vega_visualization.test.js.snap | 8 +- .../public/vega_visualization.test.js | 6 + .../visualization_migrations.test.ts | 13 +- tasks/function_test_groups.js | 4 +- .../basic_opensearch_dashboards/mappings.json | 41 - .../saved_objects/relationships/mappings.json | 43 +- .../saved_objects/search/mappings.json | 43 +- .../saved_objects/10k/mappings.json | 41 - .../saved_objects/basic/mappings.json | 43 +- .../find_edgecases/mappings.json | 41 - .../search/count/mappings.json | 44 +- .../apps/dashboard/dashboard_filtering.js | 10 - .../apps/timeline/_expression_typeahead.js | 119 - test/functional/apps/timeline/index.js | 49 - test/functional/config.js | 4 - .../opensearch_dashboards/mappings.json | 44 +- .../dashboard/legacy/mappings.json | 44 +- .../discover/mappings.json | 42 - .../empty_opensearch_dashboards/mappings.json | 42 - .../opensearch_archiver/hamlet/mappings.json | 44 +- .../invalid_scripted_field/mappings.json | 43 +- .../management/mappings.json | 44 +- .../opensearch_archiver/mgmt/mappings.json | 43 +- .../mappings.json | 44 +- .../saved_objects_imports/mappings.json | 44 +- .../edit_saved_object/mappings.json | 43 +- .../timeline/mappings.json | 44 +- .../visualize/mappings.json | 42 - .../visualize_embedding/mappings.json | 44 +- .../visualize_source-filters/mappings.json | 44 +- .../visualize_source_filters/mappings.json | 44 +- test/functional/page_objects/index.ts | 2 - test/functional/page_objects/timeline_page.ts | 106 - .../opensearch_dashboards/mappings.json | 44 +- test/plugin_functional/config.ts | 1 + .../opensearch_dashboards.json | 9 + .../package.json | 17 + .../public/index.ts | 22 + .../public/plugin.tsx | 53 + .../tsconfig.json | 17 + .../dashboard_listing_plugin.ts | 71 + .../dashboard_listing_plugin/index.ts | 35 + yarn.lock | 854 ++- 325 files changed, 6440 insertions(+), 11679 deletions(-) create mode 100644 COMMUNICATIONS.md create mode 100644 docs/saved_objects/img/current_saved_object_service_workflow.png create mode 100644 docs/saved_objects/img/proposed_saved_object_service_workflow.png create mode 100644 docs/saved_objects/resources/current_saved_object_service_workflow.puml create mode 100644 docs/saved_objects/resources/proposed_saved_object_service_workflow.puml create mode 100644 docs/saved_objects/saved_object_repository_factory_design.md create mode 100644 packages/osd-dev-utils/src/serializers/strip_promises_serizlizer.ts create mode 100644 packages/osd-optimizer/src/__mocks__/lmdb.ts create mode 100644 release-notes/opensearch-dashboards.release-notes-1.3.10.md create mode 100644 src/plugins/data_source/server/util/endpoint_validator.test.js create mode 100644 src/plugins/data_source/server/util/endpoint_validator.ts delete mode 100644 src/plugins/tile_map/public/_tile_map.scss delete mode 100644 src/plugins/tile_map/public/index.scss delete mode 100644 src/plugins/timeline/README.md delete mode 100644 src/plugins/timeline/opensearch_dashboards.json delete mode 100644 src/plugins/timeline/public/_app.scss delete mode 100644 src/plugins/timeline/public/_base.scss delete mode 100644 src/plugins/timeline/public/app.js delete mode 100644 src/plugins/timeline/public/application.ts delete mode 100644 src/plugins/timeline/public/breadcrumbs.js delete mode 100644 src/plugins/timeline/public/components/timeline_deprecation.tsx delete mode 100644 src/plugins/timeline/public/components/timeline_deprecation_directive.js delete mode 100644 src/plugins/timeline/public/components/timelinehelp_tabs.js delete mode 100644 src/plugins/timeline/public/components/timelinehelp_tabs_directive.js delete mode 100644 src/plugins/timeline/public/directives/_form.scss delete mode 100644 src/plugins/timeline/public/directives/_index.scss delete mode 100644 src/plugins/timeline/public/directives/_saved_object_finder.scss delete mode 100644 src/plugins/timeline/public/directives/_timeline_expression_input.scss delete mode 100644 src/plugins/timeline/public/directives/cells/_cells.scss delete mode 100644 src/plugins/timeline/public/directives/cells/_index.scss delete mode 100644 src/plugins/timeline/public/directives/cells/cells.html delete mode 100644 src/plugins/timeline/public/directives/cells/cells.js delete mode 100644 src/plugins/timeline/public/directives/cells/collection.ts delete mode 100644 src/plugins/timeline/public/directives/chart/chart.js delete mode 100644 src/plugins/timeline/public/directives/fixed_element.js delete mode 100644 src/plugins/timeline/public/directives/fullscreen/fullscreen.html delete mode 100644 src/plugins/timeline/public/directives/fullscreen/fullscreen.js delete mode 100644 src/plugins/timeline/public/directives/input_focus.js delete mode 100644 src/plugins/timeline/public/directives/key_map.ts delete mode 100644 src/plugins/timeline/public/directives/saved_object_finder.html delete mode 100644 src/plugins/timeline/public/directives/saved_object_finder.js delete mode 100644 src/plugins/timeline/public/directives/saved_object_save_as_checkbox.html delete mode 100644 src/plugins/timeline/public/directives/saved_object_save_as_checkbox.js delete mode 100644 src/plugins/timeline/public/directives/timeline_expression_input.html delete mode 100644 src/plugins/timeline/public/directives/timeline_expression_input.js delete mode 100644 src/plugins/timeline/public/directives/timeline_expression_input_helpers.js delete mode 100644 src/plugins/timeline/public/directives/timeline_expression_suggestions/_index.scss delete mode 100644 src/plugins/timeline/public/directives/timeline_expression_suggestions/_timeline_expression_suggestions.scss delete mode 100644 src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.html delete mode 100644 src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.js delete mode 100644 src/plugins/timeline/public/directives/timeline_grid.js delete mode 100644 src/plugins/timeline/public/directives/timeline_help/_index.scss delete mode 100644 src/plugins/timeline/public/directives/timeline_help/_timeline_help.scss delete mode 100644 src/plugins/timeline/public/directives/timeline_help/timeline_help.html delete mode 100644 src/plugins/timeline/public/directives/timeline_help/timeline_help.js delete mode 100644 src/plugins/timeline/public/directives/timeline_interval/_index.scss delete mode 100644 src/plugins/timeline/public/directives/timeline_interval/_timeline_interval.scss delete mode 100644 src/plugins/timeline/public/directives/timeline_interval/timeline_interval.html delete mode 100644 src/plugins/timeline/public/directives/timeline_interval/timeline_interval.js delete mode 100644 src/plugins/timeline/public/directives/timeline_load_sheet.js delete mode 100644 src/plugins/timeline/public/directives/timeline_options_sheet.js delete mode 100644 src/plugins/timeline/public/directives/timeline_save_sheet.js delete mode 100644 src/plugins/timeline/public/index.html delete mode 100644 src/plugins/timeline/public/index.scss delete mode 100644 src/plugins/timeline/public/index.ts delete mode 100644 src/plugins/timeline/public/lib/observe_resize.js delete mode 100644 src/plugins/timeline/public/panels/panel.ts delete mode 100644 src/plugins/timeline/public/panels/timechart/schema.ts delete mode 100644 src/plugins/timeline/public/panels/timechart/timechart.ts delete mode 100644 src/plugins/timeline/public/partials/load_sheet.html delete mode 100644 src/plugins/timeline/public/partials/save_sheet.html delete mode 100644 src/plugins/timeline/public/partials/sheet_options.html delete mode 100644 src/plugins/timeline/public/plugin.ts delete mode 100644 src/plugins/timeline/public/services/_saved_sheet.ts delete mode 100644 src/plugins/timeline/public/services/saved_sheets.ts delete mode 100644 src/plugins/timeline/public/timeline_app_state.ts delete mode 100644 src/plugins/timeline/public/types.ts delete mode 100644 src/plugins/timeline/server/config.ts delete mode 100644 src/plugins/timeline/server/index.ts delete mode 100644 src/plugins/timeline/server/plugin.ts delete mode 100644 src/plugins/timeline/server/saved_objects/index.ts delete mode 100644 src/plugins/timeline/server/saved_objects/timeline_sheet.ts delete mode 100644 test/functional/apps/timeline/_expression_typeahead.js delete mode 100644 test/functional/apps/timeline/index.js delete mode 100644 test/functional/page_objects/timeline_page.ts create mode 100644 test/plugin_functional/plugins/dashboard_listing_test_plugin/opensearch_dashboards.json create mode 100644 test/plugin_functional/plugins/dashboard_listing_test_plugin/package.json create mode 100644 test/plugin_functional/plugins/dashboard_listing_test_plugin/public/index.ts create mode 100644 test/plugin_functional/plugins/dashboard_listing_test_plugin/public/plugin.tsx create mode 100644 test/plugin_functional/plugins/dashboard_listing_test_plugin/tsconfig.json create mode 100644 test/plugin_functional/test_suites/dashboard_listing_plugin/dashboard_listing_plugin.ts create mode 100644 test/plugin_functional/test_suites/dashboard_listing_plugin/index.ts diff --git a/.github/.codecov.yml b/.github/.codecov.yml index 1ce321b650d3..ba7dd34e7473 100644 --- a/.github/.codecov.yml +++ b/.github/.codecov.yml @@ -1,7 +1,11 @@ # https://docs.codecov.com/docs/codecov-yaml +codecov: + require_ci_to_pass: yes + coverage: status: project: default: - # https://docs.codecov.com/docs/commit-status#target - target: auto # coverage must be equal or above the previous commit + target: auto + threshold: 2% # the leniency in hitting the target + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9bd61e660b6c..2a9e814b5fe3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,8 +4,20 @@ ### Issues Resolved -<!-- List any issues this PR will resolve. --> -<!-- Example: closes #1234 --> +<!-- List any issues this PR will resolve. Prefix the issue with the keyword closes, fixes, fix --> +<!-- Example: closes #1234 or fixes <Issue_URL> --> + +## Screenshot + +<!-- Attach any relevant screenshots. Any change to the UI requires an attached screenshot in the PR Description --> + +## Testing the changes + +<!-- + Please provide detailed steps for validating your changes. This could involve specific commands to run, + pages to visit, scenarios to try or any other information that would help reviewers verify + the functionality of your change +--> ### Check List diff --git a/.github/workflows/build_and_test_workflow.yml b/.github/workflows/build_and_test_workflow.yml index fd4cdbbefa78..8fd3a402d547 100644 --- a/.github/workflows/build_and_test_workflow.yml +++ b/.github/workflows/build_and_test_workflow.yml @@ -25,11 +25,13 @@ env: TEST_OPENSEARCH_TRANSPORT_PORT: 9403 TEST_OPENSEARCH_PORT: 9400 OSD_SNAPSHOT_SKIP_VERIFY_CHECKSUM: true + NODE_OPTIONS: "--max-old-space-size=6144 --dns-result-order=ipv4first" jobs: build-lint-test: name: Build and Verify on ${{ matrix.name }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest] include: @@ -122,6 +124,7 @@ jobs: functional-tests: name: Run functional tests on ${{ matrix.name }} (ciGroup${{ matrix.group }}) strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest] group: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] @@ -384,6 +387,7 @@ jobs: with: name: ${{ matrix.version }}-test-failures path: | - ./artifacts/bwc_tmp/test/cypress/videos/without-security/* - ./artifacts/bwc_tmp/test/cypress/screenshots/without-security/* + ./artifacts/bwc_tmp/test/cypress/videos/* + ./artifacts/bwc_tmp/test/cypress/screenshots/* + ./artifacts/bwc_tmp/test/cypress/results/* retention-days: 1 diff --git a/.i18nrc.json b/.i18nrc.json index 234aa8de0c25..64ca6528aa5e 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -48,7 +48,7 @@ "src/plugins/telemetry_management_section" ], "tileMap": "src/plugins/tile_map", - "timeline": ["src/plugins/timeline", "src/plugins/vis_type_timeline"], + "timeline": ["src/plugins/vis_type_timeline"], "uiActions": "src/plugins/ui_actions", "visDefaultEditor": "src/plugins/vis_default_editor", "visTypeMarkdown": "src/plugins/vis_type_markdown", diff --git a/.lycheeexclude b/.lycheeexclude index 07317835aabf..35ae861e8f91 100644 --- a/.lycheeexclude +++ b/.lycheeexclude @@ -88,6 +88,8 @@ https://opensearch.org/redirect http://www.opensearch.org/painlessDocs https://www.hostedgraphite.com/ https://connectionurl.com +http://169.254.169.254/latest/meta-data/ +http://company.net/* # External urls https://www.zeek.org/ @@ -117,3 +119,5 @@ http://www.creedthoughts.gov https://media-for-the-masses.theacademyofperformingartsandscience.org/ https://yarnpkg.com/latest.msi https://forum.opensearch.org/ +https://facebook.github.io/jest/ +https://facebook.github.io/jest/docs/cli.html diff --git a/.node-version b/.node-version index f46d5e394243..6d80269a4f04 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -14.21.3 +18.16.0 diff --git a/.nvmrc b/.nvmrc index f46d5e394243..6d80269a4f04 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14.21.3 +18.16.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7c3fd87322..3a7acdd0a965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Eliminate dependency on `got` versions older than 11.8.5 ([#2801](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2801)) - [Multi DataSource] Add explicit no spellcheck on password fields ([#2818](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2818)) - [CVE-2022-25912] Bumps simple-git from 3.4.0 to 3.15.0 ([#3036](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3036)) -- [CVE-2022-35256] Bumps node version from 14.20.0 to 14.20.1 [#3166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3166)) +- [CVE-2022-35256] Bumps node version from 14.20.0 to 14.20.1 ([#3166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3166)) - [CVE-2022-46175] Bumps json5 version from 1.0.1 and 2.2.1 to 1.0.2 and 2.2.3 ([#3201](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3201)) - [CVE-2022-25860] Bumps simple-git from 3.15.1 to 3.16.0 ([#3345](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3345)) - [Security] Bumps hapi/statehood to 7.0.4 ([#3411](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3411)) @@ -22,6 +22,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [CVE-2023-25653] Bump node-jose to 2.2.0 ([#3445](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3445)) - [CVE-2023-26486][cve-2023-26487] Bump vega from 5.22.1 to 5.23.0 ([#3533](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3533)) - [CVE-2023-0842] Bump xml2js from 0.4.23 to 0.5.0 ([#3842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3842)) +- [Multi DataSource] Add private IP blocking validation on server side ([#3912](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3912)) +- Bump `joi` to v14 to avoid the possibility of prototype poisoning in a nested dependency ([#3952](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3952)) +- [CVE-2023-2251] Bump yaml to 2.2.2 ([#3947](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3947)) ### 📈 Features/Enhancements @@ -82,6 +85,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add satisfaction survey link to help menu ([#3676] (https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3676)) - [Vis Builder] Add persistence to visualizations inner state ([#3751](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3751)) - [Table Visualization] Move format table, consolidate types and add unit tests ([#3397](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3397)) +- [Multiple Datasource] Support Amazon OpenSearch Serverless ([#3957](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3957)) +- Add support for Node.js >=14.20.1 <19 ([#4071](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4071)) +- Bundle Node.js 14 as a fallback for operating systems that cannot run Node.js 18 ([#4151](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4151)) ### 🐛 Bug Fixes @@ -131,6 +137,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Table Visualization] Fix table rendering empty unused space ([#3797](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3797)) - [Table Visualization] Fix data table not adjusting height on the initial load ([#3816](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3816)) - Cleanup unused url ([#3847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3847)) +- [BUG] Docked navigation impacts visibility of bottom bar component ([#3978](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3978)) ### 🚞 Infrastructure @@ -145,6 +152,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Upgrade yarn version to be compatible with @openearch-project/opensearch ([#3443](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3443)) - [CI] Reduce redundancy by using matrix strategy on Windows and Linux workflows ([#3514](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3514)) - Add an achievement badger to the PR ([#3721](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3721)) +- Install chrome driver for functional tests from path set by environment variable `TEST_BROWSER_BINARY_PATH`([#3997](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3997)) +- Adds threshold to code coverage config to prevent workflow failures ([#4040](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4040)) ### 📝 Documentation @@ -164,7 +173,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Doc] Update SECURITY.md with instructions for nested dependencies and backporting ([#3497](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3497)) - [Doc] [Console] Fix/update documentation links in Dev Tools console ([#3724](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3724)) - [Doc] Update DEVELOPER_GUIDE.md with added manual bootstrap timeout solution and max virtual memory error solution with docker ([#3764](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3764)) +- [Doc] Add COMMUNICATIONS.md with info about Slack, forum, office hours ([#3837](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3837)) - [Doc] Add docker files and instructions for debugging Selenium functional tests ([#3747](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3747)) +- [Saved Object Service] Adds design doc for new Saved Object Service Interface for Custom Repository [#3954](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3954) ### 🛠 Maintenance @@ -181,7 +192,11 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Remove the unused `renovate.json5` file ([3489](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3489)) - Allow selecting the Node.js binary using `NODE_HOME` and `OSD_NODE_HOME` ([3508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3508)) - Bump `styled-components` from 5.3.5 to 5.3.9 ([#3678](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3678)) +- Bump `js-yaml` from 3.14.0 to 4.1.0 ([#3770](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3770)) - Bump `oui` from `1.0.0` to `1.1.1` ([#3884](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3884)) +- Use `exec` in the CLI shell scripts to prevent new process creation ([#3955](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3955)) +- Adding @ZilongX and @Flyingliuhub as maintainers. ([#4137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4137)) +- Remove timeline application ([#3971](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3971)) ### 🪛 Refactoring @@ -191,7 +206,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Console] Replace jQuery.ajax with core.http when calling OSD APIs in console ([#3080](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3080)) - [I18n] Fix Listr type errors and error handlers ([#3629](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3629)) - [Multiple DataSource] Present the authentication type choices in a drop-down ([#3693](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3693)) -- [Console] Replace jQuery usage in console plugin with native methods ([#3733](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3733)) +- [Console] Remove unused ul element and its custom styling ([#3993](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3993)) +- Fix EUI/OUI type errors ([#3798](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3798)) +- Remove unused Sass in `tile_map` plugin ([#4110](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4110)) ### 🔩 Tests @@ -238,6 +255,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multi DataSource] UX enhancement on Data source management stack ([#2521](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2521)) - [Multi DataSource] UX enhancement on Update stored password modal for Data source management stack ([#2532](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2532)) - [Monaco editor] Add json worker support ([#3424](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3424)) +- Enhance grouping for context menus ([#3169](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3169)) +- Replace re2 with RegExp in timeline and add unit tests ([#3908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3908)) ### 🐛 Bug Fixes @@ -249,6 +268,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Console] Fix dev tool console autocomplete not loading issue ([#3775](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3775)) - [Console] Fix dev tool console run command with query parameter error ([#3813](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3813)) - Add clarifying tooltips to header navigation ([#3573](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3573)) +- [Dashboards Listing] Fix listing limit to utilize `savedObjects:listingLimit` instead of `savedObjects:perPage` ([#4021](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4021)) ### 🚞 Infrastructure diff --git a/COMMUNICATIONS.md b/COMMUNICATIONS.md new file mode 100644 index 000000000000..c1dbfc114ce7 --- /dev/null +++ b/COMMUNICATIONS.md @@ -0,0 +1,83 @@ +# OpenSearch Dashboards Communication + +- [Overview](#overview) +- [Slack](#slack) +- [Forum](#forum) +- [Developer Office Hours](#developer-office-hours) + - [What it is](#what-it-is) + - [When](#when) + - [How to sign up](#how-to-sign-up) + - [FAQ](#faq) + +## Overview + +The purpose of this document is to provide information regarding the communication channels for OpenSearch Dashboards. All communication is subject to the [OpenSearch Code of Conduct](CODE_OF_CONDUCT.md). Please see [CONTRIBUTING](CONTRIBUTING.md) if you're interested in contributing to the project. + +## Slack + +The OpenSearch project has a public workspace on [Slack](https://opensearch.slack.com). See the [Getting Started guide]() for steps to register and setup the workspace. + +Once registered, check out these channels for discussion of OpenSearch Dashboards topics: + +- [#dashboards](https://opensearch.slack.com/archives/C01QENNTGUD) +- [#dashboards-ux](https://opensearch.slack.com/archives/C05389T9LJC) + +## Forum + +Slack conversations are not searchable outside the workspace. For this reason we encourage using the [OpenSearch Dashboards category](https://forum.opensearch.org/c/opensearch-dashboards/57) of the forum for technical support discussions or summarizing findings for the rest of the community. + +## Developer Office Hours + +### What it is + +A recurring 1-hour virtual meeting for community developers to chat with [OpenSearch Dashboards project maintainers](MAINTAINERS.md). Priority will be given to topics that are signed-up in advance, but ad-hoc discussions are welcome in any remaining time. + +While we'll always prioritize asynchronous communication, sometimes a community call is the most effective and efficient venue to share information and knowledge. Some reasons to sign up: + +1. Review a proposal or technical design for a new feature in OpenSearch Dashboards or an OpenSearch Dashboards plugin +2. Learn more about how to build and extend OpenSearch Dashboards - which APIs, plugins, resources, and services are available to speed development +3. Discuss OpenSearch Dashboard roadmap and technical initiatives + +Signing up isn't required to attend - all OpenSearch Dashboards contributors or interested developers are welcome as participants. + +Bring your ideas and projects early, while you still have time and flexibility to make significant changes. + +### When + +Every other Thursday, 10AM-11AM PT. + +### How to sign up + +There will be a forum post for each iteration of the meeting, with pre-defined slots. To sign-up, simply reply in the forum thread with the following template: + +* Topic: [a brief description of what you'd like to discuss] +* Requested by: [provide GitHub aliases of attendees] +* GitHub issues or PRs: [before signing up, make sure to create an issue, whether in the OpenSearch Dashboards repository or your own plugin repository] +* Time required [choose 15, 30, 45, or 60 minutes] +* Requested maintainer: [optional; provide GitHub alias of any particular maintainer you’d like to attend] + +### FAQ + +#### Will the meetings be recorded? + +Yes, we plan to record each office hours session and post to our YouTube channel so the information can be more easily shared and referenced. + +#### Will all maintainers attend? + +Generally no, but there will always be at least one maintainer. We'll review the sign-ups ahead of time to make sure the right subject-matter experts will attend, depending on the topics. + +#### What happens if there are no sign-ups for a particular session? + +The session will still occur, and the maintainers will present a brief knowledge-sharing session or demo. We'll also hold ad-hoc discussions, but the session may end early. + +#### Is it first come first serve or do we get to decide which topics we discuss in a session? + +For sign-ups, it’s first-come first served, until we decide we need another method. + +#### Will there also be meeting notes? or is the recording the only available transcript? + +No. But we’ll also post the chat transcript and any slides shared (see https://forum.opensearch.org/t/opensearch-community-meeting-2023-0131/11892/5 as example) + +#### How can I cancel or reschedule? + +Just leave another forum reply, as early as possible so other folks have the opportunity to sign-up for the same spot. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 045b17019b7d..4a605d04c052 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,7 @@ - [Contributing to OpenSearch](#contributing-to-opensearch-dashboards) - [First Things First](#first-things-first) - [Ways to Contribute](#ways-to-contribute) + - [Join the Discussion](#join-the-discussion) - [Bug Reports](#bug-reports) - [Feature Requests](#feature-requests) - [Documentation Changes](#documentation-changes) @@ -19,6 +20,11 @@ OpenSearch is a community project that is built and maintained by people just li **Only submit your own work** (or work you have sufficient rights to submit) - Please make sure that any code or documentation you submit is your work or you have the rights to submit. We respect the intellectual property rights of others, and as part of contributing, we'll ask you to sign your contribution with a "Developer Certificate of Origin" (DCO) that states you have the rights to submit this work and you understand we'll use your contribution. There's more information about this topic in the [DCO section](#developer-certificate-of-origin). ## Ways to Contribute + +### Join the Discussion + +See the [communication guide](COMMUNICATION.md)for information on how to join our slack workspace, forum, or developer office hours. + ### Bug Reports A bug is when software behaves in a way that you didn't expect and the developer didn't intend. To help us understand what's going on, we first want to make sure you're working from the latest version. Please make sure you're testing against the [latest version](https://github.com/opensearch-project/OpenSearch-Dashboards). diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index d3c8b269c4c0..3df2106e9678 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -209,6 +209,10 @@ $ yarn start --run-examples - [Project testing guidelines](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/TESTING.md) - [Plugin conventions](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/CONVENTIONS.md#technical-conventions) +#### Join the discussion + +See the [communication guide](COMMUNICATION.md)for information on how to join our slack workspace, forum, or developer office hours. + ## Alternative development installations Although the [getting started guide](#getting-started-guide) covers the recommended development environment setup, there are several alternatives worth being aware of. diff --git a/Dockerfile b/Dockerfile index 8f2742a6c1f2..9337b827810e 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG NODE_VERSION=14.20.1 +ARG NODE_VERSION=18.16.0 FROM node:${NODE_VERSION} AS base ENV HOME '.' diff --git a/MAINTAINERS.md b/MAINTAINERS.md index ecb3f2928616..ae42e47b014b 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -17,6 +17,8 @@ This document contains a list of maintainers in this repo. See [opensearch-proje | Kristen Tian | [kristenTian](https://github.com/kristenTian) | Amazon | | Zhongnan Su | [zhongnansu](https://github.com/zhongnansu) | Amazon | | Manasvini B Suryanarayana | [manasvinibs](https://github.com/manasvinibs) | Amazon | +| Tao Liu | [Flyingliuhub](https://github.com/Flyingliuhub) | Amazon | +| Zilong Xia | [ZilongX](https://github.com/ZilongX) | Amazon | ## Emeritus diff --git a/README.md b/README.md index d1b8fc9ed9dd..5c6c764f87f6 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,13 @@ ## Welcome -OpenSearch Dashboards is an open source search and analytics visualization. We aim to be the best community-driven platform and provide all the contributors a great open source experience. +OpenSearch Dashboards is an open-source data visualization tool designed to work with OpenSearch. OpenSearch Dashboards gives you data visualization tools to improve and automate business intelligence and support data-driven decision-making and strategic planning. -Feel free to take a look at what the community has been up to, and then head over to the [Project Board](https://github.com/opensearch-project/OpenSearch-Dashboards/projects) to track release targets, or jump in and [start opening issues](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/new/choose), [set up your development environment](DEVELOPER_GUIDE.md#getting-started), or [start contributing](CONTRIBUTING.md). +We aim to be an exceptional community-driven platform and to foster open participation and collective contribution with all contributors. Stay up to date on what's happening with the OpenSearch Project by tracking GitHub [issues](https://github.com/opensearch-project/OpenSearch-Dashboards/issues) and [pull requests](https://github.com/opensearch-project/OpenSearch-Dashboards/pulls). + +You can [contribute to this project](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/CONTRIBUTING.md) by [opening issues](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/new/choose) to give feedback, share ideas, identify bugs, and contribute code. + +Set up your [OpenSearch Dashboards development environment](ttps://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/DEVELOPER_GUIDE.md#getting-started-guide) today! The project team looks forward to your contributions. ## Code Summary @@ -23,7 +27,7 @@ Feel free to take a look at what the community has been up to, and then head ove * [Project Website](https://opensearch.org/) * [Downloads](https://opensearch.org/downloads.html) * [Documentation](https://opensearch.org/docs/) -* Need help? Try [Forums](https://discuss.opendistrocommunity.dev/) +* Need help? See the [communication guide](COMMUNICATION.md) for various options * [Project Principles](https://opensearch.org/#principles) * [Developer Guide](DEVELOPER_GUIDE.md) * [Contributing to OpenSearch](CONTRIBUTING.md) diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index f6f1486e5499..a28a2301dcf6 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -229,8 +229,7 @@ # functionality in Visualization. # vis_builder.enabled: false -# Set the value of this setting to true to enable the experimental multiple data source -# support feature. Use with caution. +# Set the value of this setting to true to enable multiple data source feature. #data_source.enabled: false # Set the value of these settings to customize crypto materials to encryption saved credentials # in data sources. @@ -238,6 +237,31 @@ #data_source.encryption.wrappingKeyNamespace: 'changeme' #data_source.encryption.wrappingKey: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +#data_source.endpointDeniedIPs: [ +# '127.0.0.0/8', +# '::1/128', +# '169.254.0.0/16', +# 'fe80::/10', +# '10.0.0.0/8', +# '172.16.0.0/12', +# '192.168.0.0/16', +# 'fc00::/7', +# '0.0.0.0/8', +# '100.64.0.0/10', +# '192.0.0.0/24', +# '192.0.2.0/24', +# '198.18.0.0/15', +# '192.88.99.0/24', +# '198.51.100.0/24', +# '203.0.113.0/24', +# '224.0.0.0/4', +# '240.0.0.0/4', +# '255.255.255.255/32', +# '::/128', +# '2001:db8::/32', +# 'ff00::/8', +# ] + # Set the value of this setting to false to hide the help menu link to the OpenSearch Dashboards user survey # opensearchDashboards.survey.url: "https://survey.opensearch.org" diff --git a/cypress/integration/with-security/check_advanced_settings.js b/cypress/integration/with-security/check_advanced_settings.js index 502ee150a33f..9ca41207724e 100644 --- a/cypress/integration/with-security/check_advanced_settings.js +++ b/cypress/integration/with-security/check_advanced_settings.js @@ -25,13 +25,6 @@ describe('verify the advanced settings are saved', () => { .should('eq', 'true'); }); - it('the Timeline default columns field is set to 4', () => { - cy.get('[data-test-subj="advancedSetting-editField-timeline:default_columns"]').should( - 'have.value', - 4 - ); - }); - it('the Timeline Maximum buckets field is set to 4', () => { cy.get('[data-test-subj="advancedSetting-editField-timeline:max_buckets"]').should( 'have.value', diff --git a/cypress/integration/with-security/helpers/generate_data.js b/cypress/integration/with-security/helpers/generate_data.js index 1509dca7b012..4833a81a1398 100755 --- a/cypress/integration/with-security/helpers/generate_data.js +++ b/cypress/integration/with-security/helpers/generate_data.js @@ -31,9 +31,6 @@ describe('Generating BWC test data with security', () => { it('adds advanced settings', () => { miscUtils.visitPage('app/management/opensearch-dashboards/settings'); cy.get('[data-test-subj="advancedSetting-editField-theme:darkMode"]').click(); - cy.get('[data-test-subj="advancedSetting-editField-timeline:default_columns"]').type( - '{selectAll}4' - ); cy.get('[data-test-subj="advancedSetting-editField-timeline:max_buckets"]').type( '{selectAll}4' ); diff --git a/cypress/integration/without-security/check_advanced_settings.js b/cypress/integration/without-security/check_advanced_settings.js index 474a8178441a..9268d86a16e5 100644 --- a/cypress/integration/without-security/check_advanced_settings.js +++ b/cypress/integration/without-security/check_advanced_settings.js @@ -18,13 +18,6 @@ describe('verify the advanced settings are saved', () => { .should('eq', 'true'); }); - it('the Timeline default columns field is set to 4', () => { - cy.get('[data-test-subj="advancedSetting-editField-timeline:default_columns"]').should( - 'have.value', - 4 - ); - }); - it('the Timeline Maximum buckets field is set to 4', () => { cy.get('[data-test-subj="advancedSetting-editField-timeline:max_buckets"]').should( 'have.value', diff --git a/cypress/integration/without-security/helpers/generate_data.js b/cypress/integration/without-security/helpers/generate_data.js index f792a9b304a1..4f2962769c16 100755 --- a/cypress/integration/without-security/helpers/generate_data.js +++ b/cypress/integration/without-security/helpers/generate_data.js @@ -14,9 +14,6 @@ describe('Generating BWC test data without security', () => { it('adds advanced settings', () => { miscUtils.visitPage('app/management/opensearch-dashboards/settings'); cy.get('[data-test-subj="advancedSetting-editField-theme:darkMode"]').click(); - cy.get('[data-test-subj="advancedSetting-editField-timeline:default_columns"]').type( - '{selectAll}4' - ); cy.get('[data-test-subj="advancedSetting-editField-timeline:max_buckets"]').type( '{selectAll}4' ); diff --git a/docs/saved_objects/img/current_saved_object_service_workflow.png b/docs/saved_objects/img/current_saved_object_service_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..b763101b698793e87822e61d5fca0b2915d4e2f9 GIT binary patch literal 26471 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≦{#&}0Zam}ca?$w|v=~w{ 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 literal 0 HcmV?d00001 diff --git a/docs/saved_objects/img/proposed_saved_object_service_workflow.png b/docs/saved_objects/img/proposed_saved_object_service_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..117fd0217d519a6724bcd570f42e86d3ddbaa0bb GIT binary patch literal 53121 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 literal 0 HcmV?d00001 diff --git a/docs/saved_objects/resources/current_saved_object_service_workflow.puml b/docs/saved_objects/resources/current_saved_object_service_workflow.puml new file mode 100644 index 000000000000..bc5bbf82c621 --- /dev/null +++ b/docs/saved_objects/resources/current_saved_object_service_workflow.puml @@ -0,0 +1,19 @@ +@startuml +title: Current Saved Object Service Flow +actor User +participant "Saved Object Client" as Client +participant "Saved Object Repository" as Repo +participant "Opensearch" as OS + +User -> Client: Create Saved Object +Client -> Repo: Create Saved Object +Repo -> OS: Index Saved Object +OS --> Repo: Saved Object Saved +Client -> User: Saved Object Created +User -> Client: Get Saved Object +Client -> Repo: Get Saved Object +Repo -> OS: Get Saved Object +OS --> Repo: Return Saved Object +Repo -> Client: Return Saved Object +Client -> User: Saved Object Data +@enduml \ No newline at end of file diff --git a/docs/saved_objects/resources/proposed_saved_object_service_workflow.puml b/docs/saved_objects/resources/proposed_saved_object_service_workflow.puml new file mode 100644 index 000000000000..27a5e1cd49ed --- /dev/null +++ b/docs/saved_objects/resources/proposed_saved_object_service_workflow.puml @@ -0,0 +1,38 @@ +@startuml + +title: Proposed Saved Object Service Flow + +actor User + +participant "OpenSearch-Dashboards" as OSD + +box "Saved Object Service" #LightBlue +participant "Saved Object Client" as Client +participant "Repository Factory Provider" as Factory +end box + +box "Dashboards Storage Plugin" #LightYellow +participant "Repository\n(e.g. PostgresRepository,\nDynamoDBRepository)" as Repo +participant "Metadata Storage\n(e.g. Postgres, \nDynamoDB etc)" as Meta +end box + +autonumber +group OSD Bootstrap +Repo -> Factory: Register custom repository +Factory -> Client: Returns repository +Client -> OSD: Returns Saved Object Client +end group +User -> Client: Create Saved Object +Client -> Repo: Create Saved Object +Repo -> Meta: Create/Update Record +Meta --> Repo: Saved Object Saved +Client -> User: Saved Object Created +User -> Client: Get Saved Object +Client -> Repo: Get Saved Object +Repo -> Meta: Fetch Saved Object from storage +Meta --> Repo: Return Saved Object +Repo -> Client: Return Saved Object +Client -> User: Saved Object Data + +skinparam BoxPadding 15 +@enduml \ No newline at end of file diff --git a/docs/saved_objects/saved_object_repository_factory_design.md b/docs/saved_objects/saved_object_repository_factory_design.md new file mode 100644 index 000000000000..d3fedd3575dc --- /dev/null +++ b/docs/saved_objects/saved_object_repository_factory_design.md @@ -0,0 +1,171 @@ +# Proposed Saved Object Service Interface for Custom Repository + +## Introduction + +The new saved object service interface for custom repository is a project that aims to improve scalability of the existing saved object service by introducing a new interface. The goal of this project is to provide a more efficient and flexible interface that will make it easier for developers to configure metadata of Dashboards in any different storage than OpenSearch, such as mysql, postgres, DDB, serverless (S3+ Athena). + +Currently, Dashboards stores its metadata configuration inside OpenSearch index (called .kibana). This approach is by design of Dashboards and biased towards product decision by upstream which works seamlessly and out of the box for customers but it introduces challenges while operating at scale and providing high availability for Dashboards. While choosing OpenSearch as a storage for Dashboards metadata, availability of Dashboards depends on OpenSearch cluster’s availability and other cluster parameters such as cluster health, state, versions which could make Dashboards unavailable. + +To mitigate above problem and unblock future extensibility of Dashboards, we are building Dashboards Meta storage adaptor to decouple Dashboards metadata storage from OpenSearch. This project will focus on introducing new interface in Saved Object Service using which developer can build their custom repository and save Dashboards metadata in storage of their choice. + +The stakeholders of this new interface include the developers of the Dashboards and community contributors who wants to use other metadata store. + +## Architecture Overview + +The Saved Object Service is a critical component of Dashboards that provides a way to store and manage application data. It is built using a modular architecture that provides a high degree of flexibility and extensibility. The new interface will be designed to replace [ISavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L134) implementation so that developers can build plugins that leverage the power of existing saved object service and use their own database to store and retrieve metadata of OpenSearch Dashboards. + +### Current Architecture + +The repository interface named [ISavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L134) in OpenSearch-Dashboards is a module that provides an interface for managing saved objects. The [SavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L139) is the implementation of [ISavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L134), which uses OpenSearch index as it’s data store. It is responsible for storing, retrieving, and deleting saved objects for Dashboards, such as visualizations, dashboards, and searches. + +The Saved Object Repository is built on top of the OpenSearch client and provides a simplified interface for working with OpenSearch. It uses the Saved Object Serializer to convert saved objects between their internal and external representations. The repository is then being consumed by Saved object client to create scoped saved object client. + +![img](./img/current_saved_object_service_workflow.png) + +### Proposed Architecture + +- **Approach 1 (Preferred)**: The proposed architecture will add one more layer of abstraction in Saved Object Service. `The Repository Factory Provider` in OpenSearch Dashboards will be responsible for creating and managing instances of the Repository (e.g. SavedObjectRepository, PostgresRepository, DynamoDBRepository etc.), which is used to interact with the metadata storage that stores the saved objects. Currently we have an repository interface named [ISavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L134), and the [SavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L139) is the implementation, which use an OpenSearch index as its data store. This approach would make the implementation of [ISavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L134) replaceable by plugin. + + ![img](./img/proposed_saved_object_service_workflow.png) + + * Pros: + * Only change needed in Dashboard is to introduce one more abstraction layer in Saved Object Service. + * Adds opportunity for community developers to contribute for other meta store. + + * Cons + * Code reusability is low. + <br/> + +**POC**: +1) Core Dashboards Change: https://github.com/bandinib-amzn/OpenSearch-Dashboards/commit/b9cfc14 +2) Postgres Repository Plugin: https://github.com/bandinib-amzn/metadata_plugin/commit/dac35f0 + +`SavedObjectsServiceSetup` provides interface to create custom Saved Object Repository. +``` +/** +* Set the default {@link SavedObjectRepositoryFactoryProvider | factory provider} for creating Saved Objects repository. +* Only one repository can be set, subsequent calls to this method will fail. +*/ +registerRepositoryFactoryProvider: ( +respositoryFactoryProvider: SavedObjectRepositoryFactoryProvider +) => void; +``` + +Here are the main steps involved in using the Saved Objects Repository Factory in Dashboards: +1. Define the dependencies: The Saved Object Repository Factory Provider requires the function which creates instance of [ISavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L134). + ``` + export const repositoryFactoryProvider: SavedObjectRepositoryFactoryProvider = ( + options: SavedObjectsRepositoryOptions + ) => { + . + . + . + return new PostgresRepository({ + typeRegistry, + serializer, + migrator, + allowedTypes, + }); + } + ``` +2. Register the provider: Register the repository factory provider with right dependencies. + ``` + core.savedObjects.registerRepositoryFactoryProvider(repositoryFactoryProvider); + ``` +3. Implement the Saved Object Operations for chosen storage type: Implement the CRUD and other operations for contracts defined in [ISavedObjectRepository](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/saved_objects/service/lib/repository.ts#L134) + ``` + async create<T = unknown>( + type: string, + attributes: T, + options: SavedObjectsCreateOptions = {} + ): Promise<SavedObject<T>> { + ... + } + + async get<T = unknown>( + type: string, + id: string, + options: SavedObjectsBaseOptions = {} + ): Promise<SavedObject<T>> { + ... + } + + async update<T = unknown>( + type: string, + id: string, + attributes: Partial<T>, + options: SavedObjectsUpdateOptions = {} + ): Promise<SavedObjectsUpdateResponse<T>> { + ... + } + + async deleteFromNamespaces( + type: string, + id: string, + namespaces: string[], + options: SavedObjectsDeleteFromNamespacesOptions = {} + ): Promise<SavedObjectsDeleteFromNamespacesResponse> { + ... + } + . + . + . + ``` + +- **Approach 2**: Build external plugin and using saved object client wrapper or client factory provider injection mechanism we can build custom object for Postgres or other DB. + + * Pros: + * No changes in core Dashboards. That means we can keep Dashboards as it is with very minimal changes. + + + * Cons + * Code reusability is low. + * Some components of Saved object service such as Serializer, Type registry, interface to create internal and scoped repository are only available during Saved Object Service Start. As per the current architecture, first Saved Object Service Setup → Plugin Setup → Saved Object Service Start → Plugin Start. Some core plugin (e.g. opensearch_dashboards_usage_collection) calls find operation before plugin start and it fails because some components are still not available before plugin start. + <br/> + + **POC**: https://github.com/bandinib-amzn/metadata_plugin/compare/f040daf...89213eb + + +- **Approach 3**: In this approach, we just extend the `SavedObjectsRepository` class and override CRUD and other saved object operation in core Dashboards. + + * Pros: + * As we are extending the repository in core saved object service itself, we can reuse the validation and utility functions for other database options. + + + * Cons + * Changes in core Dashboards : We will be making considerable changes in critical component of Dashboards. + * With this approach, user will have to use the data storage option that we choose. + <br/> + + **POC**: https://github.com/bandinib-amzn/OpenSearch-Dashboards/compare/main...22d7f30 + +## Implementation Details + + +| Repository | Component | Change | +| ----------- | ----------- | ----------- | +| OpenSearch-Dashboards | Saved Object Service | Add Saved object repository factory provider | +| OpenSearch-Dashboards | Config | Configuration for metadata storage | +| MetaStorage-Plugin [Name TBD] | Plugin / Extension | We will build new plugin for Postgres. This is use case for new interface in Saved Object Repository. | + +### Configuration for metadata storage: +``` +metaStorage.enabled: true +metaStorage.config: { + type: 'xxxx', + hostName: 'xxxx', + userName: 'xxxx', + password: 'xxxx', + port: xxxx, +} +``` + +## Testing and Quality Assurance + +### Testing Approach + +The following testing approach will be used to ensure the quality of the system: + +1. **Unit testing**: Metadata store plugin will be thoroughly unit tested to ensure it meets its requirements and performs as expected. Also we will add new test cases in OpenSearch-Dashboards to test new repository factory provider. +2. **Integration testing**: Components will be integrated and tested together to ensure they work together seamlessly and without conflicts. + diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx index c24d258fd7d8..7e742aba7cc7 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx @@ -82,7 +82,6 @@ export class SearchableListContainerComponentInner extends Component<Props, Stat const output = props.embeddable.getChild(id).getOutput(); hasMatch[id] = hasHasMatchOutput(output) && output.hasMatch; }); - props.embeddable.getChildIds().forEach((id) => (checked[id] = false)); this.state = { checked, hasMatch, diff --git a/examples/embeddable_explorer/public/list_container_example.tsx b/examples/embeddable_explorer/public/list_container_example.tsx index 797b87513039..f466d120e218 100644 --- a/examples/embeddable_explorer/public/list_container_example.tsx +++ b/examples/embeddable_explorer/public/list_container_example.tsx @@ -164,6 +164,15 @@ export function ListContainerExample({ The first HelloWorldEmbeddable does not emit the hasMatch output variable, so the container chooses to hide it. </p> + <p> + Type some strings in the search bar, and press Check matching button. If the search + string matches with any strings from the title or the description of the children + embeddables, the child embeddable's check box will be checked. Noted that the + search filter is case sensitive. However, even if the search string matches with the + strings in the HelloWorldEmbeddable, its check box will not be checked because of the + reason explained above. If we click Delete checked, all the selected child embeddables + will be deleted from the container. + </p> <p> Check out the "Dynamically adding children" section, to see how to add diff --git a/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx b/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx index d38d80d0fac0..b01d04c1608b 100644 --- a/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx +++ b/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx @@ -44,6 +44,8 @@ export const ContextMenuExamples: React.FC = () => { <p> Below examples show how context menu panels look with varying number of actions and how the actions can be grouped into different panels using <EuiCode>grouping</EuiCode> field. + Grouping can only be one layer deep. A group needs to have at least two items for grouping + to work. A separator is automatically added between groups. </p> <EuiFlexGroup> diff --git a/package.json b/package.json index b9eb244f7756..30e6756ca108 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "test:jest:ci": "scripts/use_node scripts/jest --ci --colors --runInBand", "test:jest:ci:coverage": "scripts/use_node scripts/jest --ci --colors --runInBand --coverage", "test:jest_integration": "scripts/use_node scripts/jest_integration", - "test:jest_integration:ci": "scripts/use_node scripts/jest_integration --ci --colors --max-old-space-size=5120", + "test:jest_integration:ci": "scripts/use_node scripts/jest_integration --ci --colors", "test:mocha": "scripts/use_node scripts/mocha", "test:mocha:coverage": "yarn nyc --reporter=text-summary --reporter=lcov --report-dir=./target/opensearch-dashboards-coverage/mocha scripts/use_node scripts/mocha", "test:ftr": "scripts/use_node scripts/functional_tests", @@ -81,7 +81,7 @@ "url": "https://github.com/opensearch-project/opensearch-dashboards.git" }, "resolutions": { - "**/@types/node": "^14.17.32", + "**/@types/node": "~18.7.0", "**/ansi-regex": "^5.0.1", "**/async": "^3.2.3", "**/d3-color": "^3.1.0", @@ -97,7 +97,9 @@ "**/unset-value": "^2.0.1", "**/jest-config": "npm:@amoo-miki/jest-config@27.5.1", "**/jest-jasmine2": "npm:@amoo-miki/jest-jasmine2@27.5.1", - "**/xml2js": "^0.5.0" + "**/set-value": "^4.1.0", + "**/xml2js": "^0.5.0", + "**/yaml": "^2.2.2" }, "workspaces": { "packages": [ @@ -135,7 +137,7 @@ "@hapi/podium": "^4.1.3", "@hapi/vision": "^6.1.0", "@hapi/wreck": "^17.1.0", - "@opensearch-project/opensearch": "^2.1.0", + "@opensearch-project/opensearch": "^2.2.0", "@osd/ace": "1.0.0", "@osd/analytics": "1.0.0", "@osd/apm-config-loader": "1.0.0", @@ -166,9 +168,9 @@ "deep-freeze-strict": "^1.1.1", "del": "^6.1.1", "dns-sync": "^0.2.1", - "elastic-apm-node": "^3.7.0", + "elastic-apm-node": "^3.43.0", "elasticsearch": "^16.7.0", - "http-aws-es": "6.0.0", + "http-aws-es": "npm:@zhongnansu/http-aws-es@6.0.1", "execa": "^4.0.2", "expiry-js": "0.1.7", "fast-deep-equal": "^3.1.1", @@ -176,15 +178,15 @@ "getos": "^3.2.1", "glob": "^7.1.7", "glob-all": "^3.2.1", - "globby": "^8.0.1", + "globby": "^11.1.0", "handlebars": "4.7.7", "hjson": "3.2.1", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^5.0.0", "inline-style": "^2.0.0", "ip-cidr": "^2.1.0", - "joi": "^13.5.2", - "js-yaml": "^3.14.0", + "joi": "^14.3.1", + "js-yaml": "^4.1.0", "json-stable-stringify": "^1.0.1", "json-stringify-safe": "5.0.1", "lodash": "^4.17.21", @@ -199,7 +201,6 @@ "pegjs": "0.10.0", "proxy-from-env": "1.0.0", "query-string": "^6.13.2", - "re2": "1.17.4", "react": "^16.14.0", "react-dom": "^16.12.0", "react-input-range": "^1.3.0", @@ -211,6 +212,7 @@ "rison-node": "1.0.2", "rxjs": "^6.5.5", "semver": "^5.7.0", + "set-value": "^4.1.0", "source-map-support": "^0.5.19", "symbol-observable": "^1.2.0", "tar": "^6.1.11", @@ -288,7 +290,7 @@ "@types/jest": "^27.4.0", "@types/joi": "^13.4.2", "@types/jquery": "^3.3.31", - "@types/js-yaml": "^3.11.1", + "@types/js-yaml": "^4.0.5", "@types/json-stable-stringify": "^1.0.32", "@types/json5": "^0.0.30", "@types/license-checker": "15.0.0", @@ -301,7 +303,7 @@ "@types/mock-fs": "^4.10.0", "@types/moment-timezone": "^0.5.12", "@types/mustache": "^0.8.31", - "@types/node": "^14.17.32", + "@types/node": "~18.7.0", "@types/node-forge": "^1.0.1", "@types/normalize-path": "^3.0.0", "@types/pegjs": "^0.10.1", @@ -342,7 +344,6 @@ "angular-mocks": "^1.8.2", "angular-recursion": "^1.0.5", "angular-route": "^1.8.0", - "angular-sortable-view": "^0.0.17", "archiver": "^5.3.0", "axe-core": "^4.0.2", "babel-eslint": "^10.0.3", @@ -396,7 +397,7 @@ "immer": "^9.0.6", "intl-messageformat-parser": "^1.4.0", "jest": "^27.5.1", - "jest-canvas-mock": "^2.3.1", + "jest-canvas-mock": "^2.5.1", "jest-raw-loader": "^1.0.1", "jimp": "^0.14.0", "jquery": "^3.5.0", @@ -468,7 +469,7 @@ "zlib": "^1.0.5" }, "engines": { - "node": "^14.20.1", + "node": ">=14.20.1 <19", "yarn": "^1.22.10" } } diff --git a/packages/opensearch-eslint-config-opensearch-dashboards/README.md b/packages/opensearch-eslint-config-opensearch-dashboards/README.md index 4fb3f13c8f29..a2f60c437e57 100644 --- a/packages/opensearch-eslint-config-opensearch-dashboards/README.md +++ b/packages/opensearch-eslint-config-opensearch-dashboards/README.md @@ -4,7 +4,7 @@ The eslint config used by the opensearch dashboards team ## Usage -To use this eslint config, just install the peer dependencies and reference it +To use this eslint config, just install the peer dependencies and reference it in your `.eslintrc`: ```javascript @@ -17,8 +17,8 @@ in your `.eslintrc`: ## Optional jest config -If the project uses the [jest test runner](https://facebook.github.io/jest/), -the `@elastic/eslint-config-kibana/jest` config can be extended as well to use +If the project uses the [jest test runner](https://jestjs.io), +the `@elastic/eslint-config-kibana/jest` config can be extended as well to use `eslint-plugin-jest` and add settings specific to it: ```javascript diff --git a/packages/osd-ace/package.json b/packages/osd-ace/package.json index a93467058179..b78ca5e44074 100644 --- a/packages/osd-ace/package.json +++ b/packages/osd-ace/package.json @@ -16,6 +16,6 @@ "@osd/babel-preset": "1.0.0", "raw-loader": "^4.0.2", "typescript": "4.0.2", - "webpack": "^4.41.5" + "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2" } } diff --git a/packages/osd-apm-config-loader/package.json b/packages/osd-apm-config-loader/package.json index c3c0249bd73e..fc06ff025e8a 100644 --- a/packages/osd-apm-config-loader/package.json +++ b/packages/osd-apm-config-loader/package.json @@ -13,7 +13,7 @@ "dependencies": { "@elastic/safer-lodash-set": "0.0.0", "@osd/utils": "1.0.0", - "js-yaml": "^3.14.0", + "js-yaml": "^4.1.0", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/packages/osd-apm-config-loader/src/utils/read_config.ts b/packages/osd-apm-config-loader/src/utils/read_config.ts index 806a1ad2e92a..7076060cb04d 100644 --- a/packages/osd-apm-config-loader/src/utils/read_config.ts +++ b/packages/osd-apm-config-loader/src/utils/read_config.ts @@ -29,13 +29,13 @@ */ import { readFileSync } from 'fs'; -import { safeLoad } from 'js-yaml'; +import { load } from 'js-yaml'; import { set } from '@elastic/safer-lodash-set'; import { isPlainObject } from 'lodash'; import { ensureDeepObject } from './ensure_deep_object'; -const readYaml = (path: string) => safeLoad(readFileSync(path, 'utf8')); +const readYaml = (path: string) => load(readFileSync(path, 'utf8')); function replaceEnvVarRefs(val: string) { return val.replace(/\$\{(\w+)\}/g, (match, envVarName) => { diff --git a/packages/osd-config-schema/package.json b/packages/osd-config-schema/package.json index 52471e29527c..c88afe609e1f 100644 --- a/packages/osd-config-schema/package.json +++ b/packages/osd-config-schema/package.json @@ -16,7 +16,7 @@ }, "peerDependencies": { "lodash": "^4.17.21", - "joi": "^13.5.2", + "joi": "^14.3.1", "moment": "^2.24.0", "type-detect": "^4.0.8" } diff --git a/packages/osd-config-schema/src/errors/__snapshots__/schema_error.test.ts.snap b/packages/osd-config-schema/src/errors/__snapshots__/schema_error.test.ts.snap index 38f81a0cc62b..1f5e056ba69a 100644 --- a/packages/osd-config-schema/src/errors/__snapshots__/schema_error.test.ts.snap +++ b/packages/osd-config-schema/src/errors/__snapshots__/schema_error.test.ts.snap @@ -1,8 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`includes stack 1`] = ` -"Error: test - at new SchemaError (packages/osd-config-schema/src/errors/schema_error.ts:35:5) - at Object.<anonymous> (packages/osd-config-schema/src/errors/schema_error.test.ts:59:11) +"SchemaError: test + at Object.<anonymous> (packages/osd-config-schema/src/errors/schema_error.test.ts:67:11) at new Promise (<anonymous>)" `; diff --git a/packages/osd-config-schema/src/errors/schema_error.test.ts b/packages/osd-config-schema/src/errors/schema_error.test.ts index c134c888d510..c00add75719b 100644 --- a/packages/osd-config-schema/src/errors/schema_error.test.ts +++ b/packages/osd-config-schema/src/errors/schema_error.test.ts @@ -33,11 +33,14 @@ import { SchemaError } from '.'; import { standardize, getRepoRoot } from '@osd/cross-platform'; +// ToDo: Remove this logic when Node 14 support is removed +const isNode14 = process.version.startsWith('v14.'); + /** * Make all paths in stacktrace relative. */ -export const cleanStack = (stack: string) => - stack +export const cleanStack = (stack: string) => { + const result = stack .split('\n') .filter((line) => !line.includes('node_modules' + sep) && !line.includes('internal/')) .map((line) => { @@ -54,6 +57,11 @@ export const cleanStack = (stack: string) => }) .join('\n'); + return isNode14 + ? result.replace(/Error:\s([^\n]+)\n\s*at new ([^ ]+) [^\n]*\n/, '$2: $1\n') + : result; +}; + it('includes stack', () => { try { throw new SchemaError('test'); diff --git a/packages/osd-config-schema/src/errors/schema_error.ts b/packages/osd-config-schema/src/errors/schema_error.ts index fd67de08af47..a47453813fcd 100644 --- a/packages/osd-config-schema/src/errors/schema_error.ts +++ b/packages/osd-config-schema/src/errors/schema_error.ts @@ -34,6 +34,7 @@ export class SchemaError extends Error { constructor(message: string, cause?: Error) { super(message); this.cause = cause; + this.name = this.constructor.name; // Set the prototype explicitly, see: // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work diff --git a/packages/osd-config/package.json b/packages/osd-config/package.json index 7d2f68abc8d8..f6d363c9c386 100644 --- a/packages/osd-config/package.json +++ b/packages/osd-config/package.json @@ -14,7 +14,7 @@ "@osd/config-schema": "1.0.0", "@osd/logging": "1.0.0", "@osd/std": "1.0.0", - "js-yaml": "^3.14.0", + "js-yaml": "^4.1.0", "load-json-file": "^6.2.0", "lodash": "^4.17.21", "moment": "^2.24.0", diff --git a/packages/osd-config/src/config_service.test.ts b/packages/osd-config/src/config_service.test.ts index 0ae49dab6015..0f634537be7e 100644 --- a/packages/osd-config/src/config_service.test.ts +++ b/packages/osd-config/src/config_service.test.ts @@ -94,10 +94,10 @@ test('throws if config at path does not match schema', async () => { ); await expect(valuesReceived).toMatchInlineSnapshot(` - Array [ - [Error: [config validation of [key]]: expected value of type [string] but got [number]], - ] - `); + Array [ + [ValidationError: [config validation of [key]]: expected value of type [string] but got [number]], + ] + `); }); test('re-validate config when updated', async () => { @@ -120,11 +120,11 @@ test('re-validate config when updated', async () => { rawConfig$.next({ key: 123 }); await expect(valuesReceived).toMatchInlineSnapshot(` - Array [ - "value", - [Error: [config validation of [key]]: expected value of type [string] but got [number]], - ] - `); + Array [ + "value", + [ValidationError: [config validation of [key]]: expected value of type [string] but got [number]], + ] + `); }); test("returns undefined if fetching optional config at a path that doesn't exist", async () => { diff --git a/packages/osd-config/src/raw/read_config.ts b/packages/osd-config/src/raw/read_config.ts index 0c4a7c2330de..ad7820381eee 100644 --- a/packages/osd-config/src/raw/read_config.ts +++ b/packages/osd-config/src/raw/read_config.ts @@ -29,13 +29,13 @@ */ import { readFileSync } from 'fs'; -import { safeLoad } from 'js-yaml'; +import { load } from 'js-yaml'; import { set } from '@elastic/safer-lodash-set'; import { isPlainObject } from 'lodash'; import { ensureDeepObject } from './ensure_deep_object'; -const readYaml = (path: string) => safeLoad(readFileSync(path, 'utf8')); +const readYaml = (path: string) => load(readFileSync(path, 'utf8')); function replaceEnvVarRefs(val: string) { return val.replace(/\$\{(\w+)\}/g, (match, envVarName) => { diff --git a/packages/osd-cross-platform/src/path.test.ts b/packages/osd-cross-platform/src/path.test.ts index ed405298ad13..3e338a426fda 100644 --- a/packages/osd-cross-platform/src/path.test.ts +++ b/packages/osd-cross-platform/src/path.test.ts @@ -5,7 +5,7 @@ import path from 'path'; import fs from 'fs'; -import { access, rmdir, mkdir, writeFile, symlink } from 'fs/promises'; +import { access, rm, mkdir, writeFile, symlink } from 'fs/promises'; import { resolveToFullNameSync, @@ -43,9 +43,9 @@ describe('Cross Platform', () => { try { // If leftover artifacts were found, get rid of them await access(tmpTestFolder); - await rmdir(tmpTestFolder, { recursive: true }); + await rm(tmpTestFolder, { recursive: true }); } catch (ex) { - // Do nothing; if `rmdir` failed, let the `mkdir` below throw the error + // Do nothing; if `rm` failed, let the `mkdir` below throw the error } await mkdir(tmpTestFolder); @@ -60,7 +60,7 @@ describe('Cross Platform', () => { afterAll(async () => { try { - await rmdir(tmpTestFolder, { recursive: true }); + await rm(tmpTestFolder, { recursive: true }); } catch (ex) { // Do nothing } diff --git a/packages/osd-dev-utils/package.json b/packages/osd-dev-utils/package.json index ea433fa5e101..6a7a4fd9d647 100644 --- a/packages/osd-dev-utils/package.json +++ b/packages/osd-dev-utils/package.json @@ -22,7 +22,7 @@ "execa": "^4.0.2", "exit-hook": "^2.2.0", "getopts": "^2.2.5", - "globby": "^8.0.1", + "globby": "^11.1.0", "load-json-file": "^6.2.0", "markdown-it": "^12.3.2", "moment": "^2.24.0", diff --git a/packages/osd-dev-utils/src/proc_runner/proc.ts b/packages/osd-dev-utils/src/proc_runner/proc.ts index 1de472df40b2..c5e905204915 100644 --- a/packages/osd-dev-utils/src/proc_runner/proc.ts +++ b/packages/osd-dev-utils/src/proc_runner/proc.ts @@ -153,7 +153,7 @@ export function startProc(name: string, options: ProcOptions, log: ToolingLog) { await withTimeout( async () => { log.debug(`Sending "${signal}" to proc "${name}"`); - await treeKillAsync(childProcess.pid, signal); + await treeKillAsync(childProcess.pid!, signal); await outcomePromise; }, STOP_TIMEOUT, @@ -161,7 +161,7 @@ export function startProc(name: string, options: ProcOptions, log: ToolingLog) { log.warning( `Proc "${name}" was sent "${signal}" didn't emit the "exit" or "error" events after ${STOP_TIMEOUT} ms, sending SIGKILL` ); - await treeKillAsync(childProcess.pid, 'SIGKILL'); + await treeKillAsync(childProcess.pid!, 'SIGKILL'); } ); diff --git a/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts b/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts index 939b6e9924df..b7c6f8fe9139 100644 --- a/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts +++ b/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts @@ -37,6 +37,10 @@ export function createAbsolutePathSerializer( ) { const rootPaths = Array.isArray(rootPath) ? rootPath : [rootPath]; + if (process.platform === 'win32') { + rootPaths.push(...rootPaths.map((name) => name.replace(/\\/g, '/'))); + } + return { test: (value: any) => typeof value === 'string' && rootPaths.some((path) => value.startsWith(path)), diff --git a/packages/osd-dev-utils/src/serializers/index.ts b/packages/osd-dev-utils/src/serializers/index.ts index 4ea72efe3f53..2755a5a79147 100644 --- a/packages/osd-dev-utils/src/serializers/index.ts +++ b/packages/osd-dev-utils/src/serializers/index.ts @@ -33,3 +33,4 @@ export * from './strip_ansi_serializer'; export * from './recursive_serializer'; export * from './any_instance_serizlizer'; export * from './replace_serializer'; +export * from './strip_promises_serizlizer'; diff --git a/packages/osd-dev-utils/src/serializers/strip_promises_serizlizer.ts b/packages/osd-dev-utils/src/serializers/strip_promises_serizlizer.ts new file mode 100644 index 000000000000..eda8422c77c4 --- /dev/null +++ b/packages/osd-dev-utils/src/serializers/strip_promises_serizlizer.ts @@ -0,0 +1,9 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const createStripPromisesSerializer = () => ({ + serialize: (value: string) => value.replace(/("value": Promise) \{[^}]+}/gm, '$1 {}'), + test: (value: any) => typeof value === 'string', +}); diff --git a/packages/osd-dev-utils/src/simple_opensearch_dashboards_platform_plugin_discovery.ts b/packages/osd-dev-utils/src/simple_opensearch_dashboards_platform_plugin_discovery.ts index ef6c5913d9b4..b84579a36d3e 100644 --- a/packages/osd-dev-utils/src/simple_opensearch_dashboards_platform_plugin_discovery.ts +++ b/packages/osd-dev-utils/src/simple_opensearch_dashboards_platform_plugin_discovery.ts @@ -28,7 +28,8 @@ * under the License. */ -import Path from 'path'; +import { resolve } from 'path'; +import { standardize } from '@osd/cross-platform'; import globby from 'globby'; @@ -47,24 +48,23 @@ export function simpleOpenSearchDashboardsPlatformPluginDiscovery( ...scanDirs.reduce( (acc: string[], dir) => [ ...acc, - Path.resolve(dir, '*/opensearch_dashboards.json'), - Path.resolve(dir, '*/*/opensearch_dashboards.json'), - Path.resolve(dir, '*/*/*/opensearch_dashboards.json'), - Path.resolve(dir, '*/*/*/*/opensearch_dashboards.json'), - Path.resolve(dir, '*/*/*/*/*/opensearch_dashboards.json'), + resolve(dir, '*/opensearch_dashboards.json'), + resolve(dir, '*/*/opensearch_dashboards.json'), + resolve(dir, '*/*/*/opensearch_dashboards.json'), + resolve(dir, '*/*/*/*/opensearch_dashboards.json'), + resolve(dir, '*/*/*/*/*/opensearch_dashboards.json'), ], [] ), - ...pluginPaths.map((path) => Path.resolve(path, `opensearch_dashboards.json`)), + ...pluginPaths.map((path) => resolve(path, `opensearch_dashboards.json`)), ]) ); - const manifestPaths = globby.sync(patterns, { absolute: true }).map((path) => - // absolute paths returned from globby are using normalize or - // something so the path separators are `/` even on windows, - // Path.resolve solves this - Path.resolve(path) - ); + const standardizedPatterns = patterns.map((pattern) => standardize(pattern)); + + const manifestPaths = globby + .sync(standardizedPatterns, { absolute: true }) + .map((path) => standardize(resolve(path))); return manifestPaths.map(parseOpenSearchDashboardsPlatformPlugin); } diff --git a/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json b/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json index 1594e45e50b5..1112cb1ed223 100755 --- a/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json +++ b/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json @@ -19,6 +19,6 @@ "glob-all": "^3.2.1", "lru-cache": "^4.1.5", "resolve": "^1.7.1", - "webpack": "^4.41.5" + "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2" } } diff --git a/packages/osd-interpreter/package.json b/packages/osd-interpreter/package.json index 77ec29c839dd..6d11eb3bc1ee 100644 --- a/packages/osd-interpreter/package.json +++ b/packages/osd-interpreter/package.json @@ -27,11 +27,11 @@ "del": "^6.1.1", "getopts": "^2.2.5", "pegjs": "0.10.0", - "sass-loader": "^10.2.0", + "sass-loader": "^10.4.1", "style-loader": "^1.1.3", "supports-color": "^7.0.0", "url-loader": "^2.2.0", - "webpack": "^4.41.5", + "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2", "webpack-cli": "^4.9.2" } } diff --git a/packages/osd-monaco/package.json b/packages/osd-monaco/package.json index a3f973d11fd0..ae9cfaddc53a 100644 --- a/packages/osd-monaco/package.json +++ b/packages/osd-monaco/package.json @@ -13,6 +13,7 @@ "monaco-editor": "~0.17.0" }, "devDependencies": { + "@node-rs/xxhash": "^1.3.0", "@osd/babel-preset": "1.0.0", "@osd/dev-utils": "1.0.0", "babel-loader": "^8.2.3", @@ -21,7 +22,7 @@ "raw-loader": "^4.0.2", "supports-color": "^7.0.0", "typescript": "4.0.2", - "webpack": "^4.41.5", + "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2", "webpack-cli": "^4.9.2" } } diff --git a/packages/osd-monaco/webpack.config.js b/packages/osd-monaco/webpack.config.js index 1e3f108b95e5..b111fcb2608d 100644 --- a/packages/osd-monaco/webpack.config.js +++ b/packages/osd-monaco/webpack.config.js @@ -36,6 +36,7 @@ const createLangWorkerConfig = (lang) => ({ output: { path: path.resolve(__dirname, 'target/public'), filename: `${lang}.editor.worker.js`, + hashFunction: 'Xxh64', }, resolve: { modules: ['node_modules'], diff --git a/packages/osd-opensearch/package.json b/packages/osd-opensearch/package.json index 02fcddb36e6f..44404a9ae5a3 100644 --- a/packages/osd-opensearch/package.json +++ b/packages/osd-opensearch/package.json @@ -12,7 +12,7 @@ "osd:watch": "../../scripts/use_node scripts/build --watch" }, "dependencies": { - "@opensearch-project/opensearch": "^2.1.0", + "@opensearch-project/opensearch": "^2.2.0", "@osd/dev-utils": "1.0.0", "abort-controller": "^3.0.0", "chalk": "^4.1.0", diff --git a/packages/osd-optimizer/limits.yml b/packages/osd-optimizer/limits.yml index d39f9316c80d..516ebae80abf 100644 --- a/packages/osd-optimizer/limits.yml +++ b/packages/osd-optimizer/limits.yml @@ -74,7 +74,6 @@ pageLoadAssetSize: telemetry: 91832 telemetryManagementSection: 52443 tileMap: 65337 - timeline: 29920 transform: 41151 triggersActionsUi: 170145 uiActions: 95074 diff --git a/packages/osd-optimizer/package.json b/packages/osd-optimizer/package.json index 2aec5681ba91..27047c7b4ef5 100644 --- a/packages/osd-optimizer/package.json +++ b/packages/osd-optimizer/package.json @@ -19,22 +19,21 @@ "@osd/ui-shared-deps": "1.0.0", "autoprefixer": "^10.4.1", "clean-webpack-plugin": "^3.0.0", - "compression-webpack-plugin": "^4.0.0", + "compression-webpack-plugin": "npm:@amoo-miki/compression-webpack-plugin@4.0.1-rc.1", "cpy": "^8.0.0", "core-js": "^3.6.5", "dedent": "^0.7.0", "del": "^6.1.1", "execa": "^4.0.2", - "fibers": "^5.0.3", "jest-diff": "^27.5.1", - "js-yaml": "^3.14.0", + "js-yaml": "^4.1.0", "json-stable-stringify": "^1.0.1", - "lmdb-store": "^1.6.11", + "lmdb": "^2.8.0", "normalize-path": "^3.0.0", "pirates": "^4.0.1", "postcss": "^8.4.5", "rxjs": "^6.5.5", - "sass": "~1.26.11", + "node-sass": "^8.0.0", "source-map-support": "^0.5.19", "terser-webpack-plugin": "^2.1.2", "tinymath": "1.2.1", @@ -42,8 +41,9 @@ "webpack-merge": "^4.2.2" }, "devDependencies": { + "@node-rs/xxhash": "^1.3.0", "@types/babel__core": "^7.1.17", - "@types/compression-webpack-plugin": "^2.0.2", + "@types/compression-webpack-plugin": "^6.0.6", "@types/loader-utils": "^1.1.3", "@types/source-map-support": "^0.5.3", "@types/watchpack": "^1.1.6", @@ -54,10 +54,10 @@ "loader-utils": "^2.0.4", "postcss-loader": "^4.2.0", "raw-loader": "^4.0.2", - "sass-loader": "^10.2.0", + "sass-loader": "^10.4.1", "style-loader": "^1.1.3", "url-loader": "^2.2.0", "val-loader": "^2.1.2", - "webpack": "^4.41.5" + "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2" } } diff --git a/packages/osd-optimizer/src/__mocks__/lmdb.ts b/packages/osd-optimizer/src/__mocks__/lmdb.ts new file mode 100644 index 000000000000..d0bfe969d207 --- /dev/null +++ b/packages/osd-optimizer/src/__mocks__/lmdb.ts @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +jest.mock('lmdb', () => { + const mockedLmdb = { + open: jest.fn(() => ({ + openDB: jest.fn(() => ({ + get: jest.fn(), + putSync: jest.fn(), + remove: jest.fn(), + })), + })), + }; + return mockedLmdb; +}); diff --git a/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index aaa485172c5f..63b7e44fea07 100644 --- a/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -74,7 +74,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\\";module.exports=function(cssWithMappingToString){var list=[];list.toString=function toString(){return this.map((function(item){var content=cssWithMappingToString(item);if(item[2]){return\\"@media \\".concat(item[2],\\" {\\").concat(content,\\"}\\")}return content})).join(\\"\\")};list.i=function(modules,mediaQuery,dedupe){if(typeof modules===\\"string\\"){modules=[[null,modules,\\"\\"]]}var alreadyImportedModules={};if(dedupe){for(var i=0;i<this.length;i++){var id=this[i][0];if(id!=null){alreadyImportedModules[id]=true}}}for(var _i=0;_i<modules.length;_i++){var item=[].concat(modules[_i]);if(dedupe&&alreadyImportedModules[item[0]]){continue}if(mediaQuery){if(!item[2]){item[2]=mediaQuery}else{item[2]=\\"\\".concat(mediaQuery,\\" and \\").concat(item[2])}}list.push(item)}};return list}},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<stylesInDom.length;i++){if(stylesInDom[i].identifier===identifier){result=i;break}}return result}function modulesToDom(list,options){var idCountMap={};var identifiers=[];for(var i=0;i<list.length;i++){var item=list[i];var id=options.base?item[0]+options.base:item[0];var count=idCountMap[id]||0;var identifier=\\"\\".concat(id,\\" \\").concat(count);idCountMap[id]=count+1;var index=getIndexByIdentifier(identifier);var obj={css:item[1],media:item[2],sourceMap:item[3]};if(index!==-1){stylesInDom[index].references++;stylesInDom[index].updater(obj)}else{stylesInDom.push({identifier:identifier,updater:addStyle(obj,options),references:1})}identifiers.push(identifier)}return identifiers}function insertStyleElement(options){var style=document.createElement(\\"style\\");var attributes=options.attributes||{};if(typeof attributes.nonce===\\"undefined\\"){var nonce=true?__webpack_require__.nc:undefined;if(nonce){attributes.nonce=nonce}}Object.keys(attributes).forEach((function(key){style.setAttribute(key,attributes[key])}));if(typeof options.insert===\\"function\\"){options.insert(style)}else{var target=getTarget(options.insert||\\"head\\");if(!target){throw new Error(\\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\\")}target.appendChild(style)}return style}function removeStyleElement(style){if(style.parentNode===null){return false}style.parentNode.removeChild(style)}var replaceText=function replaceText(){var textStore=[];return function replace(index,replacement){textStore[index]=replacement;return textStore.filter(Boolean).join(\\"\\\\n\\")}}();function applyToSingletonTag(style,index,remove,obj){var css=remove?\\"\\":obj.media?\\"@media \\".concat(obj.media,\\" {\\").concat(obj.css,\\"}\\"):obj.css;if(style.styleSheet){style.styleSheet.cssText=replaceText(index,css)}else{var cssNode=document.createTextNode(css);var childNodes=style.childNodes;if(childNodes[index]){style.removeChild(childNodes[index])}if(childNodes.length){style.insertBefore(cssNode,childNodes[index])}else{style.appendChild(cssNode)}}}function applyToTag(style,options,obj){var css=obj.css;var media=obj.media;var sourceMap=obj.sourceMap;if(media){style.setAttribute(\\"media\\",media)}else{style.removeAttribute(\\"media\\")}if(sourceMap&&typeof btoa!==\\"undefined\\"){css+=\\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))),\\" */\\")}if(style.styleSheet){style.styleSheet.cssText=css}else{while(style.firstChild){style.removeChild(style.firstChild)}style.appendChild(document.createTextNode(css))}}var singleton=null;var singletonCounter=0;function addStyle(obj,options){var style;var update;var remove;if(options.singleton){var styleIndex=singletonCounter++;style=singleton||(singleton=insertStyleElement(options));update=applyToSingletonTag.bind(null,style,styleIndex,false);remove=applyToSingletonTag.bind(null,style,styleIndex,true)}else{style=insertStyleElement(options);update=applyToTag.bind(null,style,options);remove=function remove(){removeStyleElement(style)}}update(obj);return function updateStyle(newObj){if(newObj){if(newObj.css===obj.css&&newObj.media===obj.media&&newObj.sourceMap===obj.sourceMap){return}update(obj=newObj)}else{remove()}}}module.exports=function(list,options){options=options||{};if(!options.singleton&&typeof options.singleton!==\\"boolean\\"){options.singleton=isOldIE()}list=list||[];var lastIdentifiers=modulesToDom(list,options);return function update(newList){newList=newList||[];if(Object.prototype.toString.call(newList)!==\\"[object Array]\\"){return}for(var i=0;i<lastIdentifiers.length;i++){var identifier=lastIdentifiers[i];var index=getIndexByIdentifier(identifier);stylesInDom[index].references--}var newLastIdentifiers=modulesToDom(newList,options);for(var _i=0;_i<lastIdentifiers.length;_i++){var _identifier=lastIdentifiers[_i];var _index=getIndexByIdentifier(_identifier);if(stylesInDom[_index].references===0){stylesInDom[_index].updater();stylesInDom.splice(_index,1)}}lastIdentifiers=newLastIdentifiers}}},function(module,__webpack_exports__,__webpack_require__){__webpack_require__.r(__webpack_exports__);var ns=__osdBundles__.get(\\"plugin/foo/public\\");Object.defineProperties(__webpack_exports__,Object.getOwnPropertyDescriptors(ns))},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_bar_osd_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(4);var _node_modules_val_loader_dist_cjs_js_key_bar_osd_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_bar_osd_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__osdBundles__.define(\\"plugin/bar/public\\",__webpack_require__,23)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__osdPublicPath__[\\"bar\\"]},function(module,exports,__webpack_require__){switch(window.__osdThemeTag__){case\\"v7dark\\":return __webpack_require__(6);case\\"v7light\\":return __webpack_require__(8);case\\"v8dark\\":return __webpack_require__(10);case\\"v8light\\":return __webpack_require__(12)}},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(7);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:#639}body{width:10}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(9);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:#639}body{width:11}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(11);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:#639}body{width:12}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(13);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:#639}body{width:13}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){switch(window.__osdThemeTag__){case\\"v7dark\\":return __webpack_require__(15);case\\"v7light\\":return __webpack_require__(17);case\\"v8dark\\":return __webpack_require__(19);case\\"v8light\\":return __webpack_require__(21)}},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(16);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(18);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(20);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(22);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"barLibFn\\",(function(){return barLibFn}));__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return public_[\\"fooLibFn\\"]}));var styles=__webpack_require__(5);var public_0=__webpack_require__(14);var public_=__webpack_require__(2);function barLibFn(){return\\"bar\\"}}]);"`; +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\\";module.exports=function(cssWithMappingToString){var list=[];list.toString=function toString(){return this.map((function(item){var content=cssWithMappingToString(item);if(item[2]){return\\"@media \\".concat(item[2],\\" {\\").concat(content,\\"}\\")}return content})).join(\\"\\")};list.i=function(modules,mediaQuery,dedupe){if(typeof modules===\\"string\\"){modules=[[null,modules,\\"\\"]]}var alreadyImportedModules={};if(dedupe){for(var i=0;i<this.length;i++){var id=this[i][0];if(id!=null){alreadyImportedModules[id]=true}}}for(var _i=0;_i<modules.length;_i++){var item=[].concat(modules[_i]);if(dedupe&&alreadyImportedModules[item[0]]){continue}if(mediaQuery){if(!item[2]){item[2]=mediaQuery}else{item[2]=\\"\\".concat(mediaQuery,\\" and \\").concat(item[2])}}list.push(item)}};return list}},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<stylesInDom.length;i++){if(stylesInDom[i].identifier===identifier){result=i;break}}return result}function modulesToDom(list,options){var idCountMap={};var identifiers=[];for(var i=0;i<list.length;i++){var item=list[i];var id=options.base?item[0]+options.base:item[0];var count=idCountMap[id]||0;var identifier=\\"\\".concat(id,\\" \\").concat(count);idCountMap[id]=count+1;var index=getIndexByIdentifier(identifier);var obj={css:item[1],media:item[2],sourceMap:item[3]};if(index!==-1){stylesInDom[index].references++;stylesInDom[index].updater(obj)}else{stylesInDom.push({identifier:identifier,updater:addStyle(obj,options),references:1})}identifiers.push(identifier)}return identifiers}function insertStyleElement(options){var style=document.createElement(\\"style\\");var attributes=options.attributes||{};if(typeof attributes.nonce===\\"undefined\\"){var nonce=true?__webpack_require__.nc:undefined;if(nonce){attributes.nonce=nonce}}Object.keys(attributes).forEach((function(key){style.setAttribute(key,attributes[key])}));if(typeof options.insert===\\"function\\"){options.insert(style)}else{var target=getTarget(options.insert||\\"head\\");if(!target){throw new Error(\\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\\")}target.appendChild(style)}return style}function removeStyleElement(style){if(style.parentNode===null){return false}style.parentNode.removeChild(style)}var replaceText=function replaceText(){var textStore=[];return function replace(index,replacement){textStore[index]=replacement;return textStore.filter(Boolean).join(\\"\\\\n\\")}}();function applyToSingletonTag(style,index,remove,obj){var css=remove?\\"\\":obj.media?\\"@media \\".concat(obj.media,\\" {\\").concat(obj.css,\\"}\\"):obj.css;if(style.styleSheet){style.styleSheet.cssText=replaceText(index,css)}else{var cssNode=document.createTextNode(css);var childNodes=style.childNodes;if(childNodes[index]){style.removeChild(childNodes[index])}if(childNodes.length){style.insertBefore(cssNode,childNodes[index])}else{style.appendChild(cssNode)}}}function applyToTag(style,options,obj){var css=obj.css;var media=obj.media;var sourceMap=obj.sourceMap;if(media){style.setAttribute(\\"media\\",media)}else{style.removeAttribute(\\"media\\")}if(sourceMap&&typeof btoa!==\\"undefined\\"){css+=\\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))),\\" */\\")}if(style.styleSheet){style.styleSheet.cssText=css}else{while(style.firstChild){style.removeChild(style.firstChild)}style.appendChild(document.createTextNode(css))}}var singleton=null;var singletonCounter=0;function addStyle(obj,options){var style;var update;var remove;if(options.singleton){var styleIndex=singletonCounter++;style=singleton||(singleton=insertStyleElement(options));update=applyToSingletonTag.bind(null,style,styleIndex,false);remove=applyToSingletonTag.bind(null,style,styleIndex,true)}else{style=insertStyleElement(options);update=applyToTag.bind(null,style,options);remove=function remove(){removeStyleElement(style)}}update(obj);return function updateStyle(newObj){if(newObj){if(newObj.css===obj.css&&newObj.media===obj.media&&newObj.sourceMap===obj.sourceMap){return}update(obj=newObj)}else{remove()}}}module.exports=function(list,options){options=options||{};if(!options.singleton&&typeof options.singleton!==\\"boolean\\"){options.singleton=isOldIE()}list=list||[];var lastIdentifiers=modulesToDom(list,options);return function update(newList){newList=newList||[];if(Object.prototype.toString.call(newList)!==\\"[object Array]\\"){return}for(var i=0;i<lastIdentifiers.length;i++){var identifier=lastIdentifiers[i];var index=getIndexByIdentifier(identifier);stylesInDom[index].references--}var newLastIdentifiers=modulesToDom(newList,options);for(var _i=0;_i<lastIdentifiers.length;_i++){var _identifier=lastIdentifiers[_i];var _index=getIndexByIdentifier(_identifier);if(stylesInDom[_index].references===0){stylesInDom[_index].updater();stylesInDom.splice(_index,1)}}lastIdentifiers=newLastIdentifiers}}},function(module,__webpack_exports__,__webpack_require__){__webpack_require__.r(__webpack_exports__);var ns=__osdBundles__.get(\\"plugin/foo/public\\");Object.defineProperties(__webpack_exports__,Object.getOwnPropertyDescriptors(ns))},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_bar_osd_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(4);var _node_modules_val_loader_dist_cjs_js_key_bar_osd_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_bar_osd_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__osdBundles__.define(\\"plugin/bar/public\\",__webpack_require__,23)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__osdPublicPath__[\\"bar\\"]},function(module,exports,__webpack_require__){switch(window.__osdThemeTag__){case\\"v7dark\\":return __webpack_require__(6);case\\"v7light\\":return __webpack_require__(8);case\\"v8dark\\":return __webpack_require__(10);case\\"v8light\\":return __webpack_require__(12)}},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(7);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:rebeccapurple}body{width:10}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(9);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:rebeccapurple}body{width:11}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(11);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:rebeccapurple}body{width:12}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(13);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"p{background-color:rebeccapurple}body{width:13}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){switch(window.__osdThemeTag__){case\\"v7dark\\":return __webpack_require__(15);case\\"v7light\\":return __webpack_require__(17);case\\"v8dark\\":return __webpack_require__(19);case\\"v8light\\":return __webpack_require__(21)}},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(16);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(18);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(20);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,exports,__webpack_require__){var api=__webpack_require__(1);var content=__webpack_require__(22);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0);var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);var ___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i){return i[1]}));___CSS_LOADER_EXPORT___.push([module.i,\\"body{color:green}\\\\n\\",\\"\\"]);__webpack_exports__[\\"default\\"]=___CSS_LOADER_EXPORT___},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"barLibFn\\",(function(){return barLibFn}));__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return public_[\\"fooLibFn\\"]}));var styles=__webpack_require__(5);var public_0=__webpack_require__(14);var public_=__webpack_require__(2);function barLibFn(){return\\"bar\\"}}]);"`; exports[`prepares assets for distribution: foo async bundle 1`] = `"(window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[]).push([[1],{3:function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"foo\\",(function(){return foo}));function foo(){}}}]);"`; diff --git a/packages/osd-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/osd-optimizer/src/integration_tests/basic_optimization.test.ts index 1cdfe1f3a041..8c1b1e820e56 100644 --- a/packages/osd-optimizer/src/integration_tests/basic_optimization.test.ts +++ b/packages/osd-optimizer/src/integration_tests/basic_optimization.test.ts @@ -36,8 +36,7 @@ import { inspect } from 'util'; import cpy from 'cpy'; import del from 'del'; import { tap, filter } from 'rxjs/operators'; -import { REPO_ROOT } from '@osd/utils'; -import { ToolingLog } from '@osd/dev-utils'; +import { createAbsolutePathSerializer, ToolingLog } from '@osd/dev-utils'; import { runOptimizer, OptimizerConfig, @@ -48,14 +47,14 @@ import { import { allValuesFrom } from '../common'; +import '../__mocks__/lmdb'; + const TMP_DIR = Path.resolve(__dirname, '../__fixtures__/__tmp__'); const MOCK_REPO_SRC = Path.resolve(__dirname, '../__fixtures__/mock_repo'); const MOCK_REPO_DIR = Path.resolve(TMP_DIR, 'mock_repo'); -expect.addSnapshotSerializer({ - serialize: (value: string) => value.split(REPO_ROOT).join('<absolute path>').replace(/\\/g, '/'), - test: (value: any) => typeof value === 'string' && value.includes(REPO_ROOT), -}); +const absolutePathSerializer = createAbsolutePathSerializer(); +expect.addSnapshotSerializer(absolutePathSerializer); const log = new ToolingLog({ level: 'error', @@ -159,15 +158,16 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { expect(foo).toBeTruthy(); foo.cache.refresh(); expect(foo.cache.getModuleCount()).toBe(6); - expect(foo.cache.getReferencedFiles()).toMatchInlineSnapshot(` + expect(foo.cache.getReferencedFiles()?.map(absolutePathSerializer.serialize).sort()) + .toMatchInlineSnapshot(` Array [ - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/opensearch_dashboards.json, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/index.ts, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/lib.ts, - <absolute path>/packages/osd-optimizer/target/worker/entry_point_creator.js, - <absolute path>/packages/osd-ui-shared-deps/public_path_module_creator.js, + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/opensearch_dashboards.json", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/index.ts", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/lib.ts", + "<absolute path>/packages/osd-optimizer/target/worker/entry_point_creator.js", + "<absolute path>/packages/osd-ui-shared-deps/public_path_module_creator.js", ] `); @@ -179,21 +179,22 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { 17 ); - expect(bar.cache.getReferencedFiles()).toMatchInlineSnapshot(` + expect(bar.cache.getReferencedFiles()?.map(absolutePathSerializer.serialize).sort()) + .toMatchInlineSnapshot(` Array [ - <absolute path>/node_modules/css-loader/package.json, - <absolute path>/node_modules/style-loader/package.json, - <absolute path>/packages/osd-optimizer/postcss.config.js, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/opensearch_dashboards.json, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.scss, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.ts, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/_other_styles.scss, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/styles.scss, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/lib.ts, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/src/core/public/core_app/styles/_globals_v7dark.scss, - <absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/src/core/public/core_app/styles/_globals_v7light.scss, - <absolute path>/packages/osd-optimizer/target/worker/entry_point_creator.js, - <absolute path>/packages/osd-ui-shared-deps/public_path_module_creator.js, + "<absolute path>/node_modules/css-loader/package.json", + "<absolute path>/node_modules/style-loader/package.json", + "<absolute path>/packages/osd-optimizer/postcss.config.js", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/opensearch_dashboards.json", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.scss", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.ts", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/_other_styles.scss", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/styles.scss", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/lib.ts", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/src/core/public/core_app/styles/_globals_v7dark.scss", + "<absolute path>/packages/osd-optimizer/src/__fixtures__/__tmp__/mock_repo/src/core/public/core_app/styles/_globals_v7light.scss", + "<absolute path>/packages/osd-optimizer/target/worker/entry_point_creator.js", + "<absolute path>/packages/osd-ui-shared-deps/public_path_module_creator.js", ] `); }); @@ -256,7 +257,6 @@ const expectFileMatchesSnapshotWithCompression = (filePath: string, snapshotLabe // Verify the brotli variant matches expect( - // @ts-expect-error @types/node is missing the brotli functions Zlib.brotliDecompressSync( Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, `${filePath}.br`)) ).toString() diff --git a/packages/osd-optimizer/src/limits.ts b/packages/osd-optimizer/src/limits.ts index 86b186930275..d81137c3a9a4 100644 --- a/packages/osd-optimizer/src/limits.ts +++ b/packages/osd-optimizer/src/limits.ts @@ -51,7 +51,7 @@ export function readLimits(): Limits { } } - return yaml ? (Yaml.safeLoad(yaml) as any) : {}; + return yaml ? (Yaml.load(yaml) as any) : {}; } export function validateLimitsForAllBundles(log: ToolingLog, config: OptimizerConfig) { @@ -109,6 +109,6 @@ export function updateBundleLimits(log: ToolingLog, config: OptimizerConfig) { pageLoadAssetSize, }; - Fs.writeFileSync(LIMITS_PATH, Yaml.safeDump(newLimits)); + Fs.writeFileSync(LIMITS_PATH, Yaml.dump(newLimits)); log.success(`wrote updated limits to ${LIMITS_PATH}`); } diff --git a/packages/osd-optimizer/src/node/cache.ts b/packages/osd-optimizer/src/node/cache.ts index e60038102fb5..a81c656267bf 100644 --- a/packages/osd-optimizer/src/node/cache.ts +++ b/packages/osd-optimizer/src/node/cache.ts @@ -32,7 +32,7 @@ import Path from 'path'; import { Writable } from 'stream'; import chalk from 'chalk'; -import * as LmdbStore from 'lmdb-store'; +import LMDB from 'lmdb'; import { getMatchingRoot } from '@osd/cross-platform'; const GLOBAL_ATIME = `${Date.now()}`; @@ -40,15 +40,15 @@ const MINUTE = 1000 * 60; const HOUR = MINUTE * 60; const DAY = HOUR * 24; -const dbName = (db: LmdbStore.Database) => +const dbName = (db: LMDB.Database) => // @ts-expect-error db.name is not a documented/typed property db.name; export class Cache { - private readonly codes: LmdbStore.RootDatabase<string, string>; - private readonly atimes: LmdbStore.Database<string, string>; - private readonly mtimes: LmdbStore.Database<string, string>; - private readonly sourceMaps: LmdbStore.Database<string, string>; + private readonly codes: LMDB.RootDatabase<string, string>; + private readonly atimes: LMDB.Database<string, string>; + private readonly mtimes: LMDB.Database<string, string>; + private readonly sourceMaps: LMDB.Database<string, string>; private readonly pathRoots: string[]; private readonly prefix: string; private readonly log?: Writable; @@ -70,7 +70,7 @@ export class Cache { this.prefix = config.prefix; this.log = config.log; - this.codes = LmdbStore.open(config.dir, { + this.codes = LMDB.open(config.dir, { name: 'codes', encoding: 'string', maxReaders: 500, @@ -144,6 +144,7 @@ export class Cache { close() { clearTimeout(this.timer); + return this.codes?.close?.(); } private getKey(path: string) { @@ -162,7 +163,7 @@ export class Cache { return `${this.prefix}${normalizedPath}`; } - private safeGet<V>(db: LmdbStore.Database<V, string>, key: string) { + private safeGet<V>(db: LMDB.Database<V, string>, key: string) { try { const value = db.get(key); this.debug(value === undefined ? 'MISS' : 'HIT', db, key); @@ -172,7 +173,7 @@ export class Cache { } } - private async safePut<V>(db: LmdbStore.Database<V, string>, key: string, value: V) { + private async safePut<V>(db: LMDB.Database<V, string>, key: string, value: V) { try { await db.put(key, value); this.debug('PUT', db, key); @@ -181,13 +182,13 @@ export class Cache { } } - private debug(type: string, db: LmdbStore.Database, key: LmdbStore.Key) { + private debug(type: string, db: LMDB.Database, key: LMDB.Key) { if (this.log) { this.log.write(`${type} [${dbName(db)}] ${String(key)}\n`); } } - private logError(type: 'GET' | 'PUT', db: LmdbStore.Database, key: LmdbStore.Key, error: Error) { + private logError(type: 'GET' | 'PUT', db: LMDB.Database, key: LMDB.Key, error: Error) { this.debug(`ERROR/${type}`, db, `${String(key)}: ${error.stack}`); process.stderr.write( chalk.red( @@ -204,7 +205,6 @@ export class Cache { const validKeys: string[] = []; const invalidKeys: string[] = []; - // @ts-expect-error See https://github.com/DoctorEvidence/lmdb-store/pull/18 for (const { key, value } of this.atimes.getRange()) { const atime = parseInt(`${value}`, 10); if (Number.isNaN(atime) || atime < ATIME_LIMIT) { diff --git a/packages/osd-optimizer/src/optimizer/optimizer_config.test.ts b/packages/osd-optimizer/src/optimizer/optimizer_config.test.ts index a8a950726592..30620478349d 100644 --- a/packages/osd-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/osd-optimizer/src/optimizer/optimizer_config.test.ts @@ -43,7 +43,17 @@ jest.mock('os', () => { return realOs; }); +jest.mock('fs', () => { + const originalFs = jest.requireActual('fs'); + return { + ...originalFs, + existsSync: jest.fn(), + realpathSync: originalFs.realpathSync, + }; +}); + import Path from 'path'; +import { existsSync } from 'fs'; import { REPO_ROOT } from '@osd/utils'; import { createAbsolutePathSerializer } from '@osd/dev-utils'; @@ -122,7 +132,265 @@ describe('OptimizerConfig::parseOptions()', () => { expect(parseThemeTags).toBeCalledWith('foo'); }); - it('applies defaults', () => { + it('applies defaults if opensearch-dashboards-extra path does not exist', () => { + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + }) + ).toMatchInlineSnapshot(` + Object { + "cache": true, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 2, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [ + <absolute path>/src/plugins, + <absolute path>/plugins, + <absolute path>/opensearch-dashboards-extra, + ], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + cache: false, + }) + ).toMatchInlineSnapshot(` + Object { + "cache": false, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 2, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [ + <absolute path>/src/plugins, + <absolute path>/plugins, + <absolute path>/opensearch-dashboards-extra, + ], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + examples: true, + }) + ).toMatchInlineSnapshot(` + Object { + "cache": true, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 2, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [ + <absolute path>/src/plugins, + <absolute path>/plugins, + <absolute path>/examples, + <absolute path>/opensearch-dashboards-extra, + ], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + }) + ).toMatchInlineSnapshot(` + Object { + "cache": true, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 2, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [ + <absolute path>/src/plugins, + <absolute path>/plugins, + <absolute path>/opensearch-dashboards-extra, + ], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + pluginScanDirs: [Path.resolve(REPO_ROOT, 'x/y/z'), '/outside/of/repo'], + }) + ).toMatchInlineSnapshot(` + Object { + "cache": true, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 2, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [ + <absolute path>/x/y/z, + "/outside/of/repo", + ], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + process.env.OSD_OPTIMIZER_MAX_WORKERS = '100'; + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + pluginScanDirs: [], + }) + ).toMatchInlineSnapshot(` + Object { + "cache": true, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 100, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + process.env.OSD_OPTIMIZER_NO_CACHE = '0'; + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + pluginScanDirs: [], + }) + ).toMatchInlineSnapshot(` + Object { + "cache": false, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 100, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + process.env.OSD_OPTIMIZER_NO_CACHE = '1'; + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + pluginScanDirs: [], + }) + ).toMatchInlineSnapshot(` + Object { + "cache": false, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 100, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + process.env.OSD_OPTIMIZER_NO_CACHE = '1'; + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + pluginScanDirs: [], + cache: true, + }) + ).toMatchInlineSnapshot(` + Object { + "cache": false, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 100, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + + delete process.env.OSD_OPTIMIZER_NO_CACHE; + expect( + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + pluginScanDirs: [], + cache: true, + }) + ).toMatchInlineSnapshot(` + Object { + "cache": true, + "dist": false, + "filters": Array [], + "includeCoreBundle": false, + "inspectWorkers": false, + "maxWorkerCount": 100, + "outputRoot": <absolute path>, + "pluginPaths": Array [], + "pluginScanDirs": Array [], + "profileWebpack": false, + "repoRoot": <absolute path>, + "themeTags": undefined, + "watch": false, + } + `); + }); + + it('applies defaults if opensearch-dashboards-extra path exists', () => { + (existsSync as jest.Mock).mockReturnValue(true); expect( OptimizerConfig.parseOptions({ repoRoot: REPO_ROOT, @@ -461,7 +729,7 @@ describe('OptimizerConfig::create()', () => { [Window], ], "invocationCallOrder": Array [ - 21, + 39, ], "lastCall": Array [ Symbol(parsed plugin scan dirs), @@ -491,7 +759,7 @@ describe('OptimizerConfig::create()', () => { [Window], ], "invocationCallOrder": Array [ - 23, + 41, ], "lastCall": Array [ Array [], @@ -522,7 +790,7 @@ describe('OptimizerConfig::create()', () => { [Window], ], "invocationCallOrder": Array [ - 22, + 40, ], "lastCall": Array [ Symbol(new platform plugins), diff --git a/packages/osd-optimizer/src/worker/webpack.config.ts b/packages/osd-optimizer/src/worker/webpack.config.ts index caeed72a3bb1..f5272c207548 100644 --- a/packages/osd-optimizer/src/worker/webpack.config.ts +++ b/packages/osd-optimizer/src/worker/webpack.config.ts @@ -50,7 +50,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: const commonConfig: webpack.Configuration = { node: { fs: 'empty' }, - context: bundle.contextDir, + context: Path.normalize(bundle.contextDir), cache: true, entry: { [bundle.id]: ENTRY_CREATOR, @@ -69,6 +69,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: info.absoluteResourcePath )}${info.query}`, jsonpFunction: `${bundle.id}_bundle_jsonpfunction`, + hashFunction: 'Xxh64', }, optimization: { @@ -176,12 +177,11 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: )};\n${content}`; }, webpackImporter: false, - implementation: require('sass'), + implementation: require('node-sass'), sassOptions: { outputStyle: 'compressed', includePaths: [Path.resolve(worker.repoRoot, 'node_modules')], sourceMapRoot: `/${bundle.type}:${bundle.id}`, - fiber: require('fibers'), }, }, }, diff --git a/packages/osd-pm/dist/index.js b/packages/osd-pm/dist/index.js index 944856a8dd13..e43a9bfe407e 100644 --- a/packages/osd-pm/dist/index.js +++ b/packages/osd-pm/dist/index.js @@ -134,7 +134,7 @@ Object.defineProperty(exports, "run", { var _cli = __webpack_require__(1); -var _production = __webpack_require__(516); +var _production = __webpack_require__(515); var _projects = __webpack_require__(146); @@ -783,245 +783,245 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__importDefault", function() { return __importDefault; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__classPrivateFieldGet", function() { return __classPrivateFieldGet; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__classPrivateFieldSet", function() { return __classPrivateFieldSet; }); -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ -/* global Reflect, Promise */ - -var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); -}; - -function __extends(d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -} - -var __assign = function() { - __assign = Object.assign || function __assign(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - } - return t; - } - return __assign.apply(this, arguments); -} - -function __rest(s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -} - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -function __param(paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -} - -function __metadata(metadataKey, metadataValue) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); -} - -function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -} - -function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -} - -var __createBinding = Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -}); - -function __exportStar(m, o) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); -} - -function __values(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -} - -function __read(o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -} - -/** @deprecated */ -function __spread() { - for (var ar = [], i = 0; i < arguments.length; i++) - ar = ar.concat(__read(arguments[i])); - return ar; -} - -/** @deprecated */ -function __spreadArrays() { - for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; - for (var r = Array(s), k = 0, i = 0; i < il; i++) - for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) - r[k] = a[j]; - return r; -} - -function __spreadArray(to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -} - -function __await(v) { - return this instanceof __await ? (this.v = v, this) : new __await(v); -} - -function __asyncGenerator(thisArg, _arguments, generator) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var g = generator.apply(thisArg, _arguments || []), i, q = []; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; - function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } - function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } - function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } - function fulfill(value) { resume("next", value); } - function reject(value) { resume("throw", value); } - function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } -} - -function __asyncDelegator(o) { - var i, p; - return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } -} - -function __asyncValues(o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -} - -function __makeTemplateObject(cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; - -var __setModuleDefault = Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}; - -function __importStar(mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -} - -function __importDefault(mod) { - return (mod && mod.__esModule) ? mod : { default: mod }; -} - -function __classPrivateFieldGet(receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -} - -function __classPrivateFieldSet(receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -} +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + } + return __assign.apply(this, arguments); +} + +function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +} + +function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +function __param(paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +} + +function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); +} + +function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + +var __createBinding = Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +}); + +function __exportStar(m, o) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); +} + +function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +} + +function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +} + +/** @deprecated */ +function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; +} + +/** @deprecated */ +function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +} + +function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +} + +function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); +} + +function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +} + +function __asyncDelegator(o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } +} + +function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +} + +function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; + +var __setModuleDefault = Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}; + +function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +} + +function __importDefault(mod) { + return (mod && mod.__esModule) ? mod : { default: mod }; +} + +function __classPrivateFieldGet(receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +} + +function __classPrivateFieldSet(receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +} /***/ }), @@ -1701,224 +1701,224 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__importDefault", function() { return __importDefault; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__classPrivateFieldGet", function() { return __classPrivateFieldGet; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__classPrivateFieldSet", function() { return __classPrivateFieldSet; }); -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ -/* global Reflect, Promise */ - -var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); -}; - -function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -} - -var __assign = function() { - __assign = Object.assign || function __assign(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - } - return t; - } - return __assign.apply(this, arguments); -} - -function __rest(s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -} - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -function __param(paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -} - -function __metadata(metadataKey, metadataValue) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); -} - -function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -} - -function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -} - -function __createBinding(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -} - -function __exportStar(m, exports) { - for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) exports[p] = m[p]; -} - -function __values(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -} - -function __read(o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -} - -function __spread() { - for (var ar = [], i = 0; i < arguments.length; i++) - ar = ar.concat(__read(arguments[i])); - return ar; -} - -function __spreadArrays() { - for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; - for (var r = Array(s), k = 0, i = 0; i < il; i++) - for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) - r[k] = a[j]; - return r; -}; - -function __await(v) { - return this instanceof __await ? (this.v = v, this) : new __await(v); -} - -function __asyncGenerator(thisArg, _arguments, generator) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var g = generator.apply(thisArg, _arguments || []), i, q = []; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; - function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } - function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } - function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } - function fulfill(value) { resume("next", value); } - function reject(value) { resume("throw", value); } - function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } -} - -function __asyncDelegator(o) { - var i, p; - return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } -} - -function __asyncValues(o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -} - -function __makeTemplateObject(cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; - -function __importStar(mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result.default = mod; - return result; -} - -function __importDefault(mod) { - return (mod && mod.__esModule) ? mod : { default: mod }; -} - -function __classPrivateFieldGet(receiver, privateMap) { - if (!privateMap.has(receiver)) { - throw new TypeError("attempted to get private field on non-instance"); - } - return privateMap.get(receiver); -} - -function __classPrivateFieldSet(receiver, privateMap, value) { - if (!privateMap.has(receiver)) { - throw new TypeError("attempted to set private field on non-instance"); - } - privateMap.set(receiver, value); - return value; -} +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + } + return __assign.apply(this, arguments); +} + +function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +} + +function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +function __param(paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +} + +function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); +} + +function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + +function __createBinding(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +} + +function __exportStar(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) exports[p] = m[p]; +} + +function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +} + +function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +} + +function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; +} + +function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; + +function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); +} + +function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +} + +function __asyncDelegator(o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } +} + +function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +} + +function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; + +function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result.default = mod; + return result; +} + +function __importDefault(mod) { + return (mod && mod.__esModule) ? mod : { default: mod }; +} + +function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); +} + +function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; +} /***/ }), @@ -8322,158 +8322,158 @@ convert.rgb.gray = function (rgb) { /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -module.exports = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; + + +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; /***/ }), @@ -10848,58 +10848,58 @@ mkdirP.sync = function sync (p, opts, made) { /* 142 */ /***/ (function(module, exports) { -exports.replaceDollarWithPercentPair = replaceDollarWithPercentPair -exports.convertToSetCommand = convertToSetCommand -exports.convertToSetCommands = convertToSetCommands - -function convertToSetCommand(key, value) { - var line = "" - key = key || "" - key = key.trim() - value = value || "" - value = value.trim() - if(key && value && value.length > 0) { - line = "@SET " + key + "=" + replaceDollarWithPercentPair(value) + "\r\n" - } - return line -} - -function extractVariableValuePairs(declarations) { - var pairs = {} - declarations.map(function(declaration) { - var split = declaration.split("=") - pairs[split[0]]=split[1] - }) - return pairs -} - -function convertToSetCommands(variableString) { - var variableValuePairs = extractVariableValuePairs(variableString.split(" ")) - var variableDeclarationsAsBatch = "" - Object.keys(variableValuePairs).forEach(function (key) { - variableDeclarationsAsBatch += convertToSetCommand(key, variableValuePairs[key]) - }) - return variableDeclarationsAsBatch -} - -function replaceDollarWithPercentPair(value) { - var dollarExpressions = /\$\{?([^\$@#\?\- \t{}:]+)\}?/g - var result = "" - var startIndex = 0 - value = value || "" - do { - var match = dollarExpressions.exec(value) - if(match) { - var betweenMatches = value.substring(startIndex, match.index) || "" - result += betweenMatches + "%" + match[1] + "%" - startIndex = dollarExpressions.lastIndex - } - } while (dollarExpressions.lastIndex > 0) - result += value.substr(startIndex) - return result -} - - +exports.replaceDollarWithPercentPair = replaceDollarWithPercentPair +exports.convertToSetCommand = convertToSetCommand +exports.convertToSetCommands = convertToSetCommands + +function convertToSetCommand(key, value) { + var line = "" + key = key || "" + key = key.trim() + value = value || "" + value = value.trim() + if(key && value && value.length > 0) { + line = "@SET " + key + "=" + replaceDollarWithPercentPair(value) + "\r\n" + } + return line +} + +function extractVariableValuePairs(declarations) { + var pairs = {} + declarations.map(function(declaration) { + var split = declaration.split("=") + pairs[split[0]]=split[1] + }) + return pairs +} + +function convertToSetCommands(variableString) { + var variableValuePairs = extractVariableValuePairs(variableString.split(" ")) + var variableDeclarationsAsBatch = "" + Object.keys(variableValuePairs).forEach(function (key) { + variableDeclarationsAsBatch += convertToSetCommand(key, variableValuePairs[key]) + }) + return variableDeclarationsAsBatch +} + +function replaceDollarWithPercentPair(value) { + var dollarExpressions = /\$\{?([^\$@#\?\- \t{}:]+)\}?/g + var result = "" + var startIndex = 0 + value = value || "" + do { + var match = dollarExpressions.exec(value) + if(match) { + var betweenMatches = value.substring(startIndex, match.index) || "" + result += betweenMatches + "%" + match[1] + "%" + startIndex = dollarExpressions.lastIndex + } + } while (dollarExpressions.lastIndex > 0) + result += value.substr(startIndex) + return result +} + + /***/ }), @@ -17661,158 +17661,158 @@ convert.rgb.gray = function (rgb) { /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -module.exports = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; + + +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; /***/ }), @@ -21376,7 +21376,7 @@ var nodeModulesPaths = __webpack_require__(207); var normalizeOptions = __webpack_require__(209); var isCore = __webpack_require__(210); -var realpathFS = fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; +var realpathFS = process.platform !== 'win32' && fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; var homedir = getHomedir(); var defaultPaths = function () { @@ -22053,7 +22053,7 @@ module.exports = function bind(that) { /* 214 */ /***/ (function(module) { -module.exports = JSON.parse("{\"assert\":true,\"node:assert\":[\">= 14.18 && < 15\",\">= 16\"],\"assert/strict\":\">= 15\",\"node:assert/strict\":\">= 16\",\"async_hooks\":\">= 8\",\"node:async_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"buffer_ieee754\":\">= 0.5 && < 0.9.7\",\"buffer\":true,\"node:buffer\":[\">= 14.18 && < 15\",\">= 16\"],\"child_process\":true,\"node:child_process\":[\">= 14.18 && < 15\",\">= 16\"],\"cluster\":\">= 0.5\",\"node:cluster\":[\">= 14.18 && < 15\",\">= 16\"],\"console\":true,\"node:console\":[\">= 14.18 && < 15\",\">= 16\"],\"constants\":true,\"node:constants\":[\">= 14.18 && < 15\",\">= 16\"],\"crypto\":true,\"node:crypto\":[\">= 14.18 && < 15\",\">= 16\"],\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"node:dgram\":[\">= 14.18 && < 15\",\">= 16\"],\"diagnostics_channel\":[\">= 14.17 && < 15\",\">= 15.1\"],\"node:diagnostics_channel\":[\">= 14.18 && < 15\",\">= 16\"],\"dns\":true,\"node:dns\":[\">= 14.18 && < 15\",\">= 16\"],\"dns/promises\":\">= 15\",\"node:dns/promises\":\">= 16\",\"domain\":\">= 0.7.12\",\"node:domain\":[\">= 14.18 && < 15\",\">= 16\"],\"events\":true,\"node:events\":[\">= 14.18 && < 15\",\">= 16\"],\"freelist\":\"< 6\",\"fs\":true,\"node:fs\":[\">= 14.18 && < 15\",\">= 16\"],\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"node:fs/promises\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_agent\":\">= 0.11.1\",\"node:_http_agent\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_client\":\">= 0.11.1\",\"node:_http_client\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_common\":\">= 0.11.1\",\"node:_http_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_incoming\":\">= 0.11.1\",\"node:_http_incoming\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_outgoing\":\">= 0.11.1\",\"node:_http_outgoing\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_server\":\">= 0.11.1\",\"node:_http_server\":[\">= 14.18 && < 15\",\">= 16\"],\"http\":true,\"node:http\":[\">= 14.18 && < 15\",\">= 16\"],\"http2\":\">= 8.8\",\"node:http2\":[\">= 14.18 && < 15\",\">= 16\"],\"https\":true,\"node:https\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector\":\">= 8\",\"node:inspector\":[\">= 14.18 && < 15\",\">= 16\"],\"_linklist\":\"< 8\",\"module\":true,\"node:module\":[\">= 14.18 && < 15\",\">= 16\"],\"net\":true,\"node:net\":[\">= 14.18 && < 15\",\">= 16\"],\"node-inspect/lib/_inspect\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6 && < 12\",\"os\":true,\"node:os\":[\">= 14.18 && < 15\",\">= 16\"],\"path\":true,\"node:path\":[\">= 14.18 && < 15\",\">= 16\"],\"path/posix\":\">= 15.3\",\"node:path/posix\":\">= 16\",\"path/win32\":\">= 15.3\",\"node:path/win32\":\">= 16\",\"perf_hooks\":\">= 8.5\",\"node:perf_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"process\":\">= 1\",\"node:process\":[\">= 14.18 && < 15\",\">= 16\"],\"punycode\":\">= 0.5\",\"node:punycode\":[\">= 14.18 && < 15\",\">= 16\"],\"querystring\":true,\"node:querystring\":[\">= 14.18 && < 15\",\">= 16\"],\"readline\":true,\"node:readline\":[\">= 14.18 && < 15\",\">= 16\"],\"readline/promises\":\">= 17\",\"node:readline/promises\":\">= 17\",\"repl\":true,\"node:repl\":[\">= 14.18 && < 15\",\">= 16\"],\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"node:_stream_duplex\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_transform\":\">= 0.9.4\",\"node:_stream_transform\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_wrap\":\">= 1.4.1\",\"node:_stream_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_passthrough\":\">= 0.9.4\",\"node:_stream_passthrough\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_readable\":\">= 0.9.4\",\"node:_stream_readable\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_writable\":\">= 0.9.4\",\"node:_stream_writable\":[\">= 14.18 && < 15\",\">= 16\"],\"stream\":true,\"node:stream\":[\">= 14.18 && < 15\",\">= 16\"],\"stream/consumers\":\">= 16.7\",\"node:stream/consumers\":\">= 16.7\",\"stream/promises\":\">= 15\",\"node:stream/promises\":\">= 16\",\"stream/web\":\">= 16.5\",\"node:stream/web\":\">= 16.5\",\"string_decoder\":true,\"node:string_decoder\":[\">= 14.18 && < 15\",\">= 16\"],\"sys\":[\">= 0.4 && < 0.7\",\">= 0.8\"],\"node:sys\":[\">= 14.18 && < 15\",\">= 16\"],\"timers\":true,\"node:timers\":[\">= 14.18 && < 15\",\">= 16\"],\"timers/promises\":\">= 15\",\"node:timers/promises\":\">= 16\",\"_tls_common\":\">= 0.11.13\",\"node:_tls_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"node:_tls_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"tls\":true,\"node:tls\":[\">= 14.18 && < 15\",\">= 16\"],\"trace_events\":\">= 10\",\"node:trace_events\":[\">= 14.18 && < 15\",\">= 16\"],\"tty\":true,\"node:tty\":[\">= 14.18 && < 15\",\">= 16\"],\"url\":true,\"node:url\":[\">= 14.18 && < 15\",\">= 16\"],\"util\":true,\"node:util\":[\">= 14.18 && < 15\",\">= 16\"],\"util/types\":\">= 15.3\",\"node:util/types\":\">= 16\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/consarray\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/logreader\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8\":\">= 1\",\"node:v8\":[\">= 14.18 && < 15\",\">= 16\"],\"vm\":true,\"node:vm\":[\">= 14.18 && < 15\",\">= 16\"],\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"node:worker_threads\":[\">= 14.18 && < 15\",\">= 16\"],\"zlib\":\">= 0.5\",\"node:zlib\":[\">= 14.18 && < 15\",\">= 16\"]}"); +module.exports = JSON.parse("{\"assert\":true,\"node:assert\":[\">= 14.18 && < 15\",\">= 16\"],\"assert/strict\":\">= 15\",\"node:assert/strict\":\">= 16\",\"async_hooks\":\">= 8\",\"node:async_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"buffer_ieee754\":\">= 0.5 && < 0.9.7\",\"buffer\":true,\"node:buffer\":[\">= 14.18 && < 15\",\">= 16\"],\"child_process\":true,\"node:child_process\":[\">= 14.18 && < 15\",\">= 16\"],\"cluster\":\">= 0.5\",\"node:cluster\":[\">= 14.18 && < 15\",\">= 16\"],\"console\":true,\"node:console\":[\">= 14.18 && < 15\",\">= 16\"],\"constants\":true,\"node:constants\":[\">= 14.18 && < 15\",\">= 16\"],\"crypto\":true,\"node:crypto\":[\">= 14.18 && < 15\",\">= 16\"],\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"node:dgram\":[\">= 14.18 && < 15\",\">= 16\"],\"diagnostics_channel\":[\">= 14.17 && < 15\",\">= 15.1\"],\"node:diagnostics_channel\":[\">= 14.18 && < 15\",\">= 16\"],\"dns\":true,\"node:dns\":[\">= 14.18 && < 15\",\">= 16\"],\"dns/promises\":\">= 15\",\"node:dns/promises\":\">= 16\",\"domain\":\">= 0.7.12\",\"node:domain\":[\">= 14.18 && < 15\",\">= 16\"],\"events\":true,\"node:events\":[\">= 14.18 && < 15\",\">= 16\"],\"freelist\":\"< 6\",\"fs\":true,\"node:fs\":[\">= 14.18 && < 15\",\">= 16\"],\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"node:fs/promises\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_agent\":\">= 0.11.1\",\"node:_http_agent\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_client\":\">= 0.11.1\",\"node:_http_client\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_common\":\">= 0.11.1\",\"node:_http_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_incoming\":\">= 0.11.1\",\"node:_http_incoming\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_outgoing\":\">= 0.11.1\",\"node:_http_outgoing\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_server\":\">= 0.11.1\",\"node:_http_server\":[\">= 14.18 && < 15\",\">= 16\"],\"http\":true,\"node:http\":[\">= 14.18 && < 15\",\">= 16\"],\"http2\":\">= 8.8\",\"node:http2\":[\">= 14.18 && < 15\",\">= 16\"],\"https\":true,\"node:https\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector\":\">= 8\",\"node:inspector\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector/promises\":[\">= 19\"],\"node:inspector/promises\":[\">= 19\"],\"_linklist\":\"< 8\",\"module\":true,\"node:module\":[\">= 14.18 && < 15\",\">= 16\"],\"net\":true,\"node:net\":[\">= 14.18 && < 15\",\">= 16\"],\"node-inspect/lib/_inspect\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6 && < 12\",\"os\":true,\"node:os\":[\">= 14.18 && < 15\",\">= 16\"],\"path\":true,\"node:path\":[\">= 14.18 && < 15\",\">= 16\"],\"path/posix\":\">= 15.3\",\"node:path/posix\":\">= 16\",\"path/win32\":\">= 15.3\",\"node:path/win32\":\">= 16\",\"perf_hooks\":\">= 8.5\",\"node:perf_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"process\":\">= 1\",\"node:process\":[\">= 14.18 && < 15\",\">= 16\"],\"punycode\":\">= 0.5\",\"node:punycode\":[\">= 14.18 && < 15\",\">= 16\"],\"querystring\":true,\"node:querystring\":[\">= 14.18 && < 15\",\">= 16\"],\"readline\":true,\"node:readline\":[\">= 14.18 && < 15\",\">= 16\"],\"readline/promises\":\">= 17\",\"node:readline/promises\":\">= 17\",\"repl\":true,\"node:repl\":[\">= 14.18 && < 15\",\">= 16\"],\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"node:_stream_duplex\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_transform\":\">= 0.9.4\",\"node:_stream_transform\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_wrap\":\">= 1.4.1\",\"node:_stream_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_passthrough\":\">= 0.9.4\",\"node:_stream_passthrough\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_readable\":\">= 0.9.4\",\"node:_stream_readable\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_writable\":\">= 0.9.4\",\"node:_stream_writable\":[\">= 14.18 && < 15\",\">= 16\"],\"stream\":true,\"node:stream\":[\">= 14.18 && < 15\",\">= 16\"],\"stream/consumers\":\">= 16.7\",\"node:stream/consumers\":\">= 16.7\",\"stream/promises\":\">= 15\",\"node:stream/promises\":\">= 16\",\"stream/web\":\">= 16.5\",\"node:stream/web\":\">= 16.5\",\"string_decoder\":true,\"node:string_decoder\":[\">= 14.18 && < 15\",\">= 16\"],\"sys\":[\">= 0.4 && < 0.7\",\">= 0.8\"],\"node:sys\":[\">= 14.18 && < 15\",\">= 16\"],\"node:test\":[\">= 16.17 && < 17\",\">= 18\"],\"timers\":true,\"node:timers\":[\">= 14.18 && < 15\",\">= 16\"],\"timers/promises\":\">= 15\",\"node:timers/promises\":\">= 16\",\"_tls_common\":\">= 0.11.13\",\"node:_tls_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"node:_tls_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"tls\":true,\"node:tls\":[\">= 14.18 && < 15\",\">= 16\"],\"trace_events\":\">= 10\",\"node:trace_events\":[\">= 14.18 && < 15\",\">= 16\"],\"tty\":true,\"node:tty\":[\">= 14.18 && < 15\",\">= 16\"],\"url\":true,\"node:url\":[\">= 14.18 && < 15\",\">= 16\"],\"util\":true,\"node:util\":[\">= 14.18 && < 15\",\">= 16\"],\"util/types\":\">= 15.3\",\"node:util/types\":\">= 16\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/consarray\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/logreader\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8\":\">= 1\",\"node:v8\":[\">= 14.18 && < 15\",\">= 16\"],\"vm\":true,\"node:vm\":[\">= 14.18 && < 15\",\">= 16\"],\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"node:worker_threads\":[\">= 14.18 && < 15\",\">= 16\"],\"zlib\":\">= 0.5\",\"node:zlib\":[\">= 14.18 && < 15\",\">= 16\"]}"); /***/ }), /* 215 */ @@ -22117,7 +22117,7 @@ module.exports = core; /* 216 */ /***/ (function(module) { -module.exports = JSON.parse("{\"assert\":true,\"node:assert\":[\">= 14.18 && < 15\",\">= 16\"],\"assert/strict\":\">= 15\",\"node:assert/strict\":\">= 16\",\"async_hooks\":\">= 8\",\"node:async_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"buffer_ieee754\":\">= 0.5 && < 0.9.7\",\"buffer\":true,\"node:buffer\":[\">= 14.18 && < 15\",\">= 16\"],\"child_process\":true,\"node:child_process\":[\">= 14.18 && < 15\",\">= 16\"],\"cluster\":\">= 0.5\",\"node:cluster\":[\">= 14.18 && < 15\",\">= 16\"],\"console\":true,\"node:console\":[\">= 14.18 && < 15\",\">= 16\"],\"constants\":true,\"node:constants\":[\">= 14.18 && < 15\",\">= 16\"],\"crypto\":true,\"node:crypto\":[\">= 14.18 && < 15\",\">= 16\"],\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"node:dgram\":[\">= 14.18 && < 15\",\">= 16\"],\"diagnostics_channel\":[\">= 14.17 && < 15\",\">= 15.1\"],\"node:diagnostics_channel\":[\">= 14.18 && < 15\",\">= 16\"],\"dns\":true,\"node:dns\":[\">= 14.18 && < 15\",\">= 16\"],\"dns/promises\":\">= 15\",\"node:dns/promises\":\">= 16\",\"domain\":\">= 0.7.12\",\"node:domain\":[\">= 14.18 && < 15\",\">= 16\"],\"events\":true,\"node:events\":[\">= 14.18 && < 15\",\">= 16\"],\"freelist\":\"< 6\",\"fs\":true,\"node:fs\":[\">= 14.18 && < 15\",\">= 16\"],\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"node:fs/promises\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_agent\":\">= 0.11.1\",\"node:_http_agent\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_client\":\">= 0.11.1\",\"node:_http_client\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_common\":\">= 0.11.1\",\"node:_http_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_incoming\":\">= 0.11.1\",\"node:_http_incoming\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_outgoing\":\">= 0.11.1\",\"node:_http_outgoing\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_server\":\">= 0.11.1\",\"node:_http_server\":[\">= 14.18 && < 15\",\">= 16\"],\"http\":true,\"node:http\":[\">= 14.18 && < 15\",\">= 16\"],\"http2\":\">= 8.8\",\"node:http2\":[\">= 14.18 && < 15\",\">= 16\"],\"https\":true,\"node:https\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector\":\">= 8\",\"node:inspector\":[\">= 14.18 && < 15\",\">= 16\"],\"_linklist\":\"< 8\",\"module\":true,\"node:module\":[\">= 14.18 && < 15\",\">= 16\"],\"net\":true,\"node:net\":[\">= 14.18 && < 15\",\">= 16\"],\"node-inspect/lib/_inspect\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6 && < 12\",\"os\":true,\"node:os\":[\">= 14.18 && < 15\",\">= 16\"],\"path\":true,\"node:path\":[\">= 14.18 && < 15\",\">= 16\"],\"path/posix\":\">= 15.3\",\"node:path/posix\":\">= 16\",\"path/win32\":\">= 15.3\",\"node:path/win32\":\">= 16\",\"perf_hooks\":\">= 8.5\",\"node:perf_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"process\":\">= 1\",\"node:process\":[\">= 14.18 && < 15\",\">= 16\"],\"punycode\":\">= 0.5\",\"node:punycode\":[\">= 14.18 && < 15\",\">= 16\"],\"querystring\":true,\"node:querystring\":[\">= 14.18 && < 15\",\">= 16\"],\"readline\":true,\"node:readline\":[\">= 14.18 && < 15\",\">= 16\"],\"readline/promises\":\">= 17\",\"node:readline/promises\":\">= 17\",\"repl\":true,\"node:repl\":[\">= 14.18 && < 15\",\">= 16\"],\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"node:_stream_duplex\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_transform\":\">= 0.9.4\",\"node:_stream_transform\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_wrap\":\">= 1.4.1\",\"node:_stream_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_passthrough\":\">= 0.9.4\",\"node:_stream_passthrough\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_readable\":\">= 0.9.4\",\"node:_stream_readable\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_writable\":\">= 0.9.4\",\"node:_stream_writable\":[\">= 14.18 && < 15\",\">= 16\"],\"stream\":true,\"node:stream\":[\">= 14.18 && < 15\",\">= 16\"],\"stream/consumers\":\">= 16.7\",\"node:stream/consumers\":\">= 16.7\",\"stream/promises\":\">= 15\",\"node:stream/promises\":\">= 16\",\"stream/web\":\">= 16.5\",\"node:stream/web\":\">= 16.5\",\"string_decoder\":true,\"node:string_decoder\":[\">= 14.18 && < 15\",\">= 16\"],\"sys\":[\">= 0.4 && < 0.7\",\">= 0.8\"],\"node:sys\":[\">= 14.18 && < 15\",\">= 16\"],\"timers\":true,\"node:timers\":[\">= 14.18 && < 15\",\">= 16\"],\"timers/promises\":\">= 15\",\"node:timers/promises\":\">= 16\",\"_tls_common\":\">= 0.11.13\",\"node:_tls_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"node:_tls_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"tls\":true,\"node:tls\":[\">= 14.18 && < 15\",\">= 16\"],\"trace_events\":\">= 10\",\"node:trace_events\":[\">= 14.18 && < 15\",\">= 16\"],\"tty\":true,\"node:tty\":[\">= 14.18 && < 15\",\">= 16\"],\"url\":true,\"node:url\":[\">= 14.18 && < 15\",\">= 16\"],\"util\":true,\"node:util\":[\">= 14.18 && < 15\",\">= 16\"],\"util/types\":\">= 15.3\",\"node:util/types\":\">= 16\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/consarray\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/logreader\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8\":\">= 1\",\"node:v8\":[\">= 14.18 && < 15\",\">= 16\"],\"vm\":true,\"node:vm\":[\">= 14.18 && < 15\",\">= 16\"],\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"node:worker_threads\":[\">= 14.18 && < 15\",\">= 16\"],\"zlib\":\">= 0.5\",\"node:zlib\":[\">= 14.18 && < 15\",\">= 16\"]}"); +module.exports = JSON.parse("{\"assert\":true,\"node:assert\":[\">= 14.18 && < 15\",\">= 16\"],\"assert/strict\":\">= 15\",\"node:assert/strict\":\">= 16\",\"async_hooks\":\">= 8\",\"node:async_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"buffer_ieee754\":\">= 0.5 && < 0.9.7\",\"buffer\":true,\"node:buffer\":[\">= 14.18 && < 15\",\">= 16\"],\"child_process\":true,\"node:child_process\":[\">= 14.18 && < 15\",\">= 16\"],\"cluster\":\">= 0.5\",\"node:cluster\":[\">= 14.18 && < 15\",\">= 16\"],\"console\":true,\"node:console\":[\">= 14.18 && < 15\",\">= 16\"],\"constants\":true,\"node:constants\":[\">= 14.18 && < 15\",\">= 16\"],\"crypto\":true,\"node:crypto\":[\">= 14.18 && < 15\",\">= 16\"],\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"node:dgram\":[\">= 14.18 && < 15\",\">= 16\"],\"diagnostics_channel\":[\">= 14.17 && < 15\",\">= 15.1\"],\"node:diagnostics_channel\":[\">= 14.18 && < 15\",\">= 16\"],\"dns\":true,\"node:dns\":[\">= 14.18 && < 15\",\">= 16\"],\"dns/promises\":\">= 15\",\"node:dns/promises\":\">= 16\",\"domain\":\">= 0.7.12\",\"node:domain\":[\">= 14.18 && < 15\",\">= 16\"],\"events\":true,\"node:events\":[\">= 14.18 && < 15\",\">= 16\"],\"freelist\":\"< 6\",\"fs\":true,\"node:fs\":[\">= 14.18 && < 15\",\">= 16\"],\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"node:fs/promises\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_agent\":\">= 0.11.1\",\"node:_http_agent\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_client\":\">= 0.11.1\",\"node:_http_client\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_common\":\">= 0.11.1\",\"node:_http_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_incoming\":\">= 0.11.1\",\"node:_http_incoming\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_outgoing\":\">= 0.11.1\",\"node:_http_outgoing\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_server\":\">= 0.11.1\",\"node:_http_server\":[\">= 14.18 && < 15\",\">= 16\"],\"http\":true,\"node:http\":[\">= 14.18 && < 15\",\">= 16\"],\"http2\":\">= 8.8\",\"node:http2\":[\">= 14.18 && < 15\",\">= 16\"],\"https\":true,\"node:https\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector\":\">= 8\",\"node:inspector\":[\">= 14.18 && < 15\",\">= 16\"],\"_linklist\":\"< 8\",\"module\":true,\"node:module\":[\">= 14.18 && < 15\",\">= 16\"],\"net\":true,\"node:net\":[\">= 14.18 && < 15\",\">= 16\"],\"node-inspect/lib/_inspect\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6 && < 12\",\"os\":true,\"node:os\":[\">= 14.18 && < 15\",\">= 16\"],\"path\":true,\"node:path\":[\">= 14.18 && < 15\",\">= 16\"],\"path/posix\":\">= 15.3\",\"node:path/posix\":\">= 16\",\"path/win32\":\">= 15.3\",\"node:path/win32\":\">= 16\",\"perf_hooks\":\">= 8.5\",\"node:perf_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"process\":\">= 1\",\"node:process\":[\">= 14.18 && < 15\",\">= 16\"],\"punycode\":\">= 0.5\",\"node:punycode\":[\">= 14.18 && < 15\",\">= 16\"],\"querystring\":true,\"node:querystring\":[\">= 14.18 && < 15\",\">= 16\"],\"readline\":true,\"node:readline\":[\">= 14.18 && < 15\",\">= 16\"],\"readline/promises\":\">= 17\",\"node:readline/promises\":\">= 17\",\"repl\":true,\"node:repl\":[\">= 14.18 && < 15\",\">= 16\"],\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"node:_stream_duplex\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_transform\":\">= 0.9.4\",\"node:_stream_transform\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_wrap\":\">= 1.4.1\",\"node:_stream_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_passthrough\":\">= 0.9.4\",\"node:_stream_passthrough\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_readable\":\">= 0.9.4\",\"node:_stream_readable\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_writable\":\">= 0.9.4\",\"node:_stream_writable\":[\">= 14.18 && < 15\",\">= 16\"],\"stream\":true,\"node:stream\":[\">= 14.18 && < 15\",\">= 16\"],\"stream/consumers\":\">= 16.7\",\"node:stream/consumers\":\">= 16.7\",\"stream/promises\":\">= 15\",\"node:stream/promises\":\">= 16\",\"stream/web\":\">= 16.5\",\"node:stream/web\":\">= 16.5\",\"string_decoder\":true,\"node:string_decoder\":[\">= 14.18 && < 15\",\">= 16\"],\"sys\":[\">= 0.4 && < 0.7\",\">= 0.8\"],\"node:sys\":[\">= 14.18 && < 15\",\">= 16\"],\"node:test\":\">= 18\",\"timers\":true,\"node:timers\":[\">= 14.18 && < 15\",\">= 16\"],\"timers/promises\":\">= 15\",\"node:timers/promises\":\">= 16\",\"_tls_common\":\">= 0.11.13\",\"node:_tls_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"node:_tls_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"tls\":true,\"node:tls\":[\">= 14.18 && < 15\",\">= 16\"],\"trace_events\":\">= 10\",\"node:trace_events\":[\">= 14.18 && < 15\",\">= 16\"],\"tty\":true,\"node:tty\":[\">= 14.18 && < 15\",\">= 16\"],\"url\":true,\"node:url\":[\">= 14.18 && < 15\",\">= 16\"],\"util\":true,\"node:util\":[\">= 14.18 && < 15\",\">= 16\"],\"util/types\":\">= 15.3\",\"node:util/types\":\">= 16\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/consarray\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/logreader\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8\":\">= 1\",\"node:v8\":[\">= 14.18 && < 15\",\">= 16\"],\"vm\":true,\"node:vm\":[\">= 14.18 && < 15\",\">= 16\"],\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"node:worker_threads\":[\">= 14.18 && < 15\",\">= 16\"],\"zlib\":\">= 0.5\",\"node:zlib\":[\">= 14.18 && < 15\",\">= 16\"]}"); /***/ }), /* 217 */ @@ -22142,7 +22142,7 @@ var caller = __webpack_require__(206); var nodeModulesPaths = __webpack_require__(207); var normalizeOptions = __webpack_require__(209); -var realpathFS = fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; +var realpathFS = process.platform !== 'win32' && fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; var homedir = getHomedir(); var defaultPaths = function () { @@ -56551,9 +56551,9 @@ exports.OpenSearchDashboards = OpenSearchDashboards; "use strict"; const minimatch = __webpack_require__(150); -const arrayUnion = __webpack_require__(513); -const arrayDiffer = __webpack_require__(514); -const arrify = __webpack_require__(515); +const arrayUnion = __webpack_require__(299); +const arrayDiffer = __webpack_require__(513); +const arrify = __webpack_require__(514); module.exports = (list, patterns, options = {}) => { list = arrify(list); @@ -56583,18 +56583,6 @@ module.exports = (list, patterns, options = {}) => { "use strict"; -module.exports = (...arguments_) => { - return [...new Set([].concat(...arguments_))]; -}; - - -/***/ }), -/* 514 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - const arrayDiffer = (array, ...values) => { const rest = new Set([].concat(...values)); return array.filter(element => !rest.has(element)); @@ -56604,7 +56592,7 @@ module.exports = arrayDiffer; /***/ }), -/* 515 */ +/* 514 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56634,7 +56622,7 @@ module.exports = arrify; /***/ }), -/* 516 */ +/* 515 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56650,10 +56638,10 @@ Object.defineProperty(exports, "buildProductionProjects", { } }); -var _build_production_projects = __webpack_require__(517); +var _build_production_projects = __webpack_require__(516); /***/ }), -/* 517 */ +/* 516 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56664,7 +56652,7 @@ Object.defineProperty(exports, "__esModule", { }); exports.buildProductionProjects = buildProductionProjects; -var _cpy = _interopRequireDefault(__webpack_require__(518)); +var _cpy = _interopRequireDefault(__webpack_require__(517)); var _del = _interopRequireDefault(__webpack_require__(297)); @@ -56808,7 +56796,7 @@ async function copyToBuild(project, opensearchDashboardsRoot, buildRoot) { } /***/ }), -/* 518 */ +/* 517 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56816,14 +56804,14 @@ async function copyToBuild(project, opensearchDashboardsRoot, buildRoot) { const EventEmitter = __webpack_require__(156); const path = __webpack_require__(4); const os = __webpack_require__(121); -const pMap = __webpack_require__(519); -const arrify = __webpack_require__(515); -const globby = __webpack_require__(520); -const hasGlob = __webpack_require__(703); -const cpFile = __webpack_require__(705); -const junk = __webpack_require__(715); -const pFilter = __webpack_require__(716); -const CpyError = __webpack_require__(718); +const pMap = __webpack_require__(518); +const arrify = __webpack_require__(514); +const globby = __webpack_require__(519); +const hasGlob = __webpack_require__(702); +const cpFile = __webpack_require__(704); +const junk = __webpack_require__(714); +const pFilter = __webpack_require__(715); +const CpyError = __webpack_require__(717); const defaultOptions = { ignoreJunk: true @@ -56974,7 +56962,7 @@ module.exports = (source, destination, { /***/ }), -/* 519 */ +/* 518 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57062,17 +57050,17 @@ module.exports = async ( /***/ }), -/* 520 */ +/* 519 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const arrayUnion = __webpack_require__(521); +const arrayUnion = __webpack_require__(520); const glob = __webpack_require__(147); -const fastGlob = __webpack_require__(523); -const dirGlob = __webpack_require__(697); -const gitignore = __webpack_require__(700); +const fastGlob = __webpack_require__(522); +const dirGlob = __webpack_require__(696); +const gitignore = __webpack_require__(699); const DEFAULT_FILTER = () => false; @@ -57217,12 +57205,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 521 */ +/* 520 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(522); +var arrayUniq = __webpack_require__(521); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -57230,7 +57218,7 @@ module.exports = function () { /***/ }), -/* 522 */ +/* 521 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57299,10 +57287,10 @@ if ('Set' in global) { /***/ }), -/* 523 */ +/* 522 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(524); +const pkg = __webpack_require__(523); module.exports = pkg.async; module.exports.default = pkg.async; @@ -57315,375 +57303,375 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 524 */ +/* 523 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(525); -var taskManager = __webpack_require__(526); -var reader_async_1 = __webpack_require__(668); -var reader_stream_1 = __webpack_require__(692); -var reader_sync_1 = __webpack_require__(693); -var arrayUtils = __webpack_require__(695); -var streamUtils = __webpack_require__(696); -/** - * Synchronous API. - */ -function sync(source, opts) { - assertPatternsInput(source); - var works = getWorks(source, reader_sync_1.default, opts); - return arrayUtils.flatten(works); -} -exports.sync = sync; -/** - * Asynchronous API. - */ -function async(source, opts) { - try { - assertPatternsInput(source); - } - catch (error) { - return Promise.reject(error); - } - var works = getWorks(source, reader_async_1.default, opts); - return Promise.all(works).then(arrayUtils.flatten); -} -exports.async = async; -/** - * Stream API. - */ -function stream(source, opts) { - assertPatternsInput(source); - var works = getWorks(source, reader_stream_1.default, opts); - return streamUtils.merge(works); -} -exports.stream = stream; -/** - * Return a set of tasks based on provided patterns. - */ -function generateTasks(source, opts) { - assertPatternsInput(source); - var patterns = [].concat(source); - var options = optionsManager.prepare(opts); - return taskManager.generate(patterns, options); -} -exports.generateTasks = generateTasks; -/** - * Returns a set of works based on provided tasks and class of the reader. - */ -function getWorks(source, _Reader, opts) { - var patterns = [].concat(source); - var options = optionsManager.prepare(opts); - var tasks = taskManager.generate(patterns, options); - var reader = new _Reader(options); - return tasks.map(reader.read, reader); -} -function assertPatternsInput(source) { - if ([].concat(source).every(isString)) { - return; - } - throw new TypeError('Patterns must be a string or an array of strings'); -} -function isString(source) { - /* tslint:disable-next-line strict-type-predicates */ - return typeof source === 'string'; -} + +Object.defineProperty(exports, "__esModule", { value: true }); +var optionsManager = __webpack_require__(524); +var taskManager = __webpack_require__(525); +var reader_async_1 = __webpack_require__(667); +var reader_stream_1 = __webpack_require__(691); +var reader_sync_1 = __webpack_require__(692); +var arrayUtils = __webpack_require__(694); +var streamUtils = __webpack_require__(695); +/** + * Synchronous API. + */ +function sync(source, opts) { + assertPatternsInput(source); + var works = getWorks(source, reader_sync_1.default, opts); + return arrayUtils.flatten(works); +} +exports.sync = sync; +/** + * Asynchronous API. + */ +function async(source, opts) { + try { + assertPatternsInput(source); + } + catch (error) { + return Promise.reject(error); + } + var works = getWorks(source, reader_async_1.default, opts); + return Promise.all(works).then(arrayUtils.flatten); +} +exports.async = async; +/** + * Stream API. + */ +function stream(source, opts) { + assertPatternsInput(source); + var works = getWorks(source, reader_stream_1.default, opts); + return streamUtils.merge(works); +} +exports.stream = stream; +/** + * Return a set of tasks based on provided patterns. + */ +function generateTasks(source, opts) { + assertPatternsInput(source); + var patterns = [].concat(source); + var options = optionsManager.prepare(opts); + return taskManager.generate(patterns, options); +} +exports.generateTasks = generateTasks; +/** + * Returns a set of works based on provided tasks and class of the reader. + */ +function getWorks(source, _Reader, opts) { + var patterns = [].concat(source); + var options = optionsManager.prepare(opts); + var tasks = taskManager.generate(patterns, options); + var reader = new _Reader(options); + return tasks.map(reader.read, reader); +} +function assertPatternsInput(source) { + if ([].concat(source).every(isString)) { + return; + } + throw new TypeError('Patterns must be a string or an array of strings'); +} +function isString(source) { + /* tslint:disable-next-line strict-type-predicates */ + return typeof source === 'string'; +} /***/ }), -/* 525 */ +/* 524 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -function prepare(options) { - var opts = __assign({ cwd: process.cwd(), deep: true, ignore: [], dot: false, stats: false, onlyFiles: true, onlyDirectories: false, followSymlinkedDirectories: true, unique: true, markDirectories: false, absolute: false, nobrace: false, brace: true, noglobstar: false, globstar: true, noext: false, extension: true, nocase: false, case: true, matchBase: false, transform: null }, options); - if (opts.onlyDirectories) { - opts.onlyFiles = false; - } - opts.brace = !opts.nobrace; - opts.globstar = !opts.noglobstar; - opts.extension = !opts.noext; - opts.case = !opts.nocase; - if (options) { - opts.brace = ('brace' in options ? options.brace : opts.brace); - opts.globstar = ('globstar' in options ? options.globstar : opts.globstar); - opts.extension = ('extension' in options ? options.extension : opts.extension); - opts.case = ('case' in options ? options.case : opts.case); - } - return opts; -} -exports.prepare = prepare; + +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +function prepare(options) { + var opts = __assign({ cwd: process.cwd(), deep: true, ignore: [], dot: false, stats: false, onlyFiles: true, onlyDirectories: false, followSymlinkedDirectories: true, unique: true, markDirectories: false, absolute: false, nobrace: false, brace: true, noglobstar: false, globstar: true, noext: false, extension: true, nocase: false, case: true, matchBase: false, transform: null }, options); + if (opts.onlyDirectories) { + opts.onlyFiles = false; + } + opts.brace = !opts.nobrace; + opts.globstar = !opts.noglobstar; + opts.extension = !opts.noext; + opts.case = !opts.nocase; + if (options) { + opts.brace = ('brace' in options ? options.brace : opts.brace); + opts.globstar = ('globstar' in options ? options.globstar : opts.globstar); + opts.extension = ('extension' in options ? options.extension : opts.extension); + opts.case = ('case' in options ? options.case : opts.case); + } + return opts; +} +exports.prepare = prepare; /***/ }), -/* 526 */ +/* 525 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(527); -/** - * Generate tasks based on parent directory of each pattern. - */ -function generate(patterns, options) { - var unixPatterns = patterns.map(patternUtils.unixifyPattern); - var unixIgnore = options.ignore.map(patternUtils.unixifyPattern); - var positivePatterns = getPositivePatterns(unixPatterns); - var negativePatterns = getNegativePatternsAsPositive(unixPatterns, unixIgnore); - /** - * When the `case` option is disabled, all patterns must be marked as dynamic, because we cannot check filepath - * directly (without read directory). - */ - var staticPatterns = !options.case ? [] : positivePatterns.filter(patternUtils.isStaticPattern); - var dynamicPatterns = !options.case ? positivePatterns : positivePatterns.filter(patternUtils.isDynamicPattern); - var staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - var dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -/** - * Convert patterns to tasks based on parent directory of each pattern. - */ -function convertPatternsToTasks(positive, negative, dynamic) { - var positivePatternsGroup = groupPatternsByBaseDirectory(positive); - // When we have a global group – there is no reason to divide the patterns into independent tasks. - // In this case, the global task covers the rest. - if ('.' in positivePatternsGroup) { - var task = convertPatternGroupToTask('.', positive, negative, dynamic); - return [task]; - } - return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); -} -exports.convertPatternsToTasks = convertPatternsToTasks; -/** - * Return only positive patterns. - */ -function getPositivePatterns(patterns) { - return patternUtils.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -/** - * Return only negative patterns. - */ -function getNegativePatternsAsPositive(patterns, ignore) { - var negative = patternUtils.getNegativePatterns(patterns).concat(ignore); - var positive = negative.map(patternUtils.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -/** - * Group patterns by base directory of each pattern. - */ -function groupPatternsByBaseDirectory(patterns) { - return patterns.reduce(function (collection, pattern) { - var base = patternUtils.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, {}); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -/** - * Convert group of patterns to tasks. - */ -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map(function (base) { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -/** - * Create a task for positive and negative patterns. - */ -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - base: base, - dynamic: dynamic, - positive: positive, - negative: negative, - patterns: [].concat(positive, negative.map(patternUtils.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; + +Object.defineProperty(exports, "__esModule", { value: true }); +var patternUtils = __webpack_require__(526); +/** + * Generate tasks based on parent directory of each pattern. + */ +function generate(patterns, options) { + var unixPatterns = patterns.map(patternUtils.unixifyPattern); + var unixIgnore = options.ignore.map(patternUtils.unixifyPattern); + var positivePatterns = getPositivePatterns(unixPatterns); + var negativePatterns = getNegativePatternsAsPositive(unixPatterns, unixIgnore); + /** + * When the `case` option is disabled, all patterns must be marked as dynamic, because we cannot check filepath + * directly (without read directory). + */ + var staticPatterns = !options.case ? [] : positivePatterns.filter(patternUtils.isStaticPattern); + var dynamicPatterns = !options.case ? positivePatterns : positivePatterns.filter(patternUtils.isDynamicPattern); + var staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + var dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +exports.generate = generate; +/** + * Convert patterns to tasks based on parent directory of each pattern. + */ +function convertPatternsToTasks(positive, negative, dynamic) { + var positivePatternsGroup = groupPatternsByBaseDirectory(positive); + // When we have a global group – there is no reason to divide the patterns into independent tasks. + // In this case, the global task covers the rest. + if ('.' in positivePatternsGroup) { + var task = convertPatternGroupToTask('.', positive, negative, dynamic); + return [task]; + } + return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); +} +exports.convertPatternsToTasks = convertPatternsToTasks; +/** + * Return only positive patterns. + */ +function getPositivePatterns(patterns) { + return patternUtils.getPositivePatterns(patterns); +} +exports.getPositivePatterns = getPositivePatterns; +/** + * Return only negative patterns. + */ +function getNegativePatternsAsPositive(patterns, ignore) { + var negative = patternUtils.getNegativePatterns(patterns).concat(ignore); + var positive = negative.map(patternUtils.convertToPositivePattern); + return positive; +} +exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +/** + * Group patterns by base directory of each pattern. + */ +function groupPatternsByBaseDirectory(patterns) { + return patterns.reduce(function (collection, pattern) { + var base = patternUtils.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, {}); +} +exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +/** + * Convert group of patterns to tasks. + */ +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map(function (base) { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +/** + * Create a task for positive and negative patterns. + */ +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + base: base, + dynamic: dynamic, + positive: positive, + negative: negative, + patterns: [].concat(positive, negative.map(patternUtils.convertToNegativePattern)) + }; +} +exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 527 */ +/* 526 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -var globParent = __webpack_require__(309); -var isGlob = __webpack_require__(310); -var micromatch = __webpack_require__(528); -var GLOBSTAR = '**'; -/** - * Return true for static pattern. - */ -function isStaticPattern(pattern) { - return !isDynamicPattern(pattern); -} -exports.isStaticPattern = isStaticPattern; -/** - * Return true for pattern that looks like glob. - */ -function isDynamicPattern(pattern) { - return isGlob(pattern, { strict: false }); -} -exports.isDynamicPattern = isDynamicPattern; -/** - * Convert a windows «path» to a unix-style «path». - */ -function unixifyPattern(pattern) { - return pattern.replace(/\\/g, '/'); -} -exports.unixifyPattern = unixifyPattern; -/** - * Returns negative pattern as positive pattern. - */ -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -/** - * Returns positive pattern as negative pattern. - */ -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -/** - * Return true if provided pattern is negative pattern. - */ -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -/** - * Return true if provided pattern is positive pattern. - */ -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -/** - * Extracts negative patterns from array of patterns. - */ -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -/** - * Extracts positive patterns from array of patterns. - */ -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -/** - * Extract base directory from provided pattern. - */ -function getBaseDirectory(pattern) { - return globParent(pattern); -} -exports.getBaseDirectory = getBaseDirectory; -/** - * Return true if provided pattern has globstar. - */ -function hasGlobStar(pattern) { - return pattern.indexOf(GLOBSTAR) !== -1; -} -exports.hasGlobStar = hasGlobStar; -/** - * Return true if provided pattern ends with slash and globstar. - */ -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -/** - * Returns «true» when pattern ends with a slash and globstar or the last partial of the pattern is static pattern. - */ -function isAffectDepthOfReadingPattern(pattern) { - var basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -/** - * Return naive depth of provided pattern without depth of the base directory. - */ -function getNaiveDepth(pattern) { - var base = getBaseDirectory(pattern); - var patternDepth = pattern.split('/').length; - var patternBaseDepth = base.split('/').length; - /** - * This is a hack for pattern that has no base directory. - * - * This is related to the `*\something\*` pattern. - */ - if (base === '.') { - return patternDepth - patternBaseDepth; - } - return patternDepth - patternBaseDepth - 1; -} -exports.getNaiveDepth = getNaiveDepth; -/** - * Return max naive depth of provided patterns without depth of the base directory. - */ -function getMaxNaivePatternsDepth(patterns) { - return patterns.reduce(function (max, pattern) { - var depth = getNaiveDepth(pattern); - return depth > max ? depth : max; - }, 0); -} -exports.getMaxNaivePatternsDepth = getMaxNaivePatternsDepth; -/** - * Make RegExp for provided pattern. - */ -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -/** - * Convert patterns to regexps. - */ -function convertPatternsToRe(patterns, options) { - return patterns.map(function (pattern) { return makeRe(pattern, options); }); -} -exports.convertPatternsToRe = convertPatternsToRe; -/** - * Returns true if the entry match any of the given RegExp's. - */ -function matchAny(entry, patternsRe) { - return patternsRe.some(function (patternRe) { return patternRe.test(entry); }); -} -exports.matchAny = matchAny; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +var globParent = __webpack_require__(309); +var isGlob = __webpack_require__(310); +var micromatch = __webpack_require__(527); +var GLOBSTAR = '**'; +/** + * Return true for static pattern. + */ +function isStaticPattern(pattern) { + return !isDynamicPattern(pattern); +} +exports.isStaticPattern = isStaticPattern; +/** + * Return true for pattern that looks like glob. + */ +function isDynamicPattern(pattern) { + return isGlob(pattern, { strict: false }); +} +exports.isDynamicPattern = isDynamicPattern; +/** + * Convert a windows «path» to a unix-style «path». + */ +function unixifyPattern(pattern) { + return pattern.replace(/\\/g, '/'); +} +exports.unixifyPattern = unixifyPattern; +/** + * Returns negative pattern as positive pattern. + */ +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +exports.convertToPositivePattern = convertToPositivePattern; +/** + * Returns positive pattern as negative pattern. + */ +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +exports.convertToNegativePattern = convertToNegativePattern; +/** + * Return true if provided pattern is negative pattern. + */ +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +exports.isNegativePattern = isNegativePattern; +/** + * Return true if provided pattern is positive pattern. + */ +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +exports.isPositivePattern = isPositivePattern; +/** + * Extracts negative patterns from array of patterns. + */ +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +exports.getNegativePatterns = getNegativePatterns; +/** + * Extracts positive patterns from array of patterns. + */ +function getPositivePatterns(patterns) { + return patterns.filter(isPositivePattern); +} +exports.getPositivePatterns = getPositivePatterns; +/** + * Extract base directory from provided pattern. + */ +function getBaseDirectory(pattern) { + return globParent(pattern); +} +exports.getBaseDirectory = getBaseDirectory; +/** + * Return true if provided pattern has globstar. + */ +function hasGlobStar(pattern) { + return pattern.indexOf(GLOBSTAR) !== -1; +} +exports.hasGlobStar = hasGlobStar; +/** + * Return true if provided pattern ends with slash and globstar. + */ +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR); +} +exports.endsWithSlashGlobStar = endsWithSlashGlobStar; +/** + * Returns «true» when pattern ends with a slash and globstar or the last partial of the pattern is static pattern. + */ +function isAffectDepthOfReadingPattern(pattern) { + var basename = path.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +/** + * Return naive depth of provided pattern without depth of the base directory. + */ +function getNaiveDepth(pattern) { + var base = getBaseDirectory(pattern); + var patternDepth = pattern.split('/').length; + var patternBaseDepth = base.split('/').length; + /** + * This is a hack for pattern that has no base directory. + * + * This is related to the `*\something\*` pattern. + */ + if (base === '.') { + return patternDepth - patternBaseDepth; + } + return patternDepth - patternBaseDepth - 1; +} +exports.getNaiveDepth = getNaiveDepth; +/** + * Return max naive depth of provided patterns without depth of the base directory. + */ +function getMaxNaivePatternsDepth(patterns) { + return patterns.reduce(function (max, pattern) { + var depth = getNaiveDepth(pattern); + return depth > max ? depth : max; + }, 0); +} +exports.getMaxNaivePatternsDepth = getMaxNaivePatternsDepth; +/** + * Make RegExp for provided pattern. + */ +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +exports.makeRe = makeRe; +/** + * Convert patterns to regexps. + */ +function convertPatternsToRe(patterns, options) { + return patterns.map(function (pattern) { return makeRe(pattern, options); }); +} +exports.convertPatternsToRe = convertPatternsToRe; +/** + * Returns true if the entry match any of the given RegExp's. + */ +function matchAny(entry, patternsRe) { + return patternsRe.some(function (patternRe) { return patternRe.test(entry); }); +} +exports.matchAny = matchAny; /***/ }), -/* 528 */ +/* 527 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57694,18 +57682,18 @@ exports.matchAny = matchAny; */ var util = __webpack_require__(112); -var braces = __webpack_require__(529); -var toRegex = __webpack_require__(530); -var extend = __webpack_require__(633); +var braces = __webpack_require__(528); +var toRegex = __webpack_require__(529); +var extend = __webpack_require__(632); /** * Local dependencies */ -var compilers = __webpack_require__(635); -var parsers = __webpack_require__(664); -var cache = __webpack_require__(665); -var utils = __webpack_require__(666); +var compilers = __webpack_require__(634); +var parsers = __webpack_require__(663); +var cache = __webpack_require__(664); +var utils = __webpack_require__(665); var MAX_LENGTH = 1024 * 64; /** @@ -58567,7 +58555,7 @@ module.exports = micromatch; /***/ }), -/* 529 */ +/* 528 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58577,18 +58565,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(530); -var unique = __webpack_require__(550); -var extend = __webpack_require__(551); +var toRegex = __webpack_require__(529); +var unique = __webpack_require__(549); +var extend = __webpack_require__(550); /** * Local dependencies */ -var compilers = __webpack_require__(553); -var parsers = __webpack_require__(564); -var Braces = __webpack_require__(568); -var utils = __webpack_require__(554); +var compilers = __webpack_require__(552); +var parsers = __webpack_require__(563); +var Braces = __webpack_require__(567); +var utils = __webpack_require__(553); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -58892,16 +58880,16 @@ module.exports = braces; /***/ }), -/* 530 */ +/* 529 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(531); -var define = __webpack_require__(537); -var extend = __webpack_require__(543); -var not = __webpack_require__(547); +var safe = __webpack_require__(530); +var define = __webpack_require__(536); +var extend = __webpack_require__(542); +var not = __webpack_require__(546); var MAX_LENGTH = 1024 * 64; /** @@ -59054,10 +59042,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 531 */ +/* 530 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(532); +var parse = __webpack_require__(531); var types = parse.types; module.exports = function (re, opts) { @@ -59103,13 +59091,13 @@ function isRegExp (x) { /***/ }), -/* 532 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(533); -var types = __webpack_require__(534); -var sets = __webpack_require__(535); -var positions = __webpack_require__(536); +var util = __webpack_require__(532); +var types = __webpack_require__(533); +var sets = __webpack_require__(534); +var positions = __webpack_require__(535); module.exports = function(regexpStr) { @@ -59391,11 +59379,11 @@ module.exports.types = types; /***/ }), -/* 533 */ +/* 532 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(534); -var sets = __webpack_require__(535); +var types = __webpack_require__(533); +var sets = __webpack_require__(534); // All of these are private and only used by randexp. @@ -59508,7 +59496,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 534 */ +/* 533 */ /***/ (function(module, exports) { module.exports = { @@ -59524,10 +59512,10 @@ module.exports = { /***/ }), -/* 535 */ +/* 534 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(534); +var types = __webpack_require__(533); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -59612,10 +59600,10 @@ exports.anyChar = function() { /***/ }), -/* 536 */ +/* 535 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(534); +var types = __webpack_require__(533); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -59635,7 +59623,7 @@ exports.end = function() { /***/ }), -/* 537 */ +/* 536 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59648,8 +59636,8 @@ exports.end = function() { -var isobject = __webpack_require__(538); -var isDescriptor = __webpack_require__(539); +var isobject = __webpack_require__(537); +var isDescriptor = __webpack_require__(538); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -59680,7 +59668,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 538 */ +/* 537 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59699,7 +59687,7 @@ module.exports = function isObject(val) { /***/ }), -/* 539 */ +/* 538 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59712,9 +59700,9 @@ module.exports = function isObject(val) { -var typeOf = __webpack_require__(540); -var isAccessor = __webpack_require__(541); -var isData = __webpack_require__(542); +var typeOf = __webpack_require__(539); +var isAccessor = __webpack_require__(540); +var isData = __webpack_require__(541); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -59728,7 +59716,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 540 */ +/* 539 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -59863,7 +59851,7 @@ function isBuffer(val) { /***/ }), -/* 541 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59876,7 +59864,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(539); // accessor descriptor properties var accessor = { @@ -59939,7 +59927,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 542 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59952,7 +59940,7 @@ module.exports = isAccessorDescriptor; -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(539); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -59995,14 +59983,14 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 543 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(544); -var assignSymbols = __webpack_require__(546); +var isExtendable = __webpack_require__(543); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -60062,7 +60050,7 @@ function isEnum(obj, key) { /***/ }), -/* 544 */ +/* 543 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60075,7 +60063,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(545); +var isPlainObject = __webpack_require__(544); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -60083,7 +60071,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 545 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60096,7 +60084,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(538); +var isObject = __webpack_require__(537); function isObjectObject(o) { return isObject(o) === true @@ -60127,7 +60115,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 546 */ +/* 545 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60174,14 +60162,14 @@ module.exports = function(receiver, objects) { /***/ }), -/* 547 */ +/* 546 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(548); -var safe = __webpack_require__(531); +var extend = __webpack_require__(547); +var safe = __webpack_require__(530); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -60253,14 +60241,14 @@ module.exports = toRegex; /***/ }), -/* 548 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(549); -var assignSymbols = __webpack_require__(546); +var isExtendable = __webpack_require__(548); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -60320,7 +60308,7 @@ function isEnum(obj, key) { /***/ }), -/* 549 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60333,7 +60321,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(545); +var isPlainObject = __webpack_require__(544); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -60341,7 +60329,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 550 */ +/* 549 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60391,13 +60379,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 551 */ +/* 550 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(552); +var isObject = __webpack_require__(551); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -60431,7 +60419,7 @@ function hasOwn(obj, key) { /***/ }), -/* 552 */ +/* 551 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60451,13 +60439,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 553 */ +/* 552 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(554); +var utils = __webpack_require__(553); module.exports = function(braces, options) { braces.compiler @@ -60740,25 +60728,25 @@ function hasQueue(node) { /***/ }), -/* 554 */ +/* 553 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(555); +var splitString = __webpack_require__(554); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(551); -utils.flatten = __webpack_require__(558); -utils.isObject = __webpack_require__(538); -utils.fillRange = __webpack_require__(559); -utils.repeat = __webpack_require__(563); -utils.unique = __webpack_require__(550); +utils.extend = __webpack_require__(550); +utils.flatten = __webpack_require__(557); +utils.isObject = __webpack_require__(537); +utils.fillRange = __webpack_require__(558); +utils.repeat = __webpack_require__(562); +utils.unique = __webpack_require__(549); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -61090,7 +61078,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 555 */ +/* 554 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61103,7 +61091,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(556); +var extend = __webpack_require__(555); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -61268,14 +61256,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 556 */ +/* 555 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(557); -var assignSymbols = __webpack_require__(546); +var isExtendable = __webpack_require__(556); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -61335,7 +61323,7 @@ function isEnum(obj, key) { /***/ }), -/* 557 */ +/* 556 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61348,7 +61336,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(545); +var isPlainObject = __webpack_require__(544); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -61356,7 +61344,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 558 */ +/* 557 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61385,7 +61373,7 @@ function flat(arr, res) { /***/ }), -/* 559 */ +/* 558 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61399,10 +61387,10 @@ function flat(arr, res) { var util = __webpack_require__(112); -var isNumber = __webpack_require__(560); -var extend = __webpack_require__(551); -var repeat = __webpack_require__(561); -var toRegex = __webpack_require__(562); +var isNumber = __webpack_require__(559); +var extend = __webpack_require__(550); +var repeat = __webpack_require__(560); +var toRegex = __webpack_require__(561); /** * Return a range of numbers or letters. @@ -61600,7 +61588,7 @@ module.exports = fillRange; /***/ }), -/* 560 */ +/* 559 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61613,7 +61601,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(539); module.exports = function isNumber(num) { var type = typeOf(num); @@ -61629,7 +61617,7 @@ module.exports = function isNumber(num) { /***/ }), -/* 561 */ +/* 560 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61706,7 +61694,7 @@ function repeat(str, num) { /***/ }), -/* 562 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61719,8 +61707,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(561); -var isNumber = __webpack_require__(560); +var repeat = __webpack_require__(560); +var isNumber = __webpack_require__(559); var cache = {}; function toRegexRange(min, max, options) { @@ -62007,7 +61995,7 @@ module.exports = toRegexRange; /***/ }), -/* 563 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62036,14 +62024,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 564 */ +/* 563 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(565); -var utils = __webpack_require__(554); +var Node = __webpack_require__(564); +var utils = __webpack_require__(553); /** * Braces parsers @@ -62403,15 +62391,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 565 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(538); -var define = __webpack_require__(566); -var utils = __webpack_require__(567); +var isObject = __webpack_require__(537); +var define = __webpack_require__(565); +var utils = __webpack_require__(566); var ownNames; /** @@ -62902,7 +62890,7 @@ exports = module.exports = Node; /***/ }), -/* 566 */ +/* 565 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62915,7 +62903,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(539); +var isDescriptor = __webpack_require__(538); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -62940,13 +62928,13 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 567 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(539); var utils = module.exports; /** @@ -63966,17 +63954,17 @@ function assert(val, message) { /***/ }), -/* 568 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(551); -var Snapdragon = __webpack_require__(569); -var compilers = __webpack_require__(553); -var parsers = __webpack_require__(564); -var utils = __webpack_require__(554); +var extend = __webpack_require__(550); +var Snapdragon = __webpack_require__(568); +var compilers = __webpack_require__(552); +var parsers = __webpack_require__(563); +var utils = __webpack_require__(553); /** * Customize Snapdragon parser and renderer @@ -64077,17 +64065,17 @@ module.exports = Braces; /***/ }), -/* 569 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(570); -var define = __webpack_require__(595); -var Compiler = __webpack_require__(602); -var Parser = __webpack_require__(630); -var utils = __webpack_require__(610); +var Base = __webpack_require__(569); +var define = __webpack_require__(594); +var Compiler = __webpack_require__(601); +var Parser = __webpack_require__(629); +var utils = __webpack_require__(609); var regexCache = {}; var cache = {}; @@ -64258,20 +64246,20 @@ module.exports.Parser = Parser; /***/ }), -/* 570 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var define = __webpack_require__(571); -var CacheBase = __webpack_require__(572); -var Emitter = __webpack_require__(573); -var isObject = __webpack_require__(538); -var merge = __webpack_require__(590); -var pascal = __webpack_require__(593); -var cu = __webpack_require__(594); +var define = __webpack_require__(570); +var CacheBase = __webpack_require__(571); +var Emitter = __webpack_require__(572); +var isObject = __webpack_require__(537); +var merge = __webpack_require__(589); +var pascal = __webpack_require__(592); +var cu = __webpack_require__(593); /** * Optionally define a custom `cache` namespace to use. @@ -64700,7 +64688,7 @@ module.exports.namespace = namespace; /***/ }), -/* 571 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64713,7 +64701,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(539); +var isDescriptor = __webpack_require__(538); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -64738,21 +64726,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 572 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(538); -var Emitter = __webpack_require__(573); -var visit = __webpack_require__(574); -var toPath = __webpack_require__(577); -var union = __webpack_require__(578); -var del = __webpack_require__(582); -var get = __webpack_require__(580); -var has = __webpack_require__(588); -var set = __webpack_require__(581); +var isObject = __webpack_require__(537); +var Emitter = __webpack_require__(572); +var visit = __webpack_require__(573); +var toPath = __webpack_require__(576); +var union = __webpack_require__(577); +var del = __webpack_require__(581); +var get = __webpack_require__(579); +var has = __webpack_require__(587); +var set = __webpack_require__(580); /** * Create a `Cache` constructor that when instantiated will @@ -65006,188 +64994,188 @@ module.exports.namespace = namespace; /***/ }), -/* 573 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { - -/** - * Expose `Emitter`. - */ - -if (true) { - module.exports = Emitter; -} - -/** - * Initialize a new `Emitter`. - * - * @api public - */ - -function Emitter(obj) { - if (obj) return mixin(obj); -}; - -/** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - -function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; -} - -/** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.on = -Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks['$' + event] = this._callbacks['$' + event] || []) - .push(fn); - return this; -}; - -/** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.once = function(event, fn){ - function on() { - this.off(event, on); - fn.apply(this, arguments); - } - - on.fn = fn; - this.on(event, on); - return this; -}; - -/** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.off = -Emitter.prototype.removeListener = -Emitter.prototype.removeAllListeners = -Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } - - // specific event - var callbacks = this._callbacks['$' + event]; - if (!callbacks) return this; - - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks['$' + event]; - return this; - } - - // remove specific handler - var cb; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - if (cb === fn || cb.fn === fn) { - callbacks.splice(i, 1); - break; - } - } - - // Remove event specific arrays for event types that no - // one is subscribed for to avoid memory leak. - if (callbacks.length === 0) { - delete this._callbacks['$' + event]; - } - - return this; -}; - -/** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - -Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - - var args = new Array(arguments.length - 1) - , callbacks = this._callbacks['$' + event]; - - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; -}; - -/** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - -Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks['$' + event] || []; -}; - -/** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - -Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; -}; + +/** + * Expose `Emitter`. + */ + +if (true) { + module.exports = Emitter; +} + +/** + * Initialize a new `Emitter`. + * + * @api public + */ + +function Emitter(obj) { + if (obj) return mixin(obj); +}; + +/** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; +} + +/** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.on = +Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks['$' + event] = this._callbacks['$' + event] || []) + .push(fn); + return this; +}; + +/** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.once = function(event, fn){ + function on() { + this.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; +}; + +/** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.off = +Emitter.prototype.removeListener = +Emitter.prototype.removeAllListeners = +Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks['$' + event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks['$' + event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + + // Remove event specific arrays for event types that no + // one is subscribed for to avoid memory leak. + if (callbacks.length === 0) { + delete this._callbacks['$' + event]; + } + + return this; +}; + +/** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + +Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + + var args = new Array(arguments.length - 1) + , callbacks = this._callbacks['$' + event]; + + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; +}; + +/** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + +Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks['$' + event] || []; +}; + +/** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + +Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; +}; /***/ }), -/* 574 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65200,8 +65188,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(575); -var mapVisit = __webpack_require__(576); +var visit = __webpack_require__(574); +var mapVisit = __webpack_require__(575); module.exports = function(collection, method, val) { var result; @@ -65224,7 +65212,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 575 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65237,7 +65225,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(538); +var isObject = __webpack_require__(537); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -65264,14 +65252,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 576 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var visit = __webpack_require__(575); +var visit = __webpack_require__(574); /** * Map `visit` over an array of objects. @@ -65308,7 +65296,7 @@ function isObject(val) { /***/ }), -/* 577 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65321,7 +65309,7 @@ function isObject(val) { -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(539); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -65348,16 +65336,16 @@ function filter(arr) { /***/ }), -/* 578 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(552); -var union = __webpack_require__(579); -var get = __webpack_require__(580); -var set = __webpack_require__(581); +var isObject = __webpack_require__(551); +var union = __webpack_require__(578); +var get = __webpack_require__(579); +var set = __webpack_require__(580); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -65385,7 +65373,7 @@ function arrayify(val) { /***/ }), -/* 579 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65421,7 +65409,7 @@ module.exports = function union(init) { /***/ }), -/* 580 */ +/* 579 */ /***/ (function(module, exports) { /*! @@ -65477,7 +65465,7 @@ function toString(val) { /***/ }), -/* 581 */ +/* 580 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65490,10 +65478,10 @@ function toString(val) { -var split = __webpack_require__(555); -var extend = __webpack_require__(551); -var isPlainObject = __webpack_require__(545); -var isObject = __webpack_require__(552); +var split = __webpack_require__(554); +var extend = __webpack_require__(550); +var isPlainObject = __webpack_require__(544); +var isObject = __webpack_require__(551); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -65539,7 +65527,7 @@ function isValidKey(key) { /***/ }), -/* 582 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65552,8 +65540,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(583); -var has = __webpack_require__(584); +var isObject = __webpack_require__(582); +var has = __webpack_require__(583); const isUnsafeKey = key => { return key === '__proto__' || key === 'constructor' || key === 'prototype'; @@ -65595,7 +65583,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 583 */ +/* 582 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -65614,7 +65602,7 @@ function isObject(val) { /***/ }), -/* 584 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65627,8 +65615,8 @@ function isObject(val) { -const get = __webpack_require__(585); -const has = __webpack_require__(587); +const get = __webpack_require__(584); +const has = __webpack_require__(586); module.exports = function(obj, path, options) { if (isObject(obj) && (typeof path === 'string' || Array.isArray(path))) { @@ -65643,7 +65631,7 @@ function isObject(val) { /***/ }), -/* 585 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -65653,7 +65641,7 @@ function isObject(val) { * Released under the MIT License. */ -const isObject = __webpack_require__(586); +const isObject = __webpack_require__(585); module.exports = function(target, path, options) { if (!isObject(options)) { @@ -65759,7 +65747,7 @@ function isValidObject(val) { /***/ }), -/* 586 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65778,7 +65766,7 @@ module.exports = function isObject(val) { /***/ }), -/* 587 */ +/* 586 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65791,7 +65779,7 @@ module.exports = function isObject(val) { -const typeOf = __webpack_require__(540); +const typeOf = __webpack_require__(539); module.exports = function has(val) { switch (typeOf(val)) { @@ -65834,7 +65822,7 @@ module.exports = function has(val) { /***/ }), -/* 588 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65847,9 +65835,9 @@ module.exports = function has(val) { -var isObject = __webpack_require__(538); -var hasValues = __webpack_require__(589); -var get = __webpack_require__(580); +var isObject = __webpack_require__(537); +var hasValues = __webpack_require__(588); +var get = __webpack_require__(579); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -65857,7 +65845,7 @@ module.exports = function(val, prop) { /***/ }), -/* 589 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65870,8 +65858,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(540); -var isNumber = __webpack_require__(560); +var typeOf = __webpack_require__(539); +var isNumber = __webpack_require__(559); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -65924,14 +65912,14 @@ module.exports = function hasValue(val) { /***/ }), -/* 590 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(591); -var forIn = __webpack_require__(592); +var isExtendable = __webpack_require__(590); +var forIn = __webpack_require__(591); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -65995,7 +65983,7 @@ module.exports = mixinDeep; /***/ }), -/* 591 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66008,7 +65996,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(545); +var isPlainObject = __webpack_require__(544); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -66016,7 +66004,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 592 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66039,7 +66027,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 593 */ +/* 592 */ /***/ (function(module, exports) { /*! @@ -66066,17 +66054,17 @@ module.exports = pascalcase; /***/ }), -/* 594 */ +/* 593 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var union = __webpack_require__(579); -var define = __webpack_require__(595); -var staticExtend = __webpack_require__(599); -var isObj = __webpack_require__(538); +var union = __webpack_require__(578); +var define = __webpack_require__(594); +var staticExtend = __webpack_require__(598); +var isObj = __webpack_require__(537); /** * Expose class utils @@ -66443,7 +66431,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 595 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66456,7 +66444,7 @@ cu.bubble = function(Parent, events) { -var isDescriptor = __webpack_require__(596); +var isDescriptor = __webpack_require__(595); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -66481,7 +66469,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 596 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66494,9 +66482,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(540); -var isAccessor = __webpack_require__(597); -var isData = __webpack_require__(598); +var typeOf = __webpack_require__(539); +var isAccessor = __webpack_require__(596); +var isData = __webpack_require__(597); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -66510,7 +66498,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 597 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66523,7 +66511,7 @@ module.exports = function isDescriptor(obj, key) { -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(539); // accessor descriptor properties var accessor = { @@ -66586,7 +66574,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 598 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66599,7 +66587,7 @@ module.exports = isAccessorDescriptor; -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(539); // data descriptor properties var data = { @@ -66648,7 +66636,7 @@ module.exports = isDataDescriptor; /***/ }), -/* 599 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66661,8 +66649,8 @@ module.exports = isDataDescriptor; -var copy = __webpack_require__(600); -var define = __webpack_require__(595); +var copy = __webpack_require__(599); +var define = __webpack_require__(594); var util = __webpack_require__(112); /** @@ -66745,15 +66733,15 @@ module.exports = extend; /***/ }), -/* 600 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(540); -var copyDescriptor = __webpack_require__(601); -var define = __webpack_require__(595); +var typeOf = __webpack_require__(539); +var copyDescriptor = __webpack_require__(600); +var define = __webpack_require__(594); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -66926,7 +66914,7 @@ module.exports.has = has; /***/ }), -/* 601 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67014,16 +67002,16 @@ function isObject(val) { /***/ }), -/* 602 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(603); -var define = __webpack_require__(595); -var debug = __webpack_require__(604)('snapdragon:compiler'); -var utils = __webpack_require__(610); +var use = __webpack_require__(602); +var define = __webpack_require__(594); +var debug = __webpack_require__(603)('snapdragon:compiler'); +var utils = __webpack_require__(609); /** * Create a new `Compiler` with the given `options`. @@ -67177,7 +67165,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(629); + var sourcemaps = __webpack_require__(628); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -67198,7 +67186,7 @@ module.exports = Compiler; /***/ }), -/* 603 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67360,7 +67348,7 @@ function define(obj, key, val) { /***/ }), -/* 604 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -67369,14 +67357,14 @@ function define(obj, key, val) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(605); + module.exports = __webpack_require__(604); } else { - module.exports = __webpack_require__(608); + module.exports = __webpack_require__(607); } /***/ }), -/* 605 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -67385,7 +67373,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(606); +exports = module.exports = __webpack_require__(605); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -67567,7 +67555,7 @@ function localstorage() { /***/ }), -/* 606 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { @@ -67583,7 +67571,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(607); +exports.humanize = __webpack_require__(606); /** * The currently active debug mode names, and names to skip. @@ -67775,7 +67763,7 @@ function coerce(val) { /***/ }), -/* 607 */ +/* 606 */ /***/ (function(module, exports) { /** @@ -67933,7 +67921,7 @@ function plural(ms, n, name) { /***/ }), -/* 608 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -67949,7 +67937,7 @@ var util = __webpack_require__(112); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(606); +exports = module.exports = __webpack_require__(605); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -68128,7 +68116,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(609); + var net = __webpack_require__(608); stream = new net.Socket({ fd: fd, readable: false, @@ -68187,13 +68175,13 @@ exports.enable(load()); /***/ }), -/* 609 */ +/* 608 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 610 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68203,9 +68191,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(551); -exports.SourceMap = __webpack_require__(611); -exports.sourceMapResolve = __webpack_require__(622); +exports.extend = __webpack_require__(550); +exports.SourceMap = __webpack_require__(610); +exports.sourceMapResolve = __webpack_require__(621); /** * Convert backslash in the given string to forward slashes @@ -68248,7 +68236,7 @@ exports.last = function(arr, n) { /***/ }), -/* 611 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -68256,13 +68244,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(612).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(618).SourceMapConsumer; -exports.SourceNode = __webpack_require__(621).SourceNode; +exports.SourceMapGenerator = __webpack_require__(611).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(617).SourceMapConsumer; +exports.SourceNode = __webpack_require__(620).SourceNode; /***/ }), -/* 612 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68272,10 +68260,10 @@ exports.SourceNode = __webpack_require__(621).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(613); -var util = __webpack_require__(615); -var ArraySet = __webpack_require__(616).ArraySet; -var MappingList = __webpack_require__(617).MappingList; +var base64VLQ = __webpack_require__(612); +var util = __webpack_require__(614); +var ArraySet = __webpack_require__(615).ArraySet; +var MappingList = __webpack_require__(616).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -68684,7 +68672,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 613 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68724,7 +68712,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(614); +var base64 = __webpack_require__(613); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -68830,7 +68818,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 614 */ +/* 613 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68903,7 +68891,7 @@ exports.decode = function (charCode) { /***/ }), -/* 615 */ +/* 614 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -69326,7 +69314,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 616 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -69336,7 +69324,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(615); +var util = __webpack_require__(614); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -69453,7 +69441,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 617 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -69463,7 +69451,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(615); +var util = __webpack_require__(614); /** * Determine whether mappingB is after mappingA with respect to generated @@ -69538,7 +69526,7 @@ exports.MappingList = MappingList; /***/ }), -/* 618 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -69548,11 +69536,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(615); -var binarySearch = __webpack_require__(619); -var ArraySet = __webpack_require__(616).ArraySet; -var base64VLQ = __webpack_require__(613); -var quickSort = __webpack_require__(620).quickSort; +var util = __webpack_require__(614); +var binarySearch = __webpack_require__(618); +var ArraySet = __webpack_require__(615).ArraySet; +var base64VLQ = __webpack_require__(612); +var quickSort = __webpack_require__(619).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -70626,7 +70614,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 619 */ +/* 618 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -70743,7 +70731,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 620 */ +/* 619 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -70863,7 +70851,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 621 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -70873,8 +70861,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(612).SourceMapGenerator; -var util = __webpack_require__(615); +var SourceMapGenerator = __webpack_require__(611).SourceMapGenerator; +var util = __webpack_require__(614); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -71282,15 +71270,15 @@ exports.SourceNode = SourceNode; /***/ }), -/* 622 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { -var sourceMappingURL = __webpack_require__(623) +var sourceMappingURL = __webpack_require__(622) -var resolveUrl = __webpack_require__(624) -var decodeUriComponent = __webpack_require__(625) -var urix = __webpack_require__(627) -var atob = __webpack_require__(628) +var resolveUrl = __webpack_require__(623) +var decodeUriComponent = __webpack_require__(624) +var urix = __webpack_require__(626) +var atob = __webpack_require__(627) @@ -71630,7 +71618,7 @@ module.exports = { /***/ }), -/* 623 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -71693,7 +71681,7 @@ void (function(root, factory) { /***/ }), -/* 624 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { var url = __webpack_require__(200) @@ -71708,10 +71696,10 @@ module.exports = resolveUrl /***/ }), -/* 625 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { -var decodeUriComponent = __webpack_require__(626) +var decodeUriComponent = __webpack_require__(625) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -71722,7 +71710,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 626 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71823,30 +71811,30 @@ module.exports = function (encodedURI) { /***/ }), -/* 627 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { -// Copyright 2014 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) - -var path = __webpack_require__(4) - -"use strict" - -function urix(aPath) { - if (path.sep === "\\") { - return aPath - .replace(/\\/g, "/") - .replace(/^[a-z]:\/?/i, "/") - } - return aPath -} - -module.exports = urix +// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) + +var path = __webpack_require__(4) + +"use strict" + +function urix(aPath) { + if (path.sep === "\\") { + return aPath + .replace(/\\/g, "/") + .replace(/^[a-z]:\/?/i, "/") + } + return aPath +} + +module.exports = urix /***/ }), -/* 628 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71860,7 +71848,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 629 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71868,8 +71856,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(134); var path = __webpack_require__(4); -var define = __webpack_require__(595); -var utils = __webpack_require__(610); +var define = __webpack_require__(594); +var utils = __webpack_require__(609); /** * Expose `mixin()`. @@ -72012,19 +72000,19 @@ exports.comment = function(node) { /***/ }), -/* 630 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(603); +var use = __webpack_require__(602); var util = __webpack_require__(112); -var Cache = __webpack_require__(631); -var define = __webpack_require__(595); -var debug = __webpack_require__(604)('snapdragon:parser'); -var Position = __webpack_require__(632); -var utils = __webpack_require__(610); +var Cache = __webpack_require__(630); +var define = __webpack_require__(594); +var debug = __webpack_require__(603)('snapdragon:parser'); +var Position = __webpack_require__(631); +var utils = __webpack_require__(609); /** * Create a new `Parser` with the given `input` and `options`. @@ -72552,7 +72540,7 @@ module.exports = Parser; /***/ }), -/* 631 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72659,13 +72647,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 632 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(595); +var define = __webpack_require__(594); /** * Store position for a node @@ -72680,14 +72668,14 @@ module.exports = function Position(start, parser) { /***/ }), -/* 633 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(634); -var assignSymbols = __webpack_require__(546); +var isExtendable = __webpack_require__(633); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -72747,7 +72735,7 @@ function isEnum(obj, key) { /***/ }), -/* 634 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72760,7 +72748,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(545); +var isPlainObject = __webpack_require__(544); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -72768,14 +72756,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 635 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(636); -var extglob = __webpack_require__(648); +var nanomatch = __webpack_require__(635); +var extglob = __webpack_require__(647); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -72852,7 +72840,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 636 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72863,17 +72851,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(112); -var toRegex = __webpack_require__(530); -var extend = __webpack_require__(637); +var toRegex = __webpack_require__(529); +var extend = __webpack_require__(636); /** * Local dependencies */ -var compilers = __webpack_require__(639); -var parsers = __webpack_require__(640); -var cache = __webpack_require__(641); -var utils = __webpack_require__(643); +var compilers = __webpack_require__(638); +var parsers = __webpack_require__(639); +var cache = __webpack_require__(640); +var utils = __webpack_require__(642); var MAX_LENGTH = 1024 * 64; /** @@ -73697,14 +73685,14 @@ module.exports = nanomatch; /***/ }), -/* 637 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(638); -var assignSymbols = __webpack_require__(546); +var isExtendable = __webpack_require__(637); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -73764,7 +73752,7 @@ function isEnum(obj, key) { /***/ }), -/* 638 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73777,7 +73765,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(545); +var isPlainObject = __webpack_require__(544); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -73785,7 +73773,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 639 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74131,14 +74119,14 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 640 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(547); -var toRegex = __webpack_require__(530); +var regexNot = __webpack_require__(546); +var toRegex = __webpack_require__(529); /** * Characters to use in negation regex (we want to "not" match @@ -74524,14 +74512,14 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 641 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(642))(); +module.exports = new (__webpack_require__(641))(); /***/ }), -/* 642 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74544,7 +74532,7 @@ module.exports = new (__webpack_require__(642))(); -var MapCache = __webpack_require__(631); +var MapCache = __webpack_require__(630); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -74666,7 +74654,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 643 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74679,14 +74667,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(644)(); -var Snapdragon = __webpack_require__(569); -utils.define = __webpack_require__(645); -utils.diff = __webpack_require__(646); -utils.extend = __webpack_require__(637); -utils.pick = __webpack_require__(647); -utils.typeOf = __webpack_require__(540); -utils.unique = __webpack_require__(550); +var isWindows = __webpack_require__(643)(); +var Snapdragon = __webpack_require__(568); +utils.define = __webpack_require__(644); +utils.diff = __webpack_require__(645); +utils.extend = __webpack_require__(636); +utils.pick = __webpack_require__(646); +utils.typeOf = __webpack_require__(539); +utils.unique = __webpack_require__(549); /** * Returns true if the given value is effectively an empty string @@ -75052,7 +75040,7 @@ utils.unixify = function(options) { /***/ }), -/* 644 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -75080,7 +75068,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 645 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75093,8 +75081,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(538); -var isDescriptor = __webpack_require__(539); +var isobject = __webpack_require__(537); +var isDescriptor = __webpack_require__(538); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -75125,7 +75113,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 646 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75179,7 +75167,7 @@ function diffArray(one, two) { /***/ }), -/* 647 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75192,7 +75180,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(538); +var isObject = __webpack_require__(537); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -75221,7 +75209,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 648 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75231,18 +75219,18 @@ module.exports = function pick(obj, keys) { * Module dependencies */ -var extend = __webpack_require__(551); -var unique = __webpack_require__(550); -var toRegex = __webpack_require__(530); +var extend = __webpack_require__(550); +var unique = __webpack_require__(549); +var toRegex = __webpack_require__(529); /** * Local dependencies */ -var compilers = __webpack_require__(649); -var parsers = __webpack_require__(660); -var Extglob = __webpack_require__(663); -var utils = __webpack_require__(662); +var compilers = __webpack_require__(648); +var parsers = __webpack_require__(659); +var Extglob = __webpack_require__(662); +var utils = __webpack_require__(661); var MAX_LENGTH = 1024 * 64; /** @@ -75559,13 +75547,13 @@ module.exports = extglob; /***/ }), -/* 649 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(650); +var brackets = __webpack_require__(649); /** * Extglob compilers @@ -75735,7 +75723,7 @@ module.exports = function(extglob) { /***/ }), -/* 650 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75745,17 +75733,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(651); -var parsers = __webpack_require__(653); +var compilers = __webpack_require__(650); +var parsers = __webpack_require__(652); /** * Module dependencies */ -var debug = __webpack_require__(655)('expand-brackets'); -var extend = __webpack_require__(551); -var Snapdragon = __webpack_require__(569); -var toRegex = __webpack_require__(530); +var debug = __webpack_require__(654)('expand-brackets'); +var extend = __webpack_require__(550); +var Snapdragon = __webpack_require__(568); +var toRegex = __webpack_require__(529); /** * Parses the given POSIX character class `pattern` and returns a @@ -75953,13 +75941,13 @@ module.exports = brackets; /***/ }), -/* 651 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(652); +var posix = __webpack_require__(651); module.exports = function(brackets) { brackets.compiler @@ -76047,7 +76035,7 @@ module.exports = function(brackets) { /***/ }), -/* 652 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76076,14 +76064,14 @@ module.exports = { /***/ }), -/* 653 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(654); -var define = __webpack_require__(595); +var utils = __webpack_require__(653); +var define = __webpack_require__(594); /** * Text regex @@ -76302,14 +76290,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 654 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(530); -var regexNot = __webpack_require__(547); +var toRegex = __webpack_require__(529); +var regexNot = __webpack_require__(546); var cached; /** @@ -76343,7 +76331,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 655 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -76352,14 +76340,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(656); + module.exports = __webpack_require__(655); } else { - module.exports = __webpack_require__(659); + module.exports = __webpack_require__(658); } /***/ }), -/* 656 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -76368,7 +76356,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(657); +exports = module.exports = __webpack_require__(656); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -76550,7 +76538,7 @@ function localstorage() { /***/ }), -/* 657 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { @@ -76566,7 +76554,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(658); +exports.humanize = __webpack_require__(657); /** * The currently active debug mode names, and names to skip. @@ -76758,7 +76746,7 @@ function coerce(val) { /***/ }), -/* 658 */ +/* 657 */ /***/ (function(module, exports) { /** @@ -76916,7 +76904,7 @@ function plural(ms, n, name) { /***/ }), -/* 659 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -76932,7 +76920,7 @@ var util = __webpack_require__(112); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(657); +exports = module.exports = __webpack_require__(656); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -77111,7 +77099,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(609); + var net = __webpack_require__(608); stream = new net.Socket({ fd: fd, readable: false, @@ -77170,15 +77158,15 @@ exports.enable(load()); /***/ }), -/* 660 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(650); -var define = __webpack_require__(661); -var utils = __webpack_require__(662); +var brackets = __webpack_require__(649); +var define = __webpack_require__(660); +var utils = __webpack_require__(661); /** * Characters to use in text regex (we want to "not" match @@ -77333,7 +77321,7 @@ module.exports = parsers; /***/ }), -/* 661 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77346,7 +77334,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(539); +var isDescriptor = __webpack_require__(538); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -77371,14 +77359,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 662 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(547); -var Cache = __webpack_require__(642); +var regex = __webpack_require__(546); +var Cache = __webpack_require__(641); /** * Utils @@ -77447,7 +77435,7 @@ utils.createRegex = function(str) { /***/ }), -/* 663 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77457,16 +77445,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(569); -var define = __webpack_require__(661); -var extend = __webpack_require__(551); +var Snapdragon = __webpack_require__(568); +var define = __webpack_require__(660); +var extend = __webpack_require__(550); /** * Local dependencies */ -var compilers = __webpack_require__(649); -var parsers = __webpack_require__(660); +var compilers = __webpack_require__(648); +var parsers = __webpack_require__(659); /** * Customize Snapdragon parser and renderer @@ -77532,16 +77520,16 @@ module.exports = Extglob; /***/ }), -/* 664 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(648); -var nanomatch = __webpack_require__(636); -var regexNot = __webpack_require__(547); -var toRegex = __webpack_require__(530); +var extglob = __webpack_require__(647); +var nanomatch = __webpack_require__(635); +var regexNot = __webpack_require__(546); +var toRegex = __webpack_require__(529); var not; /** @@ -77622,14 +77610,14 @@ function textRegex(pattern) { /***/ }), -/* 665 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(642))(); +module.exports = new (__webpack_require__(641))(); /***/ }), -/* 666 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77642,13 +77630,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(569); -utils.define = __webpack_require__(667); -utils.diff = __webpack_require__(646); -utils.extend = __webpack_require__(633); -utils.pick = __webpack_require__(647); -utils.typeOf = __webpack_require__(540); -utils.unique = __webpack_require__(550); +var Snapdragon = __webpack_require__(568); +utils.define = __webpack_require__(666); +utils.diff = __webpack_require__(645); +utils.extend = __webpack_require__(632); +utils.pick = __webpack_require__(646); +utils.typeOf = __webpack_require__(539); +utils.unique = __webpack_require__(549); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -77945,7 +77933,7 @@ utils.unixify = function(options) { /***/ }), -/* 667 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77958,8 +77946,8 @@ utils.unixify = function(options) { -var isobject = __webpack_require__(538); -var isDescriptor = __webpack_require__(539); +var isobject = __webpack_require__(537); +var isDescriptor = __webpack_require__(538); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -77990,97 +77978,97 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 668 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(669); -var reader_1 = __webpack_require__(682); -var fs_stream_1 = __webpack_require__(686); -var ReaderAsync = /** @class */ (function (_super) { - __extends(ReaderAsync, _super); - function ReaderAsync() { - return _super !== null && _super.apply(this, arguments) || this; - } - Object.defineProperty(ReaderAsync.prototype, "fsAdapter", { - /** - * Returns FileSystem adapter. - */ - get: function () { - return new fs_stream_1.default(this.options); - }, - enumerable: true, - configurable: true - }); - /** - * Use async API to read entries for Task. - */ - ReaderAsync.prototype.read = function (task) { - var _this = this; - var root = this.getRootDirectory(task); - var options = this.getReaderOptions(task); - var entries = []; - return new Promise(function (resolve, reject) { - var stream = _this.api(root, task, options); - stream.on('error', function (err) { - _this.isEnoentCodeError(err) ? resolve([]) : reject(err); - stream.pause(); - }); - stream.on('data', function (entry) { return entries.push(_this.transform(entry)); }); - stream.on('end', function () { return resolve(entries); }); - }); - }; - /** - * Returns founded paths. - */ - ReaderAsync.prototype.api = function (root, task, options) { - if (task.dynamic) { - return this.dynamicApi(root, options); - } - return this.staticApi(task, options); - }; - /** - * Api for dynamic tasks. - */ - ReaderAsync.prototype.dynamicApi = function (root, options) { - return readdir.readdirStreamStat(root, options); - }; - /** - * Api for static tasks. - */ - ReaderAsync.prototype.staticApi = function (task, options) { - return this.fsAdapter.read(task.patterns, options.filter); - }; - return ReaderAsync; -}(reader_1.default)); -exports.default = ReaderAsync; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var readdir = __webpack_require__(668); +var reader_1 = __webpack_require__(681); +var fs_stream_1 = __webpack_require__(685); +var ReaderAsync = /** @class */ (function (_super) { + __extends(ReaderAsync, _super); + function ReaderAsync() { + return _super !== null && _super.apply(this, arguments) || this; + } + Object.defineProperty(ReaderAsync.prototype, "fsAdapter", { + /** + * Returns FileSystem adapter. + */ + get: function () { + return new fs_stream_1.default(this.options); + }, + enumerable: true, + configurable: true + }); + /** + * Use async API to read entries for Task. + */ + ReaderAsync.prototype.read = function (task) { + var _this = this; + var root = this.getRootDirectory(task); + var options = this.getReaderOptions(task); + var entries = []; + return new Promise(function (resolve, reject) { + var stream = _this.api(root, task, options); + stream.on('error', function (err) { + _this.isEnoentCodeError(err) ? resolve([]) : reject(err); + stream.pause(); + }); + stream.on('data', function (entry) { return entries.push(_this.transform(entry)); }); + stream.on('end', function () { return resolve(entries); }); + }); + }; + /** + * Returns founded paths. + */ + ReaderAsync.prototype.api = function (root, task, options) { + if (task.dynamic) { + return this.dynamicApi(root, options); + } + return this.staticApi(task, options); + }; + /** + * Api for dynamic tasks. + */ + ReaderAsync.prototype.dynamicApi = function (root, options) { + return readdir.readdirStreamStat(root, options); + }; + /** + * Api for static tasks. + */ + ReaderAsync.prototype.staticApi = function (task, options) { + return this.fsAdapter.read(task.patterns, options.filter); + }; + return ReaderAsync; +}(reader_1.default)); +exports.default = ReaderAsync; /***/ }), -/* 669 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(670); -const readdirAsync = __webpack_require__(678); -const readdirStream = __webpack_require__(681); +const readdirSync = __webpack_require__(669); +const readdirAsync = __webpack_require__(677); +const readdirStream = __webpack_require__(680); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -78164,7 +78152,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 670 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78172,11 +78160,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(671); +const DirectoryReader = __webpack_require__(670); let syncFacade = { - fs: __webpack_require__(676), - forEach: __webpack_require__(677), + fs: __webpack_require__(675), + forEach: __webpack_require__(676), sync: true }; @@ -78205,7 +78193,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 671 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78214,9 +78202,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(138).Readable; const EventEmitter = __webpack_require__(156).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(672); -const stat = __webpack_require__(674); -const call = __webpack_require__(675); +const normalizeOptions = __webpack_require__(671); +const stat = __webpack_require__(673); +const call = __webpack_require__(674); /** * Asynchronously reads the contents of a directory and streams the results @@ -78592,14 +78580,14 @@ module.exports = DirectoryReader; /***/ }), -/* 672 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(673); +const globToRegExp = __webpack_require__(672); module.exports = normalizeOptions; @@ -78776,7 +78764,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 673 */ +/* 672 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -78913,13 +78901,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 674 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(675); +const call = __webpack_require__(674); module.exports = stat; @@ -78994,7 +78982,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 675 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79055,14 +79043,14 @@ function callOnce (fn) { /***/ }), -/* 676 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const call = __webpack_require__(675); +const call = __webpack_require__(674); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -79126,7 +79114,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 677 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79155,7 +79143,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 678 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79163,12 +79151,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(679); -const DirectoryReader = __webpack_require__(671); +const maybe = __webpack_require__(678); +const DirectoryReader = __webpack_require__(670); let asyncFacade = { fs: __webpack_require__(134), - forEach: __webpack_require__(680), + forEach: __webpack_require__(679), async: true }; @@ -79210,7 +79198,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 679 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79237,7 +79225,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 680 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79273,7 +79261,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 681 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79281,11 +79269,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(671); +const DirectoryReader = __webpack_require__(670); let streamFacade = { fs: __webpack_require__(134), - forEach: __webpack_require__(680), + forEach: __webpack_require__(679), async: true }; @@ -79305,373 +79293,373 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 682 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -var deep_1 = __webpack_require__(683); -var entry_1 = __webpack_require__(685); -var pathUtil = __webpack_require__(684); -var Reader = /** @class */ (function () { - function Reader(options) { - this.options = options; - this.micromatchOptions = this.getMicromatchOptions(); - this.entryFilter = new entry_1.default(options, this.micromatchOptions); - this.deepFilter = new deep_1.default(options, this.micromatchOptions); - } - /** - * Returns root path to scanner. - */ - Reader.prototype.getRootDirectory = function (task) { - return path.resolve(this.options.cwd, task.base); - }; - /** - * Returns options for reader. - */ - Reader.prototype.getReaderOptions = function (task) { - return { - basePath: task.base === '.' ? '' : task.base, - filter: this.entryFilter.getFilter(task.positive, task.negative), - deep: this.deepFilter.getFilter(task.positive, task.negative), - sep: '/' - }; - }; - /** - * Returns options for micromatch. - */ - Reader.prototype.getMicromatchOptions = function () { - return { - dot: this.options.dot, - nobrace: !this.options.brace, - noglobstar: !this.options.globstar, - noext: !this.options.extension, - nocase: !this.options.case, - matchBase: this.options.matchBase - }; - }; - /** - * Returns transformed entry. - */ - Reader.prototype.transform = function (entry) { - if (this.options.absolute) { - entry.path = pathUtil.makeAbsolute(this.options.cwd, entry.path); - } - if (this.options.markDirectories && entry.isDirectory()) { - entry.path += '/'; - } - var item = this.options.stats ? entry : entry.path; - if (this.options.transform === null) { - return item; - } - return this.options.transform(item); - }; - /** - * Returns true if error has ENOENT code. - */ - Reader.prototype.isEnoentCodeError = function (err) { - return err.code === 'ENOENT'; - }; - return Reader; -}()); -exports.default = Reader; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +var deep_1 = __webpack_require__(682); +var entry_1 = __webpack_require__(684); +var pathUtil = __webpack_require__(683); +var Reader = /** @class */ (function () { + function Reader(options) { + this.options = options; + this.micromatchOptions = this.getMicromatchOptions(); + this.entryFilter = new entry_1.default(options, this.micromatchOptions); + this.deepFilter = new deep_1.default(options, this.micromatchOptions); + } + /** + * Returns root path to scanner. + */ + Reader.prototype.getRootDirectory = function (task) { + return path.resolve(this.options.cwd, task.base); + }; + /** + * Returns options for reader. + */ + Reader.prototype.getReaderOptions = function (task) { + return { + basePath: task.base === '.' ? '' : task.base, + filter: this.entryFilter.getFilter(task.positive, task.negative), + deep: this.deepFilter.getFilter(task.positive, task.negative), + sep: '/' + }; + }; + /** + * Returns options for micromatch. + */ + Reader.prototype.getMicromatchOptions = function () { + return { + dot: this.options.dot, + nobrace: !this.options.brace, + noglobstar: !this.options.globstar, + noext: !this.options.extension, + nocase: !this.options.case, + matchBase: this.options.matchBase + }; + }; + /** + * Returns transformed entry. + */ + Reader.prototype.transform = function (entry) { + if (this.options.absolute) { + entry.path = pathUtil.makeAbsolute(this.options.cwd, entry.path); + } + if (this.options.markDirectories && entry.isDirectory()) { + entry.path += '/'; + } + var item = this.options.stats ? entry : entry.path; + if (this.options.transform === null) { + return item; + } + return this.options.transform(item); + }; + /** + * Returns true if error has ENOENT code. + */ + Reader.prototype.isEnoentCodeError = function (err) { + return err.code === 'ENOENT'; + }; + return Reader; +}()); +exports.default = Reader; /***/ }), -/* 683 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(684); -var patternUtils = __webpack_require__(527); -var DeepFilter = /** @class */ (function () { - function DeepFilter(options, micromatchOptions) { - this.options = options; - this.micromatchOptions = micromatchOptions; - } - /** - * Returns filter for directories. - */ - DeepFilter.prototype.getFilter = function (positive, negative) { - var _this = this; - var maxPatternDepth = this.getMaxPatternDepth(positive); - var negativeRe = this.getNegativePatternsRe(negative); - return function (entry) { return _this.filter(entry, negativeRe, maxPatternDepth); }; - }; - /** - * Returns max depth of the provided patterns. - */ - DeepFilter.prototype.getMaxPatternDepth = function (patterns) { - var globstar = patterns.some(patternUtils.hasGlobStar); - return globstar ? Infinity : patternUtils.getMaxNaivePatternsDepth(patterns); - }; - /** - * Returns RegExp's for patterns that can affect the depth of reading. - */ - DeepFilter.prototype.getNegativePatternsRe = function (patterns) { - var affectDepthOfReadingPatterns = patterns.filter(patternUtils.isAffectDepthOfReadingPattern); - return patternUtils.convertPatternsToRe(affectDepthOfReadingPatterns, this.micromatchOptions); - }; - /** - * Returns «true» for directory that should be read. - */ - DeepFilter.prototype.filter = function (entry, negativeRe, maxPatternDepth) { - if (this.isSkippedByDeepOption(entry.depth)) { - return false; - } - if (this.isSkippedByMaxPatternDepth(entry.depth, maxPatternDepth)) { - return false; - } - if (this.isSkippedSymlinkedDirectory(entry)) { - return false; - } - if (this.isSkippedDotDirectory(entry)) { - return false; - } - return this.isSkippedByNegativePatterns(entry, negativeRe); - }; - /** - * Returns «true» when the «deep» option is disabled or number and depth of the entry is greater that the option value. - */ - DeepFilter.prototype.isSkippedByDeepOption = function (entryDepth) { - return !this.options.deep || (typeof this.options.deep === 'number' && entryDepth >= this.options.deep); - }; - /** - * Returns «true» when depth parameter is not an Infinity and entry depth greater that the parameter value. - */ - DeepFilter.prototype.isSkippedByMaxPatternDepth = function (entryDepth, maxPatternDepth) { - return maxPatternDepth !== Infinity && entryDepth >= maxPatternDepth; - }; - /** - * Returns «true» for symlinked directory if the «followSymlinkedDirectories» option is disabled. - */ - DeepFilter.prototype.isSkippedSymlinkedDirectory = function (entry) { - return !this.options.followSymlinkedDirectories && entry.isSymbolicLink(); - }; - /** - * Returns «true» for a directory whose name starts with a period if «dot» option is disabled. - */ - DeepFilter.prototype.isSkippedDotDirectory = function (entry) { - return !this.options.dot && pathUtils.isDotDirectory(entry.path); - }; - /** - * Returns «true» for a directory whose path math to any negative pattern. - */ - DeepFilter.prototype.isSkippedByNegativePatterns = function (entry, negativeRe) { - return !patternUtils.matchAny(entry.path, negativeRe); - }; - return DeepFilter; -}()); -exports.default = DeepFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +var pathUtils = __webpack_require__(683); +var patternUtils = __webpack_require__(526); +var DeepFilter = /** @class */ (function () { + function DeepFilter(options, micromatchOptions) { + this.options = options; + this.micromatchOptions = micromatchOptions; + } + /** + * Returns filter for directories. + */ + DeepFilter.prototype.getFilter = function (positive, negative) { + var _this = this; + var maxPatternDepth = this.getMaxPatternDepth(positive); + var negativeRe = this.getNegativePatternsRe(negative); + return function (entry) { return _this.filter(entry, negativeRe, maxPatternDepth); }; + }; + /** + * Returns max depth of the provided patterns. + */ + DeepFilter.prototype.getMaxPatternDepth = function (patterns) { + var globstar = patterns.some(patternUtils.hasGlobStar); + return globstar ? Infinity : patternUtils.getMaxNaivePatternsDepth(patterns); + }; + /** + * Returns RegExp's for patterns that can affect the depth of reading. + */ + DeepFilter.prototype.getNegativePatternsRe = function (patterns) { + var affectDepthOfReadingPatterns = patterns.filter(patternUtils.isAffectDepthOfReadingPattern); + return patternUtils.convertPatternsToRe(affectDepthOfReadingPatterns, this.micromatchOptions); + }; + /** + * Returns «true» for directory that should be read. + */ + DeepFilter.prototype.filter = function (entry, negativeRe, maxPatternDepth) { + if (this.isSkippedByDeepOption(entry.depth)) { + return false; + } + if (this.isSkippedByMaxPatternDepth(entry.depth, maxPatternDepth)) { + return false; + } + if (this.isSkippedSymlinkedDirectory(entry)) { + return false; + } + if (this.isSkippedDotDirectory(entry)) { + return false; + } + return this.isSkippedByNegativePatterns(entry, negativeRe); + }; + /** + * Returns «true» when the «deep» option is disabled or number and depth of the entry is greater that the option value. + */ + DeepFilter.prototype.isSkippedByDeepOption = function (entryDepth) { + return !this.options.deep || (typeof this.options.deep === 'number' && entryDepth >= this.options.deep); + }; + /** + * Returns «true» when depth parameter is not an Infinity and entry depth greater that the parameter value. + */ + DeepFilter.prototype.isSkippedByMaxPatternDepth = function (entryDepth, maxPatternDepth) { + return maxPatternDepth !== Infinity && entryDepth >= maxPatternDepth; + }; + /** + * Returns «true» for symlinked directory if the «followSymlinkedDirectories» option is disabled. + */ + DeepFilter.prototype.isSkippedSymlinkedDirectory = function (entry) { + return !this.options.followSymlinkedDirectories && entry.isSymbolicLink(); + }; + /** + * Returns «true» for a directory whose name starts with a period if «dot» option is disabled. + */ + DeepFilter.prototype.isSkippedDotDirectory = function (entry) { + return !this.options.dot && pathUtils.isDotDirectory(entry.path); + }; + /** + * Returns «true» for a directory whose path math to any negative pattern. + */ + DeepFilter.prototype.isSkippedByNegativePatterns = function (entry, negativeRe) { + return !patternUtils.matchAny(entry.path, negativeRe); + }; + return DeepFilter; +}()); +exports.default = DeepFilter; /***/ }), -/* 684 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -/** - * Returns «true» if the last partial of the path starting with a period. - */ -function isDotDirectory(filepath) { - return path.basename(filepath).startsWith('.'); -} -exports.isDotDirectory = isDotDirectory; -/** - * Convert a windows-like path to a unix-style path. - */ -function normalize(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.normalize = normalize; -/** - * Returns normalized absolute path of provided filepath. - */ -function makeAbsolute(cwd, filepath) { - return normalize(path.resolve(cwd, filepath)); -} -exports.makeAbsolute = makeAbsolute; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +/** + * Returns «true» if the last partial of the path starting with a period. + */ +function isDotDirectory(filepath) { + return path.basename(filepath).startsWith('.'); +} +exports.isDotDirectory = isDotDirectory; +/** + * Convert a windows-like path to a unix-style path. + */ +function normalize(filepath) { + return filepath.replace(/\\/g, '/'); +} +exports.normalize = normalize; +/** + * Returns normalized absolute path of provided filepath. + */ +function makeAbsolute(cwd, filepath) { + return normalize(path.resolve(cwd, filepath)); +} +exports.makeAbsolute = makeAbsolute; /***/ }), -/* 685 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(684); -var patternUtils = __webpack_require__(527); -var EntryFilter = /** @class */ (function () { - function EntryFilter(options, micromatchOptions) { - this.options = options; - this.micromatchOptions = micromatchOptions; - this.index = new Map(); - } - /** - * Returns filter for directories. - */ - EntryFilter.prototype.getFilter = function (positive, negative) { - var _this = this; - var positiveRe = patternUtils.convertPatternsToRe(positive, this.micromatchOptions); - var negativeRe = patternUtils.convertPatternsToRe(negative, this.micromatchOptions); - return function (entry) { return _this.filter(entry, positiveRe, negativeRe); }; - }; - /** - * Returns true if entry must be added to result. - */ - EntryFilter.prototype.filter = function (entry, positiveRe, negativeRe) { - // Exclude duplicate results - if (this.options.unique) { - if (this.isDuplicateEntry(entry)) { - return false; - } - this.createIndexRecord(entry); - } - // Filter files and directories by options - if (this.onlyFileFilter(entry) || this.onlyDirectoryFilter(entry)) { - return false; - } - if (this.isSkippedByAbsoluteNegativePatterns(entry, negativeRe)) { - return false; - } - return this.isMatchToPatterns(entry.path, positiveRe) && !this.isMatchToPatterns(entry.path, negativeRe); - }; - /** - * Return true if the entry already has in the cross reader index. - */ - EntryFilter.prototype.isDuplicateEntry = function (entry) { - return this.index.has(entry.path); - }; - /** - * Create record in the cross reader index. - */ - EntryFilter.prototype.createIndexRecord = function (entry) { - this.index.set(entry.path, undefined); - }; - /** - * Returns true for non-files if the «onlyFiles» option is enabled. - */ - EntryFilter.prototype.onlyFileFilter = function (entry) { - return this.options.onlyFiles && !entry.isFile(); - }; - /** - * Returns true for non-directories if the «onlyDirectories» option is enabled. - */ - EntryFilter.prototype.onlyDirectoryFilter = function (entry) { - return this.options.onlyDirectories && !entry.isDirectory(); - }; - /** - * Return true when `absolute` option is enabled and matched to the negative patterns. - */ - EntryFilter.prototype.isSkippedByAbsoluteNegativePatterns = function (entry, negativeRe) { - if (!this.options.absolute) { - return false; - } - var fullpath = pathUtils.makeAbsolute(this.options.cwd, entry.path); - return this.isMatchToPatterns(fullpath, negativeRe); - }; - /** - * Return true when entry match to provided patterns. - * - * First, just trying to apply patterns to the path. - * Second, trying to apply patterns to the path with final slash (need to micromatch to support «directory/**» patterns). - */ - EntryFilter.prototype.isMatchToPatterns = function (filepath, patternsRe) { - return patternUtils.matchAny(filepath, patternsRe) || patternUtils.matchAny(filepath + '/', patternsRe); - }; - return EntryFilter; -}()); -exports.default = EntryFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +var pathUtils = __webpack_require__(683); +var patternUtils = __webpack_require__(526); +var EntryFilter = /** @class */ (function () { + function EntryFilter(options, micromatchOptions) { + this.options = options; + this.micromatchOptions = micromatchOptions; + this.index = new Map(); + } + /** + * Returns filter for directories. + */ + EntryFilter.prototype.getFilter = function (positive, negative) { + var _this = this; + var positiveRe = patternUtils.convertPatternsToRe(positive, this.micromatchOptions); + var negativeRe = patternUtils.convertPatternsToRe(negative, this.micromatchOptions); + return function (entry) { return _this.filter(entry, positiveRe, negativeRe); }; + }; + /** + * Returns true if entry must be added to result. + */ + EntryFilter.prototype.filter = function (entry, positiveRe, negativeRe) { + // Exclude duplicate results + if (this.options.unique) { + if (this.isDuplicateEntry(entry)) { + return false; + } + this.createIndexRecord(entry); + } + // Filter files and directories by options + if (this.onlyFileFilter(entry) || this.onlyDirectoryFilter(entry)) { + return false; + } + if (this.isSkippedByAbsoluteNegativePatterns(entry, negativeRe)) { + return false; + } + return this.isMatchToPatterns(entry.path, positiveRe) && !this.isMatchToPatterns(entry.path, negativeRe); + }; + /** + * Return true if the entry already has in the cross reader index. + */ + EntryFilter.prototype.isDuplicateEntry = function (entry) { + return this.index.has(entry.path); + }; + /** + * Create record in the cross reader index. + */ + EntryFilter.prototype.createIndexRecord = function (entry) { + this.index.set(entry.path, undefined); + }; + /** + * Returns true for non-files if the «onlyFiles» option is enabled. + */ + EntryFilter.prototype.onlyFileFilter = function (entry) { + return this.options.onlyFiles && !entry.isFile(); + }; + /** + * Returns true for non-directories if the «onlyDirectories» option is enabled. + */ + EntryFilter.prototype.onlyDirectoryFilter = function (entry) { + return this.options.onlyDirectories && !entry.isDirectory(); + }; + /** + * Return true when `absolute` option is enabled and matched to the negative patterns. + */ + EntryFilter.prototype.isSkippedByAbsoluteNegativePatterns = function (entry, negativeRe) { + if (!this.options.absolute) { + return false; + } + var fullpath = pathUtils.makeAbsolute(this.options.cwd, entry.path); + return this.isMatchToPatterns(fullpath, negativeRe); + }; + /** + * Return true when entry match to provided patterns. + * + * First, just trying to apply patterns to the path. + * Second, trying to apply patterns to the path with final slash (need to micromatch to support «directory/**» patterns). + */ + EntryFilter.prototype.isMatchToPatterns = function (filepath, patternsRe) { + return patternUtils.matchAny(filepath, patternsRe) || patternUtils.matchAny(filepath + '/', patternsRe); + }; + return EntryFilter; +}()); +exports.default = EntryFilter; /***/ }), -/* 686 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(138); -var fsStat = __webpack_require__(687); -var fs_1 = __webpack_require__(691); -var FileSystemStream = /** @class */ (function (_super) { - __extends(FileSystemStream, _super); - function FileSystemStream() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - * Use stream API to read entries for Task. - */ - FileSystemStream.prototype.read = function (patterns, filter) { - var _this = this; - var filepaths = patterns.map(this.getFullEntryPath, this); - var transform = new stream.Transform({ objectMode: true }); - transform._transform = function (index, _enc, done) { - return _this.getEntry(filepaths[index], patterns[index]).then(function (entry) { - if (entry !== null && filter(entry)) { - transform.push(entry); - } - if (index === filepaths.length - 1) { - transform.end(); - } - done(); - }); - }; - for (var i = 0; i < filepaths.length; i++) { - transform.write(i); - } - return transform; - }; - /** - * Return entry for the provided path. - */ - FileSystemStream.prototype.getEntry = function (filepath, pattern) { - var _this = this; - return this.getStat(filepath) - .then(function (stat) { return _this.makeEntry(stat, pattern); }) - .catch(function () { return null; }); - }; - /** - * Return fs.Stats for the provided path. - */ - FileSystemStream.prototype.getStat = function (filepath) { - return fsStat.stat(filepath, { throwErrorOnBrokenSymlinks: false }); - }; - return FileSystemStream; -}(fs_1.default)); -exports.default = FileSystemStream; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var stream = __webpack_require__(138); +var fsStat = __webpack_require__(686); +var fs_1 = __webpack_require__(690); +var FileSystemStream = /** @class */ (function (_super) { + __extends(FileSystemStream, _super); + function FileSystemStream() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * Use stream API to read entries for Task. + */ + FileSystemStream.prototype.read = function (patterns, filter) { + var _this = this; + var filepaths = patterns.map(this.getFullEntryPath, this); + var transform = new stream.Transform({ objectMode: true }); + transform._transform = function (index, _enc, done) { + return _this.getEntry(filepaths[index], patterns[index]).then(function (entry) { + if (entry !== null && filter(entry)) { + transform.push(entry); + } + if (index === filepaths.length - 1) { + transform.end(); + } + done(); + }); + }; + for (var i = 0; i < filepaths.length; i++) { + transform.write(i); + } + return transform; + }; + /** + * Return entry for the provided path. + */ + FileSystemStream.prototype.getEntry = function (filepath, pattern) { + var _this = this; + return this.getStat(filepath) + .then(function (stat) { return _this.makeEntry(stat, pattern); }) + .catch(function () { return null; }); + }; + /** + * Return fs.Stats for the provided path. + */ + FileSystemStream.prototype.getStat = function (filepath) { + return fsStat.stat(filepath, { throwErrorOnBrokenSymlinks: false }); + }; + return FileSystemStream; +}(fs_1.default)); +exports.default = FileSystemStream; /***/ }), -/* 687 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(688); -const statProvider = __webpack_require__(690); +const optionsManager = __webpack_require__(687); +const statProvider = __webpack_require__(689); /** * Asynchronous API. */ @@ -79702,13 +79690,13 @@ exports.statSync = statSync; /***/ }), -/* 688 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(689); +const fsAdapter = __webpack_require__(688); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -79721,7 +79709,7 @@ exports.prepare = prepare; /***/ }), -/* 689 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79744,7 +79732,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 690 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79796,318 +79784,318 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 691 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -var FileSystem = /** @class */ (function () { - function FileSystem(options) { - this.options = options; - } - /** - * Return full path to entry. - */ - FileSystem.prototype.getFullEntryPath = function (filepath) { - return path.resolve(this.options.cwd, filepath); - }; - /** - * Return an implementation of the Entry interface. - */ - FileSystem.prototype.makeEntry = function (stat, pattern) { - stat.path = pattern; - stat.depth = pattern.split('/').length; - return stat; - }; - return FileSystem; -}()); -exports.default = FileSystem; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +var FileSystem = /** @class */ (function () { + function FileSystem(options) { + this.options = options; + } + /** + * Return full path to entry. + */ + FileSystem.prototype.getFullEntryPath = function (filepath) { + return path.resolve(this.options.cwd, filepath); + }; + /** + * Return an implementation of the Entry interface. + */ + FileSystem.prototype.makeEntry = function (stat, pattern) { + stat.path = pattern; + stat.depth = pattern.split('/').length; + return stat; + }; + return FileSystem; +}()); +exports.default = FileSystem; /***/ }), -/* 692 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(138); -var readdir = __webpack_require__(669); -var reader_1 = __webpack_require__(682); -var fs_stream_1 = __webpack_require__(686); -var TransformStream = /** @class */ (function (_super) { - __extends(TransformStream, _super); - function TransformStream(reader) { - var _this = _super.call(this, { objectMode: true }) || this; - _this.reader = reader; - return _this; - } - TransformStream.prototype._transform = function (entry, _encoding, callback) { - callback(null, this.reader.transform(entry)); - }; - return TransformStream; -}(stream.Transform)); -var ReaderStream = /** @class */ (function (_super) { - __extends(ReaderStream, _super); - function ReaderStream() { - return _super !== null && _super.apply(this, arguments) || this; - } - Object.defineProperty(ReaderStream.prototype, "fsAdapter", { - /** - * Returns FileSystem adapter. - */ - get: function () { - return new fs_stream_1.default(this.options); - }, - enumerable: true, - configurable: true - }); - /** - * Use stream API to read entries for Task. - */ - ReaderStream.prototype.read = function (task) { - var _this = this; - var root = this.getRootDirectory(task); - var options = this.getReaderOptions(task); - var transform = new TransformStream(this); - var readable = this.api(root, task, options); - return readable - .on('error', function (err) { return _this.isEnoentCodeError(err) ? null : transform.emit('error', err); }) - .pipe(transform); - }; - /** - * Returns founded paths. - */ - ReaderStream.prototype.api = function (root, task, options) { - if (task.dynamic) { - return this.dynamicApi(root, options); - } - return this.staticApi(task, options); - }; - /** - * Api for dynamic tasks. - */ - ReaderStream.prototype.dynamicApi = function (root, options) { - return readdir.readdirStreamStat(root, options); - }; - /** - * Api for static tasks. - */ - ReaderStream.prototype.staticApi = function (task, options) { - return this.fsAdapter.read(task.patterns, options.filter); - }; - return ReaderStream; -}(reader_1.default)); -exports.default = ReaderStream; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var stream = __webpack_require__(138); +var readdir = __webpack_require__(668); +var reader_1 = __webpack_require__(681); +var fs_stream_1 = __webpack_require__(685); +var TransformStream = /** @class */ (function (_super) { + __extends(TransformStream, _super); + function TransformStream(reader) { + var _this = _super.call(this, { objectMode: true }) || this; + _this.reader = reader; + return _this; + } + TransformStream.prototype._transform = function (entry, _encoding, callback) { + callback(null, this.reader.transform(entry)); + }; + return TransformStream; +}(stream.Transform)); +var ReaderStream = /** @class */ (function (_super) { + __extends(ReaderStream, _super); + function ReaderStream() { + return _super !== null && _super.apply(this, arguments) || this; + } + Object.defineProperty(ReaderStream.prototype, "fsAdapter", { + /** + * Returns FileSystem adapter. + */ + get: function () { + return new fs_stream_1.default(this.options); + }, + enumerable: true, + configurable: true + }); + /** + * Use stream API to read entries for Task. + */ + ReaderStream.prototype.read = function (task) { + var _this = this; + var root = this.getRootDirectory(task); + var options = this.getReaderOptions(task); + var transform = new TransformStream(this); + var readable = this.api(root, task, options); + return readable + .on('error', function (err) { return _this.isEnoentCodeError(err) ? null : transform.emit('error', err); }) + .pipe(transform); + }; + /** + * Returns founded paths. + */ + ReaderStream.prototype.api = function (root, task, options) { + if (task.dynamic) { + return this.dynamicApi(root, options); + } + return this.staticApi(task, options); + }; + /** + * Api for dynamic tasks. + */ + ReaderStream.prototype.dynamicApi = function (root, options) { + return readdir.readdirStreamStat(root, options); + }; + /** + * Api for static tasks. + */ + ReaderStream.prototype.staticApi = function (task, options) { + return this.fsAdapter.read(task.patterns, options.filter); + }; + return ReaderStream; +}(reader_1.default)); +exports.default = ReaderStream; /***/ }), -/* 693 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(669); -var reader_1 = __webpack_require__(682); -var fs_sync_1 = __webpack_require__(694); -var ReaderSync = /** @class */ (function (_super) { - __extends(ReaderSync, _super); - function ReaderSync() { - return _super !== null && _super.apply(this, arguments) || this; - } - Object.defineProperty(ReaderSync.prototype, "fsAdapter", { - /** - * Returns FileSystem adapter. - */ - get: function () { - return new fs_sync_1.default(this.options); - }, - enumerable: true, - configurable: true - }); - /** - * Use sync API to read entries for Task. - */ - ReaderSync.prototype.read = function (task) { - var root = this.getRootDirectory(task); - var options = this.getReaderOptions(task); - try { - var entries = this.api(root, task, options); - return entries.map(this.transform, this); - } - catch (err) { - if (this.isEnoentCodeError(err)) { - return []; - } - throw err; - } - }; - /** - * Returns founded paths. - */ - ReaderSync.prototype.api = function (root, task, options) { - if (task.dynamic) { - return this.dynamicApi(root, options); - } - return this.staticApi(task, options); - }; - /** - * Api for dynamic tasks. - */ - ReaderSync.prototype.dynamicApi = function (root, options) { - return readdir.readdirSyncStat(root, options); - }; - /** - * Api for static tasks. - */ - ReaderSync.prototype.staticApi = function (task, options) { - return this.fsAdapter.read(task.patterns, options.filter); - }; - return ReaderSync; -}(reader_1.default)); -exports.default = ReaderSync; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var readdir = __webpack_require__(668); +var reader_1 = __webpack_require__(681); +var fs_sync_1 = __webpack_require__(693); +var ReaderSync = /** @class */ (function (_super) { + __extends(ReaderSync, _super); + function ReaderSync() { + return _super !== null && _super.apply(this, arguments) || this; + } + Object.defineProperty(ReaderSync.prototype, "fsAdapter", { + /** + * Returns FileSystem adapter. + */ + get: function () { + return new fs_sync_1.default(this.options); + }, + enumerable: true, + configurable: true + }); + /** + * Use sync API to read entries for Task. + */ + ReaderSync.prototype.read = function (task) { + var root = this.getRootDirectory(task); + var options = this.getReaderOptions(task); + try { + var entries = this.api(root, task, options); + return entries.map(this.transform, this); + } + catch (err) { + if (this.isEnoentCodeError(err)) { + return []; + } + throw err; + } + }; + /** + * Returns founded paths. + */ + ReaderSync.prototype.api = function (root, task, options) { + if (task.dynamic) { + return this.dynamicApi(root, options); + } + return this.staticApi(task, options); + }; + /** + * Api for dynamic tasks. + */ + ReaderSync.prototype.dynamicApi = function (root, options) { + return readdir.readdirSyncStat(root, options); + }; + /** + * Api for static tasks. + */ + ReaderSync.prototype.staticApi = function (task, options) { + return this.fsAdapter.read(task.patterns, options.filter); + }; + return ReaderSync; +}(reader_1.default)); +exports.default = ReaderSync; /***/ }), -/* 694 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(687); -var fs_1 = __webpack_require__(691); -var FileSystemSync = /** @class */ (function (_super) { - __extends(FileSystemSync, _super); - function FileSystemSync() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - * Use sync API to read entries for Task. - */ - FileSystemSync.prototype.read = function (patterns, filter) { - var _this = this; - var entries = []; - patterns.forEach(function (pattern) { - var filepath = _this.getFullEntryPath(pattern); - var entry = _this.getEntry(filepath, pattern); - if (entry === null || !filter(entry)) { - return; - } - entries.push(entry); - }); - return entries; - }; - /** - * Return entry for the provided path. - */ - FileSystemSync.prototype.getEntry = function (filepath, pattern) { - try { - var stat = this.getStat(filepath); - return this.makeEntry(stat, pattern); - } - catch (err) { - return null; - } - }; - /** - * Return fs.Stats for the provided path. - */ - FileSystemSync.prototype.getStat = function (filepath) { - return fsStat.statSync(filepath, { throwErrorOnBrokenSymlinks: false }); - }; - return FileSystemSync; -}(fs_1.default)); -exports.default = FileSystemSync; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var fsStat = __webpack_require__(686); +var fs_1 = __webpack_require__(690); +var FileSystemSync = /** @class */ (function (_super) { + __extends(FileSystemSync, _super); + function FileSystemSync() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * Use sync API to read entries for Task. + */ + FileSystemSync.prototype.read = function (patterns, filter) { + var _this = this; + var entries = []; + patterns.forEach(function (pattern) { + var filepath = _this.getFullEntryPath(pattern); + var entry = _this.getEntry(filepath, pattern); + if (entry === null || !filter(entry)) { + return; + } + entries.push(entry); + }); + return entries; + }; + /** + * Return entry for the provided path. + */ + FileSystemSync.prototype.getEntry = function (filepath, pattern) { + try { + var stat = this.getStat(filepath); + return this.makeEntry(stat, pattern); + } + catch (err) { + return null; + } + }; + /** + * Return fs.Stats for the provided path. + */ + FileSystemSync.prototype.getStat = function (filepath) { + return fsStat.statSync(filepath, { throwErrorOnBrokenSymlinks: false }); + }; + return FileSystemSync; +}(fs_1.default)); +exports.default = FileSystemSync; /***/ }), -/* 695 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Flatten nested arrays (max depth is 2) into a non-nested array of non-array items. - */ -function flatten(items) { - return items.reduce(function (collection, item) { return [].concat(collection, item); }, []); -} -exports.flatten = flatten; + +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Flatten nested arrays (max depth is 2) into a non-nested array of non-array items. + */ +function flatten(items) { + return items.reduce(function (collection, item) { return [].concat(collection, item); }, []); +} +exports.flatten = flatten; /***/ }), -/* 696 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(300); -/** - * Merge multiple streams and propagate their errors into one stream in parallel. - */ -function merge(streams) { - var mergedStream = merge2(streams); - streams.forEach(function (stream) { - stream.on('error', function (err) { return mergedStream.emit('error', err); }); - }); - return mergedStream; -} -exports.merge = merge; + +Object.defineProperty(exports, "__esModule", { value: true }); +var merge2 = __webpack_require__(300); +/** + * Merge multiple streams and propagate their errors into one stream in parallel. + */ +function merge(streams) { + var mergedStream = merge2(streams); + streams.forEach(function (stream) { + stream.on('error', function (err) { return mergedStream.emit('error', err); }); + }); + return mergedStream; +} +exports.merge = merge; /***/ }), -/* 697 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(698); +const pathType = __webpack_require__(697); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -80173,13 +80161,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 698 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const pify = __webpack_require__(699); +const pify = __webpack_require__(698); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -80222,7 +80210,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 699 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80313,17 +80301,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 700 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(523); -const gitIgnore = __webpack_require__(701); +const fastGlob = __webpack_require__(522); +const gitIgnore = __webpack_require__(700); const pify = __webpack_require__(233); -const slash = __webpack_require__(702); +const slash = __webpack_require__(701); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -80421,7 +80409,7 @@ module.exports.sync = options => { /***/ }), -/* 701 */ +/* 700 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -80890,7 +80878,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 702 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80908,7 +80896,7 @@ module.exports = input => { /***/ }), -/* 703 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80921,7 +80909,7 @@ module.exports = input => { -var isGlob = __webpack_require__(704); +var isGlob = __webpack_require__(703); module.exports = function hasGlob(val) { if (val == null) return false; @@ -80941,7 +80929,7 @@ module.exports = function hasGlob(val) { /***/ }), -/* 704 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -80972,17 +80960,17 @@ module.exports = function isGlob(str) { /***/ }), -/* 705 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(134); -const pEvent = __webpack_require__(706); -const CpFileError = __webpack_require__(709); -const fs = __webpack_require__(711); -const ProgressEmitter = __webpack_require__(714); +const pEvent = __webpack_require__(705); +const CpFileError = __webpack_require__(708); +const fs = __webpack_require__(710); +const ProgressEmitter = __webpack_require__(713); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -81096,12 +81084,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 706 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(707); +const pTimeout = __webpack_require__(706); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -81394,13 +81382,13 @@ module.exports.TimeoutError = pTimeout.TimeoutError; /***/ }), -/* 707 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(708); +const pFinally = __webpack_require__(707); class TimeoutError extends Error { constructor(message) { @@ -81458,7 +81446,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 708 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81480,12 +81468,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 709 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(710); +const NestedError = __webpack_require__(709); class CpFileError extends NestedError { constructor(message, nested) { @@ -81499,7 +81487,7 @@ module.exports = CpFileError; /***/ }), -/* 710 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(112).inherits; @@ -81555,16 +81543,16 @@ module.exports = NestedError; /***/ }), -/* 711 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(112); const fs = __webpack_require__(133); -const makeDir = __webpack_require__(712); -const pEvent = __webpack_require__(706); -const CpFileError = __webpack_require__(709); +const makeDir = __webpack_require__(711); +const pEvent = __webpack_require__(705); +const CpFileError = __webpack_require__(708); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -81661,7 +81649,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 712 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81669,7 +81657,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(134); const path = __webpack_require__(4); const {promisify} = __webpack_require__(112); -const semver = __webpack_require__(713); +const semver = __webpack_require__(712); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -81824,7 +81812,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 713 */ +/* 712 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -83426,7 +83414,7 @@ function coerce (version, options) { /***/ }), -/* 714 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83467,7 +83455,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 715 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83513,12 +83501,12 @@ exports.default = module.exports; /***/ }), -/* 716 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(717); +const pMap = __webpack_require__(716); const pFilter = async (iterable, filterer, options) => { const values = await pMap( @@ -83535,7 +83523,7 @@ module.exports.default = pFilter; /***/ }), -/* 717 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83614,12 +83602,12 @@ module.exports.default = pMap; /***/ }), -/* 718 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(710); +const NestedError = __webpack_require__(709); class CpyError extends NestedError { constructor(message, nested) { diff --git a/packages/osd-pm/package.json b/packages/osd-pm/package.json index 74261eb778e0..5f50dc858b13 100644 --- a/packages/osd-pm/package.json +++ b/packages/osd-pm/package.json @@ -18,6 +18,7 @@ "@babel/plugin-proposal-object-rest-spread": "^7.16.5", "@babel/preset-env": "^7.16.5", "@babel/preset-typescript": "^7.16.5", + "@node-rs/xxhash": "^1.3.0", "@types/cmd-shim": "^2.0.0", "@types/cpy": "^5.1.0", "@types/dedent": "^0.7.0", @@ -28,7 +29,7 @@ "@types/lodash": "^4.14.170", "@types/log-symbols": "^2.0.0", "@types/ncp": "^2.0.1", - "@types/node": "^14.17.32", + "@types/node": "~18.7.0", "@types/ora": "^1.3.5", "@types/read-pkg": "^4.0.0", "@types/strip-ansi": "^5.2.1", @@ -46,7 +47,7 @@ "execa": "^4.0.2", "getopts": "^2.2.5", "glob": "^7.1.7", - "globby": "^8.0.1", + "globby": "^11.1.0", "has-ansi": "^3.0.0", "is-path-inside": "^3.0.2", "lodash": "^4.17.21", @@ -62,7 +63,7 @@ "tempy": "^0.3.0", "typescript": "4.0.2", "unlazy-loader": "^0.1.3", - "webpack": "^4.41.5", + "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2", "webpack-cli": "^4.9.2", "write-pkg": "^4.0.0" }, diff --git a/packages/osd-pm/src/utils/fs.ts b/packages/osd-pm/src/utils/fs.ts index c29aacb2a901..cf9b1f3a8542 100644 --- a/packages/osd-pm/src/utils/fs.ts +++ b/packages/osd-pm/src/utils/fs.ts @@ -30,19 +30,15 @@ import cmdShimCb from 'cmd-shim'; import fs from 'fs'; +import { lstat, symlink, mkdir, unlink } from 'fs/promises'; import { ncp } from 'ncp'; import { dirname, relative } from 'path'; import { promisify } from 'util'; -const lstat = promisify(fs.lstat); -export const readFile = promisify(fs.readFile); -export const writeFile = promisify(fs.writeFile); -const symlink = promisify(fs.symlink); -export const chmod = promisify(fs.chmod); +export { readFile, writeFile, chmod, unlink } from 'fs/promises'; + const cmdShim = promisify<string, string>(cmdShimCb); -const mkdir = promisify(fs.mkdir); export const mkdirp = async (path: string) => await mkdir(path, { recursive: true }); -export const unlink = promisify(fs.unlink); export const copyDirectory = promisify(ncp); async function statTest(path: string, block: (stats: fs.Stats) => boolean) { diff --git a/packages/osd-pm/src/utils/project_checksums.ts b/packages/osd-pm/src/utils/project_checksums.ts index e7d928ce3f73..e60590ee91ee 100644 --- a/packages/osd-pm/src/utils/project_checksums.ts +++ b/packages/osd-pm/src/utils/project_checksums.ts @@ -28,11 +28,9 @@ * under the License. */ -import Fs from 'fs'; +import { stat } from 'fs/promises'; import Crypto from 'crypto'; -import { promisify } from 'util'; - import execa from 'execa'; import { YarnLock, resolveDepsForProject } from './yarn_lock'; @@ -45,7 +43,6 @@ export type ChecksumMap = Map<string, string | undefined>; /** map of [repo relative path to changed file, type of change] */ type Changes = Map<string, 'modified' | 'deleted' | 'invalid' | 'untracked'>; -const statAsync = promisify(Fs.stat); const projectBySpecificitySorter = (a: Project, b: Project) => b.path.length - a.path.length; /** Get the changed files for a set of projects */ @@ -184,7 +181,7 @@ async function getChecksum( return `${path}:deleted`; } - const stats = await statAsync(osd.getAbsolute(path)); + const stats = await stat(osd.getAbsolute(path)); log.verbose(`[${project.name}] modified time ${stats.mtimeMs} for ${path}`); return `${path}:${stats.mtimeMs}`; }) diff --git a/packages/osd-pm/webpack.config.js b/packages/osd-pm/webpack.config.js index 66b9e0a81ee4..fd8cdcbfde27 100644 --- a/packages/osd-pm/webpack.config.js +++ b/packages/osd-pm/webpack.config.js @@ -41,6 +41,7 @@ module.exports = { path: path.resolve(__dirname, 'dist'), filename: '[name].js', libraryTarget: 'commonjs2', + hashFunction: 'Xxh64', }, resolve: { diff --git a/packages/osd-test/package.json b/packages/osd-test/package.json index 69fa50828fc0..c1ee4f1687cd 100644 --- a/packages/osd-test/package.json +++ b/packages/osd-test/package.json @@ -31,7 +31,7 @@ "exit-hook": "^2.2.0", "getopts": "^2.2.5", "glob": "^7.1.7", - "joi": "^13.5.2", + "joi": "^14.3.1", "lodash": "^4.17.21", "parse-link-header": "^2.0.0", "rxjs": "^6.5.5", diff --git a/packages/osd-test/src/functional_test_runner/lib/suite_tracker.test.ts b/packages/osd-test/src/functional_test_runner/lib/suite_tracker.test.ts index cc628b9de811..9ba16ca6407c 100644 --- a/packages/osd-test/src/functional_test_runner/lib/suite_tracker.test.ts +++ b/packages/osd-test/src/functional_test_runner/lib/suite_tracker.test.ts @@ -31,7 +31,19 @@ import fs from 'fs'; import { join, resolve } from 'path'; -jest.mock('fs'); +jest.mock('fs', () => { + const mockedFs: object = jest.createMockFromModule('fs'); + const realFs = jest.requireActual('fs'); + + return { + __esModules: true, + ...mockedFs, + // Both needed by @osd/cross-platform + realpathSync: realFs.realpathSync, + readFileSync: realFs.readFileSync, + }; +}); + jest.mock('@osd/utils', () => { return { REPO_ROOT: '/dev/null/root' }; }); diff --git a/packages/osd-ui-framework/Gruntfile.js b/packages/osd-ui-framework/Gruntfile.js index b52e1430f56e..9ad26b578e08 100644 --- a/packages/osd-ui-framework/Gruntfile.js +++ b/packages/osd-ui-framework/Gruntfile.js @@ -28,7 +28,7 @@ * under the License. */ -const sass = require('sass'); +const sass = require('node-sass'); const postcss = require('postcss'); const postcssConfig = require('@osd/optimizer/postcss.config.js'); diff --git a/packages/osd-ui-framework/package.json b/packages/osd-ui-framework/package.json index 3ebc09f4a8ad..ffd624757392 100644 --- a/packages/osd-ui-framework/package.json +++ b/packages/osd-ui-framework/package.json @@ -30,7 +30,7 @@ "grunt-babel": "^8.0.0", "grunt-contrib-clean": "^2.0.0", "grunt-contrib-copy": "^1.0.0", - "sass": "~1.26.11", + "node-sass": "^8.0.0", "postcss": "^8.4.5", "sinon": "^7.4.2" } diff --git a/packages/osd-ui-shared-deps/package.json b/packages/osd-ui-shared-deps/package.json index 61f7008fbc0f..a4924657cb1e 100644 --- a/packages/osd-ui-shared-deps/package.json +++ b/packages/osd-ui-shared-deps/package.json @@ -17,7 +17,7 @@ "abortcontroller-polyfill": "^1.4.0", "angular": "^1.8.2", "axios": "^0.27.2", - "compression-webpack-plugin": "^4.0.0", + "compression-webpack-plugin": "npm:@amoo-miki/compression-webpack-plugin@4.0.1-rc.1", "core-js": "^3.6.5", "custom-event-polyfill": "^0.3.0", "jquery": "^3.5.0", @@ -38,6 +38,7 @@ "whatwg-fetch": "^3.0.0" }, "devDependencies": { + "@node-rs/xxhash": "^1.3.0", "@osd/babel-preset": "1.0.0", "@osd/dev-utils": "1.0.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", @@ -45,6 +46,6 @@ "del": "^6.1.1", "loader-utils": "^2.0.4", "val-loader": "^2.1.2", - "webpack": "^4.41.5" + "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2" } } diff --git a/packages/osd-ui-shared-deps/webpack.config.js b/packages/osd-ui-shared-deps/webpack.config.js index 5cd48010215d..397fbe12cd4b 100644 --- a/packages/osd-ui-shared-deps/webpack.config.js +++ b/packages/osd-ui-shared-deps/webpack.config.js @@ -57,6 +57,7 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ devtoolModuleFilenameTemplate: (info) => `osd-ui-shared-deps/${Path.relative(REPO_ROOT, info.absoluteResourcePath)}`, library: '__osdSharedDeps__', + hashFunction: 'Xxh64', }, module: { diff --git a/release-notes/opensearch-dashboards.release-notes-1.3.10.md b/release-notes/opensearch-dashboards.release-notes-1.3.10.md new file mode 100644 index 000000000000..be08d46aac93 --- /dev/null +++ b/release-notes/opensearch-dashboards.release-notes-1.3.10.md @@ -0,0 +1,33 @@ +# Version 1.3.10 Release Notes + +### 🛡 Security + +- [CVE-2020-15366][1.x] Bump ajv from 4.11.8 to 6.12.6 ([#4035](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4035)) +- [CVE-2022-48285][1.x] Bump jszip from 3.7.1 to 3.10.1 ([#4011](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4011)) +- [CVE-2021-35065][1.x] Bump glob-parent from 6.0.0 to 6.0.2 ([#4005](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4005)) +- [CVE-2022-25851][1.x] Bump jpeg-js from 0.4.1 to 0.4.4 ([#3860](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3860)) +- [CVE-2022-25858][1.x] Bump terser from 4.8.0 to 4.8.1 ([#3786](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3786)) +- [CVE-2021-23490][1.x] Bump parse-link-header from 1.0.1 to 2.0.0 ([#3820](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3820)) +- [CVE-2021-3765][1.x] Bump validator from 8.2.0 to 13.9.0 ([#3753](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3753)) +- [CVE-2022-25758][1.x] Bump scss-tokenizer from 0.3.0 to 0.4.3 ([#3789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3789)) +- [CVE-2021-3803][1.x] Bump nth-check from 1.0.2 to 2.0.1 ([#3745](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3745)) +- Bump highlight.js from 9.18.5 to 10.7.3 to solve security concerns ([#4062](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4062)) + +### 📈 Features/Enhancements + +- Add tooltip to help icon ([#3872](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3872)) + +### 🐛 Bug Fixes + +- [TSVB] Fix the link to "serial differencing aggregation" documentation ([#3503](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3503)) + +### 📝 Documentation + +- Update jest documentation links ([#3939](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3939)) + +### 🛠 Maintenance + +- Add threshold to code coverage changes for project ([#4050](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4050)) +- Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests ([#4039]()) +- Update MAINTAINERS.md and CODEOWNERS ([#3938](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3938)) +- Add opensearch-dashboards-docker-dev to .gitignore ([#3781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3781)) diff --git a/release-notes/opensearch-dashboards.release-notes-2.7.0.md b/release-notes/opensearch-dashboards.release-notes-2.7.0.md index 3ad55fe4d807..60eda3832436 100644 --- a/release-notes/opensearch-dashboards.release-notes-2.7.0.md +++ b/release-notes/opensearch-dashboards.release-notes-2.7.0.md @@ -76,7 +76,6 @@ ### 🪛 Refactoring - Remove automatic addition of `osd-version` header to requests outside of OpenSearch Dashboards ([#3643](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3643)) -- [Console] Replace jQuery usage in console plugin with native methods ([#3733](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3733)) - [Doc Links] Clean up docs_link_service organization so that strings are in the right categories. ([#3685](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3685)) - [I18n] Fix Listr type errors and error handlers ([#3629](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3629)) - [Multiple DataSource] Refactor dev tool console to use opensearch-js client to send requests ([#3544](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3544)) diff --git a/scripts/bwc/opensearch_dashboards_service.sh b/scripts/bwc/opensearch_dashboards_service.sh index 79f1c56708b4..784fbcaa594b 100755 --- a/scripts/bwc/opensearch_dashboards_service.sh +++ b/scripts/bwc/opensearch_dashboards_service.sh @@ -29,4 +29,4 @@ function check_dashboards_status { cd "$DIR" check_status $DASHBOARDS_PATH "$DASHBOARDS_MSG" $DASHBOARDS_URL "$OPENSEARCH_ARGS" >> /dev/null 2>&1 echo "OpenSearch Dashboards is up!" -} +} diff --git a/scripts/bwc/utils.sh b/scripts/bwc/utils.sh index fea47b14759a..c6a4ef739307 100755 --- a/scripts/bwc/utils.sh +++ b/scripts/bwc/utils.sh @@ -9,11 +9,12 @@ function open_artifact() { artifact_dir=$1 artifact=$2 cd $artifact_dir - + # check if artifact provided is URL or attempt if passing by absolute path if curl -I -L $artifact; then curl -L $artifact | tar -xz --strip-components=1 else + echo "Artifact is not a URL; attempting to unarchive a local file..." tar -xf $artifact --strip-components=1 fi } @@ -45,14 +46,25 @@ function print_txt() { } # this function is used to check the running status of OpenSearch or OpenSearch Dashboards -# $1 is the path to the tmp file which saves the running status +# $1 is the path to the tmp file which saves the running status # $2 is the error msg to check # $3 is the url to curl # $4 contains arguments that need to be passed to the curl command function check_status() { - while [ ! -f $1 ] || ! grep -q "$2" $1; do - if [ -f $1 ]; then rm $1; fi - curl $3 $4 > $1 || true + # Calculate end time as 180s from now + check_status_end_time=$(expr 180 + "$(date '+%s')") + + rm -rf $1 + + while [ ! -f $1 ] || ! grep -q "$2" $1; do + if [ -f $1 ]; then rm $1; fi + curl $3 $4 > $1 || true + + # Stop checking after $check_status_end_time + if [ $check_status_end_time -lt $(date '+%s') ]; then + echo "Error: Status check has timed out" + exit 1 + fi done rm $1 } @@ -62,7 +74,7 @@ function check_status() { function upload_data() { rm -rf "$OPENSEARCH_DIR/data" cd $OPENSEARCH_DIR - cp "$CWD/cypress/test-data/$DASHBOARDS_TYPE/$1.tar.gz" . + cp "$CWD/cypress/test-data/$DASHBOARDS_TYPE/$1.tar.gz" . tar -xvf "$OPENSEARCH_DIR/$1.tar.gz" >> /dev/null 2>&1 rm "$1.tar.gz" echo "Data has been uploaded and ready to test" @@ -75,6 +87,6 @@ function get_dashboards_package_version() { | awk -F: '{ print $2 }' \ | sed 's/[",]//g' \ | tr -d [:space:]) - + echo "$DASHBOARDS_PACKAGE_VERSION" } diff --git a/scripts/jest.js b/scripts/jest.js index 53c687e51a84..80f269c72498 100755 --- a/scripts/jest.js +++ b/scripts/jest.js @@ -38,7 +38,7 @@ // // node scripts/jest --coverage // -// See all cli options in https://facebook.github.io/jest/docs/cli.html +// See all cli options in https://jestjs.io/docs/cli var resolve = require('path').resolve; process.argv.push('--config', resolve(__dirname, '../src/dev/jest/config.js')); diff --git a/scripts/jest_integration.js b/scripts/jest_integration.js index f5bc937c4d79..e0614aef7a11 100755 --- a/scripts/jest_integration.js +++ b/scripts/jest_integration.js @@ -38,7 +38,12 @@ // // node scripts/jest_integration --coverage // -// See all cli options in https://facebook.github.io/jest/docs/cli.html +// See all cli options in https://jestjs.io/docs/cli + +/* Increase the max listeners + * Numerous listeners are added by `root.start()` that are never removed. + */ +process.setMaxListeners(Infinity); var resolve = require('path').resolve; process.argv.push('--config', resolve(__dirname, '../src/dev/jest/config.integration.js')); diff --git a/scripts/use_node b/scripts/use_node index f39a39434db1..48e7e2858200 100755 --- a/scripts/use_node +++ b/scripts/use_node @@ -57,11 +57,17 @@ elif [ ! -z "$NODE_HOME" ]; then NODE_ERROR_MSG="in NODE_HOME" NODE_ERROR_SHOW=true else + # Set these variables outside, as catchalls, to show meaningful errors if needed NODE="$OSD_HOME/node/bin/node" NODE_ERROR_MSG="bundled with OpenSearch Dashboards" # A bin folder at the root is only present in release builds that have a bundled Node.js binary - if [ -x "OSD_HOME/bin" ]; then + if [ -d "${OSD_HOME}/bin" ]; then NODE_ERROR_SHOW=true + # Not all operating systems can run the latest Node.js and the fallback is for them + "${NODE}" -v 2> /dev/null + if [ $? -ne 0 ] && [ -d "${OSD_HOME}/node/fallback" ]; then + NODE="$OSD_HOME/node/fallback/bin/node" + fi fi fi @@ -75,7 +81,7 @@ else fi if [ ! -x "$NODE" ]; then - # Irrespective of NODE_ERROR_SHOW, show the error + # Irrespective of NODE_ERROR_SHOW, if NODE is not found or executable, show the error echo "Could not find a Node.js runtime binary $NODE_ERROR_MSG or on the system" >&2 exit 1 fi @@ -91,7 +97,7 @@ fi # If a file path was provided for execution, prefix with OSD_HOME; use relative paths to avoid the need for this. if [ ! -z "$OSD_USE_NODE_JS_FILE_PATH" ]; then - NODE_OPTIONS="$OSD_NODE_OPTS_PREFIX $OSD_NODE_OPTS $NODE_OPTIONS" "${NODE}" "${OSD_HOME}${OSD_USE_NODE_JS_FILE_PATH}" "${@}" + NODE_OPTIONS="$OSD_NODE_OPTS_PREFIX $OSD_NODE_OPTS $NODE_OPTIONS" exec "${NODE}" "${OSD_HOME}${OSD_USE_NODE_JS_FILE_PATH}" "${@}" elif [ $# -ne 0 ]; then - NODE_OPTIONS="$OSD_NODE_OPTS_PREFIX $OSD_NODE_OPTS $NODE_OPTIONS" "${NODE}" "${@}" + NODE_OPTIONS="$OSD_NODE_OPTS_PREFIX $OSD_NODE_OPTS $NODE_OPTIONS" exec "${NODE}" "${@}" fi diff --git a/src/cli/cluster/cluster_manager.test.ts b/src/cli/cluster/cluster_manager.test.ts index 350d863c9892..7c83d9194501 100644 --- a/src/cli/cluster/cluster_manager.test.ts +++ b/src/cli/cluster/cluster_manager.test.ts @@ -31,6 +31,7 @@ import * as Rx from 'rxjs'; import { mockCluster } from './cluster_manager.test.mocks'; +import '@osd/optimizer/target/__mocks__/lmdb'; jest.mock('readline', () => ({ createInterface: jest.fn(() => ({ diff --git a/src/cli/serve/integration_tests/invalid_config.test.ts b/src/cli/serve/integration_tests/invalid_config.test.ts index 87ffd37d038e..4fe27ccd39ef 100644 --- a/src/cli/serve/integration_tests/invalid_config.test.ts +++ b/src/cli/serve/integration_tests/invalid_config.test.ts @@ -64,7 +64,7 @@ describe('cli invalid config support', function () { .split('\n') .filter(Boolean) .map((line) => JSON.parse(line) as LogEntry) - .filter((line) => line.tags.includes('fatal')) + .filter((line) => line.tags?.includes?.('fatal')) .map((obj) => ({ ...obj, pid: '## PID ##', diff --git a/src/cli/serve/integration_tests/reload_logging_config.test.ts b/src/cli/serve/integration_tests/reload_logging_config.test.ts index fb3c63ffc71d..5950cb1fdfb0 100644 --- a/src/cli/serve/integration_tests/reload_logging_config.test.ts +++ b/src/cli/serve/integration_tests/reload_logging_config.test.ts @@ -36,7 +36,7 @@ import Del from 'del'; import * as Rx from 'rxjs'; import { map, filter, take } from 'rxjs/operators'; -import { safeDump } from 'js-yaml'; +import { dump } from 'js-yaml'; import { getConfigFromFiles } from '@osd/config'; const legacyConfig = follow('__fixtures__/reload_logging_config/opensearch_dashboards.test.yml'); @@ -89,7 +89,7 @@ function createConfigManager(configPath: string) { return { modify(fn: (input: Record<string, any>) => Record<string, any>) { const oldContent = getConfigFromFiles([configPath]); - const yaml = safeDump(fn(oldContent)); + const yaml = dump(fn(oldContent)); Fs.writeFileSync(configPath, yaml); }, }; diff --git a/src/cli_plugin/install/cleanup.js b/src/cli_plugin/install/cleanup.js index b23b28fc5295..c93cad047f6d 100644 --- a/src/cli_plugin/install/cleanup.js +++ b/src/cli_plugin/install/cleanup.js @@ -28,33 +28,29 @@ * under the License. */ -import del from 'del'; -import fs from 'fs'; +import { rm, access } from 'fs/promises'; +import { rmSync, constants } from 'fs'; -export function cleanPrevious(settings, logger) { - return new Promise(function (resolve, reject) { - try { - fs.statSync(settings.workingPath); - - logger.log('Found previous install attempt. Deleting...'); - try { - del.sync(settings.workingPath, { force: true }); - } catch (e) { - reject(e); - } - resolve(); - } catch (e) { - if (e.code !== 'ENOENT') reject(e); +const exists = async (loc) => { + try { + await access(loc, constants.W_OK); + return true; + } catch (e) { + if (e.code !== 'ENOENT') throw e; + } +}; +export const cleanPrevious = async (settings, logger) => { + const workingPathExists = await exists(settings.workingPath); - resolve(); - } - }); -} + if (workingPathExists) { + logger.log('Found previous install attempt. Deleting...'); + return await rm(settings.workingPath, { recursive: true }); + } +}; export function cleanArtifacts(settings) { - // delete the working directory. - // At this point we're bailing, so swallow any errors on delete. + // Delete the working directory; at this point we're bailing, so swallow any errors on delete. try { - del.sync(settings.workingPath); + rmSync(settings.workingPath, { recursive: true, force: true }); } catch (e) {} // eslint-disable-line no-empty } diff --git a/src/cli_plugin/install/cleanup.test.js b/src/cli_plugin/install/cleanup.test.js index f05d69d1621b..f1453d0ff129 100644 --- a/src/cli_plugin/install/cleanup.test.js +++ b/src/cli_plugin/install/cleanup.test.js @@ -28,9 +28,11 @@ * under the License. */ -import sinon from 'sinon'; -import fs from 'fs'; -import del from 'del'; +import fs from 'fs/promises'; +import { rmSync } from 'fs'; + +jest.mock('fs'); +jest.mock('fs/promises'); import { cleanPrevious, cleanArtifacts } from './cleanup'; import { Logger } from '../lib/logger'; @@ -41,105 +43,116 @@ describe('opensearchDashboards cli', function () { const settings = { workingPath: 'dummy', }; + const logger = new Logger(settings); describe('cleanPrevious', function () { - let errorStub; - let logger; - - beforeEach(function () { - errorStub = sinon.stub(); - logger = new Logger(settings); - sinon.stub(logger, 'log'); - sinon.stub(logger, 'error'); - }); - afterEach(function () { - logger.log.restore(); - logger.error.restore(); - fs.statSync.restore(); - del.sync.restore(); + jest.resetAllMocks(); }); - it('should resolve if the working path does not exist', function () { - sinon.stub(del, 'sync'); - sinon.stub(fs, 'statSync').callsFake(() => { - const error = new Error('ENOENT'); - error.code = 'ENOENT'; - throw error; + it('should resolve if the working path does not exist', async () => { + fs.access.mockImplementation(() => { + const notFoundError = new Error('ENOENT'); + notFoundError.code = 'ENOENT'; + throw notFoundError; }); - return cleanPrevious(settings, logger) - .catch(errorStub) - .then(function () { - expect(errorStub.called).toBe(false); - }); + const exceptionCatcher = jest.fn(); + + try { + await cleanPrevious(settings, logger); + } catch (e) { + exceptionCatcher(); + } + + expect(fs.rm).not.toHaveBeenCalled(); + expect(exceptionCatcher).not.toHaveBeenCalled(); }); - it('should rethrow any exception except ENOENT from fs.statSync', function () { - sinon.stub(del, 'sync'); - sinon.stub(fs, 'statSync').throws(new Error('An Unhandled Error')); + it('should rethrow any exceptions from accessing the folders except ENOENT', async () => { + fs.access.mockImplementation(() => { + const noAccessError = new Error('EACCES'); + noAccessError.code = 'EACCES'; + throw noAccessError; + }); + + const exceptionCatcher = jest.fn(); - errorStub = sinon.stub(); - return cleanPrevious(settings, logger) - .catch(errorStub) - .then(function () { - expect(errorStub.called).toBe(true); - }); + try { + await cleanPrevious(settings, logger); + } catch (e) { + exceptionCatcher(); + } + + expect(fs.rm).not.toHaveBeenCalled(); + expect(exceptionCatcher).toHaveBeenCalled(); }); - it('should log a message if there was a working directory', function () { - sinon.stub(del, 'sync'); - sinon.stub(fs, 'statSync'); - - return cleanPrevious(settings, logger) - .catch(errorStub) - .then(function () { - expect(logger.log.calledWith('Found previous install attempt. Deleting...')).toBe( - true - ); - }); + it('should log a message if there was a working directory', async () => { + jest.spyOn(logger, 'log'); + const exceptionCatcher = jest.fn(); + + try { + await cleanPrevious(settings, logger); + } catch (e) { + exceptionCatcher(); + } + + expect(logger.log).toHaveBeenCalledWith('Found previous install attempt. Deleting...'); + expect(fs.rm).toHaveBeenCalled(); + expect(exceptionCatcher).not.toHaveBeenCalled(); }); - it('should rethrow any exception from del.sync', function () { - sinon.stub(fs, 'statSync'); - sinon.stub(del, 'sync').throws(new Error('I am an error thrown by del')); + it('should rethrow any exception from deleting the folder', async () => { + fs.rm.mockImplementation(() => { + const dummyError = new Error('EDUMMY'); + dummyError.code = 'EDUMMY'; + throw dummyError; + }); + + const exceptionCatcher = jest.fn(); - errorStub = sinon.stub(); - return cleanPrevious(settings, logger) - .catch(errorStub) - .then(function () { - expect(errorStub.called).toBe(true); - }); + try { + await cleanPrevious(settings, logger); + } catch (e) { + exceptionCatcher(); + } + + expect(fs.rm).toHaveBeenCalled(); + expect(exceptionCatcher).toHaveBeenCalled(); }); - it('should resolve if the working path is deleted', function () { - sinon.stub(del, 'sync'); - sinon.stub(fs, 'statSync'); + it('should resolve if the working path is deleted', async () => { + const exceptionCatcher = jest.fn(); + + try { + await cleanPrevious(settings, logger); + } catch (e) { + exceptionCatcher(); + } - return cleanPrevious(settings, logger) - .catch(errorStub) - .then(function () { - expect(errorStub.called).toBe(false); - }); + expect(fs.rm).toHaveBeenCalled(); + expect(exceptionCatcher).not.toHaveBeenCalled(); }); }); describe('cleanArtifacts', function () { - beforeEach(function () {}); - afterEach(function () { - del.sync.restore(); + jest.resetAllMocks(); }); - it('should attempt to delete the working directory', function () { - sinon.stub(del, 'sync'); - + it('should attempt to delete the working directory', () => { cleanArtifacts(settings); - expect(del.sync.calledWith(settings.workingPath)).toBe(true); + + expect(rmSync).toHaveBeenCalledWith(settings.workingPath, expect.anything()); }); - it('should swallow any errors thrown by del.sync', function () { - sinon.stub(del, 'sync').throws(new Error('Something bad happened.')); + it('should swallow any errors thrown by del.sync', () => { + rmSync.mockImplementation(() => { + const dummyError = new Error('EDUMMY'); + dummyError.code = 'EDUMMY'; + throw dummyError; + }); expect(() => cleanArtifacts(settings)).not.toThrow(); }); diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 6ef1f30605a3..b8635f5a070f 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -50,6 +50,9 @@ class FakeApp implements App { const store = new Map(); const originalLocalStorage = window.localStorage; +// @ts-expect-error to allow redeclaring a readonly prop +delete window.localStorage; + (window as any).localStorage = { setItem: (key: string, value: string) => store.set(String(key), String(value)), getItem: (key: string) => store.get(String(key)), diff --git a/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts b/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts index 1a0772174af1..7046d5efc236 100644 --- a/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts +++ b/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts @@ -66,6 +66,9 @@ describe('RecentlyAccessed#start()', () => { let originalLocalStorage: Storage; beforeAll(() => { originalLocalStorage = window.localStorage; + + // @ts-expect-error to allow redeclaring a readonly prop + delete window.localStorage; // @ts-expect-error window.localStorage = new LocalStorageMock(); }); diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 6486e207ed37..4c2beb329a98 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -5415,14 +5415,10 @@ exports[`Header handles visibility and lock changes 1`] = ` aria-label="Help menu" onClick={[Function]} > - <EuiIcon - size="m" - type="questionInCircle" - /> <EuiIcon size="m" title="Help" - type="help" + type="questionInCircle" /> </EuiHeaderSectionItemButton> } @@ -5474,9 +5470,6 @@ exports[`Header handles visibility and lock changes 1`] = ` > <span data-euiicon-type="questionInCircle" - /> - <span - data-euiicon-type="help" title="Help" /> </span> @@ -5519,21 +5512,12 @@ exports[`Header handles visibility and lock changes 1`] = ` > <EuiIcon size="m" + title="Help" type="questionInCircle" > <span data-euiicon-type="questionInCircle" size="m" - /> - </EuiIcon> - <EuiIcon - size="m" - title="Help" - type="help" - > - <span - data-euiicon-type="help" - size="m" title="Help" /> </EuiIcon> @@ -10674,14 +10658,10 @@ exports[`Header renders condensed header 1`] = ` aria-label="Help menu" onClick={[Function]} > - <EuiIcon - size="m" - type="questionInCircle" - /> <EuiIcon size="m" title="Help" - type="help" + type="questionInCircle" /> </EuiHeaderSectionItemButton> } @@ -10733,9 +10713,6 @@ exports[`Header renders condensed header 1`] = ` > <span data-euiicon-type="questionInCircle" - /> - <span - data-euiicon-type="help" title="Help" /> </span> @@ -10778,21 +10755,12 @@ exports[`Header renders condensed header 1`] = ` > <EuiIcon size="m" + title="Help" type="questionInCircle" > <span data-euiicon-type="questionInCircle" size="m" - /> - </EuiIcon> - <EuiIcon - size="m" - title="Help" - type="help" - > - <span - data-euiicon-type="help" - size="m" title="Help" /> </EuiIcon> diff --git a/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap index 5406fbf6abaa..036e2b4ee0ce 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap @@ -1622,14 +1622,10 @@ exports[`Header help menu hides survey link 1`] = ` aria-label="Help menu" onClick={[Function]} > - <EuiIcon - size="m" - type="questionInCircle" - /> <EuiIcon size="m" title="Help" - type="help" + type="questionInCircle" /> </EuiHeaderSectionItemButton> } @@ -1681,9 +1677,6 @@ exports[`Header help menu hides survey link 1`] = ` > <span data-euiicon-type="questionInCircle" - /> - <span - data-euiicon-type="help" title="Help" /> </span> @@ -1726,21 +1719,12 @@ exports[`Header help menu hides survey link 1`] = ` > <EuiIcon size="m" + title="Help" type="questionInCircle" > <span data-euiicon-type="questionInCircle" size="m" - /> - </EuiIcon> - <EuiIcon - size="m" - title="Help" - type="help" - > - <span - data-euiicon-type="help" - size="m" title="Help" /> </EuiIcon> @@ -3798,14 +3782,10 @@ exports[`Header help menu renders survey link 1`] = ` aria-label="Help menu" onClick={[Function]} > - <EuiIcon - size="m" - type="questionInCircle" - /> <EuiIcon size="m" title="Help" - type="help" + type="questionInCircle" /> </EuiHeaderSectionItemButton> } @@ -3857,9 +3837,6 @@ exports[`Header help menu renders survey link 1`] = ` > <span data-euiicon-type="questionInCircle" - /> - <span - data-euiicon-type="help" title="Help" /> </span> @@ -3902,21 +3879,12 @@ exports[`Header help menu renders survey link 1`] = ` > <EuiIcon size="m" + title="Help" type="questionInCircle" > <span data-euiicon-type="questionInCircle" size="m" - /> - </EuiIcon> - <EuiIcon - size="m" - title="Help" - type="help" - > - <span - data-euiicon-type="help" - size="m" title="Help" /> </EuiIcon> diff --git a/src/core/public/chrome/ui/header/header_help_menu.tsx b/src/core/public/chrome/ui/header/header_help_menu.tsx index f0be769937f5..0eba4c0c2673 100644 --- a/src/core/public/chrome/ui/header/header_help_menu.tsx +++ b/src/core/public/chrome/ui/header/header_help_menu.tsx @@ -330,9 +330,8 @@ class HeaderHelpMenuUI extends Component<Props, State> { })} onClick={this.onMenuButtonClick} > - <EuiIcon type="questionInCircle" size="m" /> <EuiIcon - type="help" + type="questionInCircle" size="m" title={intl.formatMessage({ id: 'core.ui.chrome.headerGlobalNav.helpMenuButtonTitle', diff --git a/src/core/public/core_app/status/status_app.tsx b/src/core/public/core_app/status/status_app.tsx index a4663be7ec83..e15a47510fbc 100644 --- a/src/core/public/core_app/status/status_app.tsx +++ b/src/core/public/core_app/status/status_app.tsx @@ -58,6 +58,8 @@ interface StatusAppState { } export class StatusApp extends Component<StatusAppProps, StatusAppState> { + private _isMounted: boolean = false; + constructor(props: StatusAppProps) { super(props); this.state = { @@ -68,15 +70,25 @@ export class StatusApp extends Component<StatusAppProps, StatusAppState> { } async componentDidMount() { + this._isMounted = true; const { http, notifications } = this.props; try { const data = await loadStatus({ http, notifications }); + + if (!this._isMounted) return; + this.setState({ loading: false, fetchError: false, data }); } catch (e) { + if (!this._isMounted) return; + this.setState({ fetchError: true, loading: false, data: null }); } } + componentWillUnmount() { + this._isMounted = false; + } + render() { const { loading, fetchError, data } = this.state; diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 2faf70d54e82..7dd8c6561ade 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -421,7 +421,6 @@ export class DocLinksService { visualize: { // https://opensearch.org/docs/latest/dashboards/visualize/viz-index/ guide: `${OPENSEARCH_WEBSITE_DOCS}visualize/viz-index/`, - timelineDeprecation: `${OPENSEARCH_WEBSITE_DOCS}`, }, }, noDocumentation: { @@ -571,7 +570,6 @@ export class DocLinksService { reIndex: { rethrottle: `${OPENSEARCH_WEBSITE_DOCS}`, }, - timelineDeprecation: `${OPENSEARCH_WEBSITE_DOCS}`, apmServer: `${OPENSEARCH_WEBSITE_DOCS}`, tutorial: { loadDataTutorial: `${OPENSEARCH_WEBSITE_DOCS}`, @@ -919,7 +917,6 @@ export interface DocLinksStart { readonly reIndex: { readonly rethrottle: string; }; - readonly timelineDeprecation: string; readonly apmServer: string; readonly tutorial: { readonly loadDataTutorial: string; diff --git a/src/core/public/rendering/_base.scss b/src/core/public/rendering/_base.scss index e59008f08259..1333e48f6ca5 100644 --- a/src/core/public/rendering/_base.scss +++ b/src/core/public/rendering/_base.scss @@ -5,7 +5,6 @@ */ // SASSTODO: Naming here is too embedded and high up that changing them could cause major breaks #opensearch-dashboards-body { - overflow-x: hidden; min-height: 100%; } diff --git a/src/core/public/rendering/app_containers.test.tsx b/src/core/public/rendering/app_containers.test.tsx index fd43c79514c1..157253f5f757 100644 --- a/src/core/public/rendering/app_containers.test.tsx +++ b/src/core/public/rendering/app_containers.test.tsx @@ -43,6 +43,7 @@ describe('AppWrapper', () => { expect(component.getDOMNode()).toMatchInlineSnapshot(` <div class="app-wrapper" + id="app-wrapper" > app-content </div> @@ -53,6 +54,7 @@ describe('AppWrapper', () => { expect(component.getDOMNode()).toMatchInlineSnapshot(` <div class="app-wrapper hidden-chrome" + id="app-wrapper" > app-content </div> @@ -63,6 +65,7 @@ describe('AppWrapper', () => { expect(component.getDOMNode()).toMatchInlineSnapshot(` <div class="app-wrapper" + id="app-wrapper" > app-content </div> diff --git a/src/core/public/rendering/app_containers.tsx b/src/core/public/rendering/app_containers.tsx index dab85769bd0b..8ccf795f8dcf 100644 --- a/src/core/public/rendering/app_containers.tsx +++ b/src/core/public/rendering/app_containers.tsx @@ -37,7 +37,11 @@ export const AppWrapper: React.FunctionComponent<{ chromeVisible$: Observable<boolean>; }> = ({ chromeVisible$, children }) => { const visible = useObservable(chromeVisible$); - return <div className={classNames('app-wrapper', { 'hidden-chrome': !visible })}>{children}</div>; + return ( + <div id="app-wrapper" className={classNames('app-wrapper', { 'hidden-chrome': !visible })}> + {children} + </div> + ); }; export const AppContainer: React.FunctionComponent<{ diff --git a/src/core/server/bootstrap.ts b/src/core/server/bootstrap.ts index 8defb17d2030..b36caa518bb0 100644 --- a/src/core/server/bootstrap.ts +++ b/src/core/server/bootstrap.ts @@ -29,11 +29,14 @@ */ import chalk from 'chalk'; -import { isMaster as isClusterManager } from 'cluster'; +import cluster from 'cluster'; import { CliArgs, Env, RawConfigService } from './config'; import { Root } from './root'; import { CriticalError } from './errors'; +// ToDo: `isMaster` is a Node 14- prop; remove it when Node 18+ is the only engine supported +const isClusterManager = cluster.isPrimary ?? cluster.isMaster; + interface OpenSearchDashboardsFeatures { // Indicates whether we can run OpenSearch Dashboards in a so called cluster mode in which // OpenSearch Dashboards is run as a "worker" process together with optimizer "worker" process @@ -63,7 +66,7 @@ export async function bootstrap({ features, }: BootstrapArgs) { if (cliArgs.repl && !features.isReplModeSupported) { - onRootShutdown('OpenSearch Dashboards REPL mode can only be run in development mode.'); + terminate('OpenSearch Dashboards REPL mode can only be run in development mode.'); } if (cliArgs.optimize) { @@ -96,8 +99,8 @@ export async function bootstrap({ // This is only used by the LogRotator service // in order to be able to reload the log configuration // under the cluster mode - process.on('message', (msg) => { - if (!msg || msg.reloadLoggingConfig !== true) { + process.on('message', (msg: any) => { + if (msg?.reloadLoggingConfig !== true) { return; } @@ -133,16 +136,27 @@ export async function bootstrap({ } } +/* `onRootShutdown` is called multiple times due to catching and rethrowing of exceptions + * in Root and bootstrap. The debouncer below is to make sure every catch and rethrow is + * executed before calling `terminate`. + */ +let shutdownTimer: NodeJS.Timeout; function onRootShutdown(reason?: any) { + clearTimeout(shutdownTimer); + shutdownTimer = setTimeout(() => terminate(reason), 300); +} + +function terminate(reason?: any) { + const exitCode = + reason === undefined ? 0 : reason instanceof CriticalError ? reason.processExitCode : 1; + if (reason !== undefined) { // There is a chance that logger wasn't configured properly and error that // that forced root to shut down could go unnoticed. To prevent this we always // mirror such fatal errors in standard output with `console.error`. // eslint-disable-next-line console.error(`\n${chalk.white.bgRed(' FATAL ')} ${reason}\n`); - - process.exit(reason instanceof CriticalError ? reason.processExitCode : 1); } - process.exit(0); + process.exit(exitCode); } diff --git a/src/core/server/http/router/request.ts b/src/core/server/http/router/request.ts index 0091a320bd63..f31b658890a4 100644 --- a/src/core/server/http/router/request.ts +++ b/src/core/server/http/router/request.ts @@ -242,11 +242,7 @@ export class OpenSearchDashboardsRequest< } private getEvents(request: Request): OpenSearchDashboardsRequestEvents { - const finish$ = merge( - fromEvent(request.raw.res, 'finish'), // Response has been sent - fromEvent(request.raw.req, 'close') // connection was closed - ).pipe(shareReplay(1), first()); - + const finish$ = fromEvent(request.raw.res, 'finish').pipe(shareReplay(1), first()); const aborted$ = fromEvent<void>(request.raw.req, 'aborted').pipe(first(), takeUntil(finish$)); const completed$ = merge<void, void>(finish$, aborted$).pipe(shareReplay(1), first()); diff --git a/src/core/server/http/router/validator/validator.test.ts b/src/core/server/http/router/validator/validator.test.ts index bda6da118efd..4c0dd2009daf 100644 --- a/src/core/server/http/router/validator/validator.test.ts +++ b/src/core/server/http/router/validator/validator.test.ts @@ -31,6 +31,9 @@ import { RouteValidationError, RouteValidator } from './'; import { schema, Type } from '@osd/config-schema'; +// ToDo: Remove this logic when Node 14 support is removed +const isNode14 = process.version.startsWith('v14.'); + describe('Router validator', () => { it('should validate and infer the type from a function', () => { const validator = RouteValidator.from({ @@ -70,7 +73,9 @@ describe('Router validator', () => { expect(() => validator.getParams({})).toThrowError('[foo]: Not a string'); expect(() => validator.getParams(undefined)).toThrowError( - `Cannot read property 'foo' of undefined` + isNode14 + ? `Cannot read property 'foo' of undefined` + : `Cannot read properties of undefined (reading 'foo')` ); expect(() => validator.getParams({}, 'myField')).toThrowError('[myField.foo]: Not a string'); diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index 9c1412541a66..8ad4c738df44 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -31,6 +31,8 @@ jest.mock('../../../legacy/server/osd_server'); jest.mock('./cluster_manager'); +import '@osd/optimizer/target/__mocks__/lmdb'; + import { BehaviorSubject, throwError } from 'rxjs'; import { REPO_ROOT } from '@osd/dev-utils'; diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts index cce7a4ff4b49..1a0fd8a6d364 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts @@ -29,6 +29,7 @@ */ const realFs = jest.requireActual('fs'); +const realFsPromises = jest.requireActual('fs/promises'); export const mockReadFile = jest.fn(); const mockStat = jest.fn(); @@ -37,3 +38,11 @@ jest.doMock('fs', () => ({ readFile: mockReadFile, stat: mockStat, })); + +export const mockReadFilePromise = jest.fn(); +const mockStatPromise = jest.fn(); +jest.doMock('fs/promises', () => ({ + ...realFsPromises, + readFile: mockReadFilePromise, + stat: mockStatPromise, +})); diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts index 6103b2d748b6..18b715d95050 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts @@ -28,7 +28,7 @@ * under the License. */ -import { mockReadFile } from './plugin_manifest_parser.test.mocks'; +import { mockReadFilePromise } from './plugin_manifest_parser.test.mocks'; import { PluginDiscoveryErrorType } from './plugin_discovery_error'; import { loggingSystemMock } from '../../logging/logging_system.mock'; @@ -52,9 +52,7 @@ afterEach(() => { }); test('return error when manifest is empty', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from('')); - }); + mockReadFilePromise.mockResolvedValue(Buffer.from('')); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Unexpected end of JSON input (invalid-manifest, ${pluginManifestPath})`, @@ -64,9 +62,7 @@ test('return error when manifest is empty', async () => { }); test('return error when manifest content is null', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from('null')); - }); + mockReadFilePromise.mockResolvedValue(Buffer.from('null')); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Plugin manifest must contain a JSON encoded object. (invalid-manifest, ${pluginManifestPath})`, @@ -76,9 +72,7 @@ test('return error when manifest content is null', async () => { }); test('return error when manifest content is not a valid JSON', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from('not-json')); - }); + mockReadFilePromise.mockResolvedValue(Buffer.from('not-json')); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Unexpected token o in JSON at position 1 (invalid-manifest, ${pluginManifestPath})`, @@ -88,9 +82,7 @@ test('return error when manifest content is not a valid JSON', async () => { }); test('return error when plugin id is missing', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ version: 'some-version' }))); - }); + mockReadFilePromise.mockResolvedValue(Buffer.from(JSON.stringify({ version: 'some-version' }))); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Plugin manifest must contain an "id" property. (invalid-manifest, ${pluginManifestPath})`, @@ -100,9 +92,9 @@ test('return error when plugin id is missing', async () => { }); test('return error when plugin id includes `.` characters', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'some.name', version: 'some-version' }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'some.name', version: 'some-version' })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Plugin "id" must not include \`.\` characters. (invalid-manifest, ${pluginManifestPath})`, @@ -112,14 +104,9 @@ test('return error when plugin id includes `.` characters', async () => { }); test('logs warning if pluginId is not in camelCase format', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ id: 'some_name', version: 'opensearchDashboards', server: true }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'some_name', version: 'opensearchDashboards', server: true })) + ); expect(loggingSystemMock.collect(logger).warn).toHaveLength(0); await parseManifest(pluginPath, packageInfo, logger); @@ -133,14 +120,9 @@ test('logs warning if pluginId is not in camelCase format', async () => { }); test('does not log pluginId format warning in dist mode', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ id: 'some_name', version: 'opensearchDashboards', server: true }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'some_name', version: 'opensearchDashboards', server: true })) + ); expect(loggingSystemMock.collect(logger).warn).toHaveLength(0); await parseManifest(pluginPath, { ...packageInfo, dist: true }, logger); @@ -148,9 +130,7 @@ test('does not log pluginId format warning in dist mode', async () => { }); test('return error when plugin version is missing', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'someId' }))); - }); + mockReadFilePromise.mockResolvedValue(Buffer.from(JSON.stringify({ id: 'someId' }))); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Plugin manifest for "someId" must contain a "version" property. (invalid-manifest, ${pluginManifestPath})`, @@ -160,9 +140,9 @@ test('return error when plugin version is missing', async () => { }); test('return error when plugin expected OpenSearch Dashboards version is lower than actual version', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'someId', version: '6.4.2' }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'someId', version: '6.4.2' })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Plugin "someId" is only compatible with OpenSearch Dashboards version "6.4.2", but used OpenSearch Dashboards version is "7.0.0-alpha1". (incompatible-version, ${pluginManifestPath})`, @@ -172,18 +152,15 @@ test('return error when plugin expected OpenSearch Dashboards version is lower t }); test('return error when plugin expected OpenSearch Dashboards version cannot be interpreted as semver', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ - id: 'someId', - version: '1.0.0', - opensearchDashboardsVersion: 'non-sem-ver', - }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ + id: 'someId', + version: '1.0.0', + opensearchDashboardsVersion: 'non-sem-ver', + }) + ) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Plugin "someId" is only compatible with OpenSearch Dashboards version "non-sem-ver", but used OpenSearch Dashboards version is "7.0.0-alpha1". (incompatible-version, ${pluginManifestPath})`, @@ -193,9 +170,9 @@ test('return error when plugin expected OpenSearch Dashboards version cannot be }); test('return error when plugin config path is not a string', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', configPath: 2 }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', configPath: 2 })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `The "configPath" in plugin manifest for "someId" should either be a string or an array of strings. (invalid-manifest, ${pluginManifestPath})`, @@ -205,12 +182,9 @@ test('return error when plugin config path is not a string', async () => { }); test('return error when plugin config path is an array that contains non-string values', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', configPath: ['config', 2] })) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', configPath: ['config', 2] })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `The "configPath" in plugin manifest for "someId" should either be a string or an array of strings. (invalid-manifest, ${pluginManifestPath})`, @@ -220,9 +194,9 @@ test('return error when plugin config path is an array that contains non-string }); test('return error when plugin expected OpenSearch Dashboards version is higher than actual version', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.1' }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.1' })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Plugin "someId" is only compatible with OpenSearch Dashboards version "7.0.1", but used OpenSearch Dashboards version is "7.0.0-alpha1". (incompatible-version, ${pluginManifestPath})`, @@ -232,9 +206,9 @@ test('return error when plugin expected OpenSearch Dashboards version is higher }); test('return error when both `server` and `ui` are set to `false` or missing', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0' }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0' })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Both "server" and "ui" are missing or set to "false" in plugin manifest for "someId", but at least one of these must be set to "true". (invalid-manifest, ${pluginManifestPath})`, @@ -242,12 +216,9 @@ test('return error when both `server` and `ui` are set to `false` or missing', a path: pluginManifestPath, }); - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', server: false, ui: false })) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', server: false, ui: false })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Both "server" and "ui" are missing or set to "false" in plugin manifest for "someId", but at least one of these must be set to "true". (invalid-manifest, ${pluginManifestPath})`, @@ -257,20 +228,17 @@ test('return error when both `server` and `ui` are set to `false` or missing', a }); test('return error when manifest contains unrecognized properties', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ - id: 'someId', - version: '7.0.0', - server: true, - unknownOne: 'one', - unknownTwo: true, - }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ + id: 'someId', + version: '7.0.0', + server: true, + unknownOne: 'one', + unknownTwo: true, + }) + ) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ message: `Manifest for plugin "someId" contains the following unrecognized properties: unknownOne,unknownTwo. (invalid-manifest, ${pluginManifestPath})`, @@ -281,46 +249,40 @@ test('return error when manifest contains unrecognized properties', async () => describe('configPath', () => { test('falls back to plugin id if not specified', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'plugin', version: '7.0.0', server: true }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'plugin', version: '7.0.0', server: true })) + ); const manifest = await parseManifest(pluginPath, packageInfo, logger); expect(manifest.configPath).toBe(manifest.id); }); test('falls back to plugin id in snakeCase format', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'SomeId', version: '7.0.0', server: true }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'SomeId', version: '7.0.0', server: true })) + ); const manifest = await parseManifest(pluginPath, packageInfo, logger); expect(manifest.configPath).toBe('some_id'); }); test('not formated to snakeCase if defined explicitly as string', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ id: 'someId', configPath: 'somePath', version: '7.0.0', server: true }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ id: 'someId', configPath: 'somePath', version: '7.0.0', server: true }) + ) + ); const manifest = await parseManifest(pluginPath, packageInfo, logger); expect(manifest.configPath).toBe('somePath'); }); test('not formated to snakeCase if defined explicitly as an array of strings', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ id: 'someId', configPath: ['somePath'], version: '7.0.0', server: true }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ id: 'someId', configPath: ['somePath'], version: '7.0.0', server: true }) + ) + ); const manifest = await parseManifest(pluginPath, packageInfo, logger); expect(manifest.configPath).toEqual(['somePath']); @@ -328,9 +290,9 @@ describe('configPath', () => { }); test('set defaults for all missing optional fields', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb(null, Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', server: true }))); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'someId', version: '7.0.0', server: true })) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).resolves.toEqual({ id: 'someId', @@ -346,22 +308,19 @@ test('set defaults for all missing optional fields', async () => { }); test('return all set optional fields as they are in manifest', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ - id: 'someId', - configPath: ['some', 'path'], - version: 'some-version', - opensearchDashboardsVersion: '7.0.0', - requiredPlugins: ['some-required-plugin', 'some-required-plugin-2'], - optionalPlugins: ['some-optional-plugin'], - ui: true, - }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ + id: 'someId', + configPath: ['some', 'path'], + version: 'some-version', + opensearchDashboardsVersion: '7.0.0', + requiredPlugins: ['some-required-plugin', 'some-required-plugin-2'], + optionalPlugins: ['some-optional-plugin'], + ui: true, + }) + ) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).resolves.toEqual({ id: 'someId', @@ -377,21 +336,18 @@ test('return all set optional fields as they are in manifest', async () => { }); test('return manifest when plugin expected OpenSearch Dashboards version matches actual version', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ - id: 'someId', - configPath: 'some-path', - version: 'some-version', - opensearchDashboardsVersion: '7.0.0-alpha2', - requiredPlugins: ['some-required-plugin'], - server: true, - }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ + id: 'someId', + configPath: 'some-path', + version: 'some-version', + opensearchDashboardsVersion: '7.0.0-alpha2', + requiredPlugins: ['some-required-plugin'], + server: true, + }) + ) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).resolves.toEqual({ id: 'someId', @@ -407,21 +363,18 @@ test('return manifest when plugin expected OpenSearch Dashboards version matches }); test('return manifest when plugin expected OpenSearch Dashboards version is `opensearchDashboards`', async () => { - mockReadFile.mockImplementation((path, cb) => { - cb( - null, - Buffer.from( - JSON.stringify({ - id: 'someId', - version: 'some-version', - opensearchDashboardsVersion: 'opensearchDashboards', - requiredPlugins: ['some-required-plugin'], - server: true, - ui: true, - }) - ) - ); - }); + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ + id: 'someId', + version: 'some-version', + opensearchDashboardsVersion: 'opensearchDashboards', + requiredPlugins: ['some-required-plugin'], + server: true, + ui: true, + }) + ) + ); await expect(parseManifest(pluginPath, packageInfo, logger)).resolves.toEqual({ id: 'someId', diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.ts index 0f39f14b0ce5..2a5ccf611f0c 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.ts @@ -28,10 +28,9 @@ * under the License. */ -import { readFile, stat } from 'fs'; +import { readFile, stat } from 'fs/promises'; import { resolve } from 'path'; import { coerce } from 'semver'; -import { promisify } from 'util'; import { snakeCase } from 'lodash'; import { isConfigPath, PackageInfo } from '../../config'; import { Logger } from '../../logging'; @@ -39,9 +38,6 @@ import { PluginManifest } from '../types'; import { PluginDiscoveryError } from './plugin_discovery_error'; import { isCamelCase } from './is_camel_case'; -const fsReadFileAsync = promisify(readFile); -const fsStatAsync = promisify(stat); - /** * Name of the JSON manifest file that should be located in the plugin directory. */ @@ -92,7 +88,7 @@ export async function parseManifest( let manifestContent; try { - manifestContent = await fsReadFileAsync(manifestPath); + manifestContent = await readFile(manifestPath); } catch (err) { throw PluginDiscoveryError.missingManifest(manifestPath, err); } @@ -219,7 +215,7 @@ export async function parseManifest( */ export async function isNewPlatformPlugin(pluginPath: string) { try { - return (await fsStatAsync(resolve(pluginPath, MANIFEST_FILE_NAME))).isFile(); + return (await stat(resolve(pluginPath, MANIFEST_FILE_NAME))).isFile(); } catch (err) { return false; } diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts index ac2806748207..cf7e1d8246a7 100644 --- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts +++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts @@ -32,6 +32,9 @@ import { exportSavedObjectsToStream } from './get_sorted_objects_for_export'; import { savedObjectsClientMock } from '../service/saved_objects_client.mock'; import { Readable } from 'stream'; import { createPromiseFromStreams, createConcatStream } from '../../utils/streams'; +import { createStripPromisesSerializer } from '@osd/dev-utils'; + +expect.addSnapshotSerializer(createStripPromisesSerializer()); async function readStreamToCompletion(stream: Readable) { return createPromiseFromStreams([stream, createConcatStream([])]); @@ -89,27 +92,27 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "name", - "type": "index-pattern", + id: 1, + name: name, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "exportedCount": 2, - "missingRefCount": 0, - "missingReferences": Array [], + exportedCount: 2, + missingRefCount: 0, + missingReferences: Array [], }, ] `); @@ -118,20 +121,20 @@ describe('getSortedObjectsForExport()', () => { "calls": Array [ Array [ Object { - "namespaces": undefined, - "perPage": 500, - "search": undefined, - "type": Array [ - "index-pattern", - "search", + namespaces: undefined, + perPage: 500, + search: undefined, + type: Array [ + index-pattern, + search, ], }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -179,27 +182,27 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "name", - "type": "index-pattern", + id: 1, + name: name, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "exportedCount": 2, - "missingRefCount": 0, - "missingReferences": Array [], + exportedCount: 2, + missingRefCount: 0, + missingReferences: Array [], }, ] `); @@ -208,20 +211,20 @@ describe('getSortedObjectsForExport()', () => { "calls": Array [ Array [ Object { - "namespaces": undefined, - "perPage": 500, - "search": undefined, - "type": Array [ - "index-pattern", - "search", + namespaces: undefined, + perPage: 500, + search: undefined, + type: Array [ + index-pattern, + search, ], }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -268,22 +271,22 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "name", - "type": "index-pattern", + id: 1, + name: name, + type: index-pattern, }, ], - "type": "search", + type: search, }, ] `); @@ -329,27 +332,27 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "name", - "type": "index-pattern", + id: 1, + name: name, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "exportedCount": 2, - "missingRefCount": 0, - "missingReferences": Array [], + exportedCount: 2, + missingRefCount: 0, + missingReferences: Array [], }, ] `); @@ -358,20 +361,20 @@ describe('getSortedObjectsForExport()', () => { "calls": Array [ Array [ Object { - "namespaces": undefined, - "perPage": 500, - "search": "foo", - "type": Array [ - "index-pattern", - "search", + namespaces: undefined, + perPage: 500, + search: foo, + type: Array [ + index-pattern, + search, ], }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -418,27 +421,27 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "name", - "type": "index-pattern", + id: 1, + name: name, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "exportedCount": 2, - "missingRefCount": 0, - "missingReferences": Array [], + exportedCount: 2, + missingRefCount: 0, + missingReferences: Array [], }, ] `); @@ -447,22 +450,22 @@ describe('getSortedObjectsForExport()', () => { "calls": Array [ Array [ Object { - "namespaces": Array [ - "foo", + namespaces: Array [ + foo, ], - "perPage": 500, - "search": undefined, - "type": Array [ - "index-pattern", - "search", + perPage: 500, + search: undefined, + type: Array [ + index-pattern, + search, ], }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -503,7 +506,7 @@ describe('getSortedObjectsForExport()', () => { exportSizeLimit: 1, types: ['index-pattern', 'search'], }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Can't export more than 1 objects"`); + ).rejects.toThrowErrorMatchingInlineSnapshot(`Can't export more than 1 objects`); }); test('sorts objects within type', async () => { @@ -550,33 +553,33 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object { - "name": "foo", + attributes: Object { + name: foo, }, - "id": "1", - "references": Array [], - "type": "index-pattern", + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object { - "name": "bar", + attributes: Object { + name: bar, }, - "id": "2", - "references": Array [], - "type": "index-pattern", + id: 2, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object { - "name": "baz", + attributes: Object { + name: baz, }, - "id": "3", - "references": Array [], - "type": "index-pattern", + id: 3, + references: Array [], + type: index-pattern, }, Object { - "exportedCount": 3, - "missingRefCount": 0, - "missingReferences": Array [], + exportedCount: 3, + missingRefCount: 0, + missingReferences: Array [], }, ] `); @@ -623,57 +626,57 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "name", - "type": "index-pattern", + id: 1, + name: name, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "exportedCount": 2, - "missingRefCount": 0, - "missingReferences": Array [], + exportedCount: 2, + missingRefCount: 0, + missingReferences: Array [], }, ] `); expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "id": "1", - "type": "index-pattern", - }, - Object { - "id": "2", - "type": "search", - }, - ], - Object { - "namespace": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], - } - `); + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + id: 1, + type: index-pattern, + }, + Object { + id: 2, + type: search, + }, + ], + Object { + namespace: undefined, + }, + ], + ], + "results": Array [ + Object { + type: return, + value: Promise {}, + }, + ], + } + `); }); test('modifies return results to redact `namespaces` attribute', async () => { @@ -745,68 +748,68 @@ describe('getSortedObjectsForExport()', () => { expect(response).toMatchInlineSnapshot(` Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "name", - "type": "index-pattern", + id: 1, + name: name, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "exportedCount": 2, - "missingRefCount": 0, - "missingReferences": Array [], + exportedCount: 2, + missingRefCount: 0, + missingReferences: Array [], }, ] `); expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "id": "2", - "type": "search", - }, - ], - Object { - "namespace": undefined, - }, - ], - Array [ - Array [ - Object { - "id": "1", - "type": "index-pattern", - }, - ], - Object { - "namespace": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - Object { - "type": "return", - "value": Promise {}, - }, - ], - } - `); + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + id: 2, + type: search, + }, + ], + Object { + namespace: undefined, + }, + ], + Array [ + Array [ + Object { + id: 1, + type: index-pattern, + }, + ], + Object { + namespace: undefined, + }, + ], + ], + "results": Array [ + Object { + type: return, + value: Promise {}, + }, + Object { + type: return, + value: Promise {}, + }, + ], + } + `); }); test('export selected objects throws error when exceeding exportSizeLimit', async () => { @@ -825,7 +828,7 @@ describe('getSortedObjectsForExport()', () => { ], }; await expect(exportSavedObjectsToStream(exportOpts)).rejects.toThrowErrorMatchingInlineSnapshot( - `"Can't export more than 1 objects"` + `Can't export more than 1 objects` ); }); @@ -838,7 +841,7 @@ describe('getSortedObjectsForExport()', () => { }; expect(exportSavedObjectsToStream(exportOpts)).rejects.toThrowErrorMatchingInlineSnapshot( - `"Either \`type\` or \`objects\` are required."` + `Either \`type\` or \`objects\` are required.` ); }); @@ -851,7 +854,7 @@ describe('getSortedObjectsForExport()', () => { }; expect(exportSavedObjectsToStream(exportOpts)).rejects.toThrowErrorMatchingInlineSnapshot( - `"Can't specify both \\"search\\" and \\"objects\\" properties when exporting"` + `Can't specify both "search" and "objects" properties when exporting` ); }); }); diff --git a/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts b/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts index 587491280285..00c81e55244e 100644 --- a/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts +++ b/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts @@ -32,6 +32,10 @@ import { SavedObject } from '../types'; import { savedObjectsClientMock } from '../../mocks'; import { getObjectReferencesToFetch, fetchNestedDependencies } from './inject_nested_depdendencies'; import { SavedObjectsErrorHelpers } from '..'; +import { createStripPromisesSerializer } from '@osd/dev-utils'; + +// This is to clean Promises +expect.addSnapshotSerializer(createStripPromisesSerializer()); describe('getObjectReferencesToFetch()', () => { test('works with no saved objects', () => { @@ -82,8 +86,8 @@ describe('getObjectReferencesToFetch()', () => { expect(result).toMatchInlineSnapshot(` Array [ Object { - "id": "1", - "type": "index-pattern", + id: 1, + type: index-pattern, }, ] `); @@ -139,13 +143,13 @@ describe('injectNestedDependencies', () => { const result = await fetchNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` Object { - "missingRefs": Array [], - "objects": Array [ + missingRefs: Array [], + objects: Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, ], } @@ -176,25 +180,25 @@ describe('injectNestedDependencies', () => { const result = await fetchNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` Object { - "missingRefs": Array [], - "objects": Array [ + missingRefs: Array [], + objects: Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", + id: 1, + name: ref_0, + type: index-pattern, }, ], - "type": "search", + type: search, }, ], } @@ -229,25 +233,25 @@ describe('injectNestedDependencies', () => { const result = await fetchNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` Object { - "missingRefs": Array [], - "objects": Array [ + missingRefs: Array [], + objects: Array [ Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", + id: 1, + name: ref_0, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, ], } @@ -258,19 +262,19 @@ describe('injectNestedDependencies', () => { Array [ Array [ Object { - "id": "1", - "type": "index-pattern", + id: 1, + type: index-pattern, }, ], Object { - "namespace": undefined, + namespace: undefined, }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -350,66 +354,66 @@ describe('injectNestedDependencies', () => { const result = await fetchNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` Object { - "missingRefs": Array [], - "objects": Array [ + missingRefs: Array [], + objects: Array [ Object { - "attributes": Object {}, - "id": "5", - "references": Array [ + attributes: Object {}, + id: 5, + references: Array [ Object { - "id": "4", - "name": "panel_0", - "type": "visualization", + id: 4, + name: panel_0, + type: visualization, }, Object { - "id": "3", - "name": "panel_1", - "type": "visualization", + id: 3, + name: panel_1, + type: visualization, }, ], - "type": "dashboard", + type: dashboard, }, Object { - "attributes": Object {}, - "id": "4", - "references": Array [ + attributes: Object {}, + id: 4, + references: Array [ Object { - "id": "2", - "name": "ref_0", - "type": "search", + id: 2, + name: ref_0, + type: search, }, ], - "type": "visualization", + type: visualization, }, Object { - "attributes": Object {}, - "id": "3", - "references": Array [ + attributes: Object {}, + id: 3, + references: Array [ Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", + id: 1, + name: ref_0, + type: index-pattern, }, ], - "type": "visualization", + type: visualization, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", + id: 1, + name: ref_0, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 1, + references: Array [], + type: index-pattern, }, ], } @@ -420,42 +424,42 @@ describe('injectNestedDependencies', () => { Array [ Array [ Object { - "id": "4", - "type": "visualization", + id: 4, + type: visualization, }, Object { - "id": "3", - "type": "visualization", + id: 3, + type: visualization, }, ], Object { - "namespace": undefined, + namespace: undefined, }, ], Array [ Array [ Object { - "id": "2", - "type": "search", + id: 2, + type: search, }, Object { - "id": "1", - "type": "index-pattern", + id: 1, + type: index-pattern, }, ], Object { - "namespace": undefined, + namespace: undefined, }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -503,35 +507,35 @@ describe('injectNestedDependencies', () => { const result = await fetchNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` Object { - "missingRefs": Array [ + missingRefs: Array [ Object { - "id": "1", - "type": "index-pattern", + id: 1, + type: index-pattern, }, ], - "objects": Array [ + objects: Array [ Object { - "attributes": Object {}, - "id": "1", - "references": Array [ + attributes: Object {}, + id: 1, + references: Array [ Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", + id: 1, + name: ref_0, + type: index-pattern, }, Object { - "id": "2", - "name": "ref_1", - "type": "index-pattern", + id: 2, + name: ref_1, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "attributes": Object {}, - "id": "2", - "references": Array [], - "type": "index-pattern", + attributes: Object {}, + id: 2, + references: Array [], + type: index-pattern, }, ], } @@ -572,31 +576,31 @@ describe('injectNestedDependencies', () => { const result = await fetchNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` Object { - "missingRefs": Array [], - "objects": Array [ + missingRefs: Array [], + objects: Array [ Object { - "attributes": Object {}, - "id": "2", - "references": Array [ + attributes: Object {}, + id: 2, + references: Array [ Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", + id: 1, + name: ref_0, + type: index-pattern, }, ], - "type": "search", + type: search, }, Object { - "attributes": Object {}, - "id": "1", - "references": Array [ + attributes: Object {}, + id: 1, + references: Array [ Object { - "id": "2", - "name": "ref_0", - "type": "search", + id: 2, + name: ref_0, + type: search, }, ], - "type": "index-pattern", + type: index-pattern, }, ], } @@ -607,19 +611,19 @@ describe('injectNestedDependencies', () => { Array [ Array [ Object { - "id": "1", - "type": "index-pattern", + id: 1, + type: index-pattern, }, ], Object { - "namespace": undefined, + namespace: undefined, }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } diff --git a/src/core/server/saved_objects/import/validate_references.test.ts b/src/core/server/saved_objects/import/validate_references.test.ts index 8b9a4ebee4c1..df9a4e216bbb 100644 --- a/src/core/server/saved_objects/import/validate_references.test.ts +++ b/src/core/server/saved_objects/import/validate_references.test.ts @@ -31,7 +31,9 @@ import { getNonExistingReferenceAsKeys, validateReferences } from './validate_references'; import { savedObjectsClientMock } from '../../mocks'; import { SavedObjectsErrorHelpers } from '..'; +import { createStripPromisesSerializer } from '@osd/dev-utils'; +expect.addSnapshotSerializer(createStripPromisesSerializer()); describe('getNonExistingReferenceAsKeys()', () => { const savedObjectsClient = savedObjectsClientMock.create(); @@ -132,22 +134,22 @@ describe('getNonExistingReferenceAsKeys()', () => { Array [ Array [ Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "1", - "type": "index-pattern", + id: 1, + type: index-pattern, }, ], Object { - "namespace": undefined, + namespace: undefined, }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -226,29 +228,29 @@ describe('getNonExistingReferenceAsKeys()', () => { Array [ Array [ Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "1", - "type": "index-pattern", + id: 1, + type: index-pattern, }, Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "3", - "type": "search", + id: 3, + type: search, }, ], Object { - "namespace": undefined, + namespace: undefined, }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -366,46 +368,46 @@ describe('validateReferences()', () => { expect(result).toMatchInlineSnapshot(` Array [ Object { - "error": Object { - "references": Array [ + error: Object { + references: Array [ Object { - "id": "3", - "type": "index-pattern", + id: 3, + type: index-pattern, }, ], - "type": "missing_references", + type: missing_references, }, - "id": "2", - "meta": Object { - "title": "My Visualization 2", + id: 2, + meta: Object { + title: My Visualization 2, }, - "title": "My Visualization 2", - "type": "visualization", + title: My Visualization 2, + type: visualization, }, Object { - "error": Object { - "references": Array [ + error: Object { + references: Array [ Object { - "id": "5", - "type": "index-pattern", + id: 5, + type: index-pattern, }, Object { - "id": "6", - "type": "index-pattern", + id: 6, + type: index-pattern, }, Object { - "id": "7", - "type": "search", + id: 7, + type: search, }, ], - "type": "missing_references", + type: missing_references, }, - "id": "4", - "meta": Object { - "title": "My Visualization 4", + id: 4, + meta: Object { + title: My Visualization 4, }, - "title": "My Visualization 4", - "type": "visualization", + title: My Visualization 4, + type: visualization, }, ] `); @@ -415,50 +417,50 @@ describe('validateReferences()', () => { Array [ Array [ Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "3", - "type": "index-pattern", + id: 3, + type: index-pattern, }, Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "5", - "type": "index-pattern", + id: 5, + type: index-pattern, }, Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "6", - "type": "index-pattern", + id: 6, + type: index-pattern, }, Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "7", - "type": "search", + id: 7, + type: search, }, Object { - "fields": Array [ - "id", + fields: Array [ + id, ], - "id": "8", - "type": "search", + id: 8, + type: search, }, ], Object { - "namespace": undefined, + namespace: undefined, }, ], ], "results": Array [ Object { - "type": "return", - "value": Promise {}, + type: return, + value: Promise {}, }, ], } @@ -597,6 +599,6 @@ describe('validateReferences()', () => { ]; await expect( validateReferences(savedObjects, savedObjectsClient) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Bad Request"`); + ).rejects.toThrowErrorMatchingInlineSnapshot(`Bad Request`); }); }); diff --git a/src/core/server/status/plugins_status.ts b/src/core/server/status/plugins_status.ts index 52ba85459658..b4fe9bcd0835 100644 --- a/src/core/server/status/plugins_status.ts +++ b/src/core/server/status/plugins_status.ts @@ -43,7 +43,7 @@ interface Deps { export class PluginsStatusService { private readonly pluginStatuses = new Map<PluginName, Observable<ServiceStatus>>(); - private readonly update$ = new BehaviorSubject(true); + private readonly update$ = new BehaviorSubject<boolean>(true); private readonly defaultInheritedStatus$: Observable<ServiceStatus>; constructor(private readonly deps: Deps) { @@ -121,7 +121,7 @@ export class PluginsStatusService { return combineLatest(pluginStatuses).pipe( map((statuses) => Object.fromEntries(statuses)), - distinctUntilChanged(isDeepStrictEqual) + distinctUntilChanged<Record<PluginName, ServiceStatus>>(isDeepStrictEqual) ); }) ); diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index b128bfb7218c..10547e510fa6 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -95,7 +95,7 @@ export class StatusService implements CoreService<InternalStatusServiceSetup> { this.logger.debug(`Recalculated overall status`, { status: summary }); return summary; }), - distinctUntilChanged(isDeepStrictEqual), + distinctUntilChanged<ServiceStatus<unknown>>(isDeepStrictEqual), shareReplay(1) ); @@ -149,7 +149,7 @@ export class StatusService implements CoreService<InternalStatusServiceSetup> { opensearch: opensearchStatus, savedObjects: savedObjectsStatus, })), - distinctUntilChanged(isDeepStrictEqual), + distinctUntilChanged<CoreStatus>(isDeepStrictEqual), shareReplay(1) ); } diff --git a/src/core/server/ui_settings/ui_settings_client.test.ts b/src/core/server/ui_settings/ui_settings_client.test.ts index a7aa95ebab91..9108467fd04c 100644 --- a/src/core/server/ui_settings/ui_settings_client.test.ts +++ b/src/core/server/ui_settings/ui_settings_client.test.ts @@ -168,7 +168,7 @@ describe('ui settings', () => { foo: 1, }) ).rejects.toMatchInlineSnapshot( - `[Error: [validation [foo]]: expected value of type [string] but got [number]]` + `[ValidationError: [validation [foo]]: expected value of type [string] but got [number]]` ); expect(savedObjectsClient.update).toHaveBeenCalledTimes(0); @@ -196,7 +196,7 @@ describe('ui settings', () => { const { uiSettings, savedObjectsClient } = setup({ defaults }); await expect(uiSettings.set('foo', 1)).rejects.toMatchInlineSnapshot( - `[Error: [validation [foo]]: expected value of type [string] but got [number]]` + `[ValidationError: [validation [foo]]: expected value of type [string] but got [number]]` ); expect(savedObjectsClient.update).toHaveBeenCalledTimes(0); @@ -389,7 +389,7 @@ describe('ui settings', () => { expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` Array [ Array [ - "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + "Ignore invalid UiSettings value. ValidationError: [validation [id]]: expected value of type [number] but got [string].", ], ] `); @@ -531,7 +531,7 @@ describe('ui settings', () => { expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` Array [ Array [ - "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + "Ignore invalid UiSettings value. ValidationError: [validation [id]]: expected value of type [number] but got [string].", ], ] `); @@ -682,7 +682,7 @@ describe('ui settings', () => { expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` Array [ Array [ - "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + "Ignore invalid UiSettings value. ValidationError: [validation [id]]: expected value of type [number] but got [string].", ], ] `); diff --git a/src/core/server/ui_settings/ui_settings_service.test.ts b/src/core/server/ui_settings/ui_settings_service.test.ts index ae881363e63f..e32429de7abe 100644 --- a/src/core/server/ui_settings/ui_settings_service.test.ts +++ b/src/core/server/ui_settings/ui_settings_service.test.ts @@ -111,7 +111,7 @@ describe('uiSettings', () => { }); await expect(service.start()).rejects.toMatchInlineSnapshot( - `[Error: [ui settings defaults [custom]]: expected value of type [string] but got [number]]` + `[ValidationError: [ui settings defaults [custom]]: expected value of type [string] but got [number]]` ); }); @@ -134,7 +134,7 @@ describe('uiSettings', () => { }); await expect(customizedService.start()).rejects.toMatchInlineSnapshot( - `[Error: [ui settings overrides [custom]]: expected value of type [string] but got [number]]` + `[ValidationError: [ui settings overrides [custom]]: expected value of type [string] but got [number]]` ); }); }); diff --git a/src/core/test_helpers/osd_server.ts b/src/core/test_helpers/osd_server.ts index 5c062d76dcac..8f03ac949842 100644 --- a/src/core/test_helpers/osd_server.ts +++ b/src/core/test_helpers/osd_server.ts @@ -52,6 +52,7 @@ export type HttpMethod = 'delete' | 'get' | 'head' | 'post' | 'put'; const DEFAULTS_SETTINGS = { server: { autoListen: true, + host: '0.0.0.0', // Use the ephemeral port to make sure that tests use the first available // port and aren't affected by the timing issues in test environment. port: 0, diff --git a/src/dev/build/lib/build.test.ts b/src/dev/build/lib/build.test.ts index 140237437ad1..96509df78aed 100644 --- a/src/dev/build/lib/build.test.ts +++ b/src/dev/build/lib/build.test.ts @@ -42,6 +42,7 @@ const config = new Config( darwin: false, linux: false, linuxArm: false, + windows: false, }, { version: '1.0.0', @@ -53,6 +54,7 @@ const config = new Config( }, }, '1.2.3', + '1.2.3', REPO_ROOT, { buildNumber: 1234, diff --git a/src/dev/build/lib/config.ts b/src/dev/build/lib/config.ts index 03fdd14b05c0..6af5b8e6901a 100644 --- a/src/dev/build/lib/config.ts +++ b/src/dev/build/lib/config.ts @@ -31,6 +31,7 @@ import { dirname, resolve, relative } from 'path'; import os from 'os'; import loadJsonFile from 'load-json-file'; +import { readFile } from 'fs/promises'; import { getVersionInfo, VersionInfo } from './version_info'; import { @@ -67,11 +68,15 @@ export class Config { const pkgPath = resolve(__dirname, '../../../../package.json'); const pkg: Package = loadJsonFile.sync(pkgPath); + const nvmrcPath = resolve(__dirname, '../../../../.nvmrc'); + const nvmrcContent = (await readFile(nvmrcPath, 'utf8'))?.trim?.(); + return new Config( targetAllPlatforms, targetPlatforms, pkg, pkg.engines.node, + nvmrcContent, dirname(pkgPath), await getVersionInfo({ isRelease, @@ -87,6 +92,7 @@ export class Config { private readonly targetPlatforms: TargetPlatforms, private readonly pkg: Package, private readonly nodeRange: string, + private readonly nodeVersion: string, private readonly repoRoot: string, private readonly versionInfo: VersionInfo, public readonly isRelease: boolean @@ -100,12 +106,19 @@ export class Config { } /** - * Get the node version required by OpenSearch Dashboards + * Get the node version range compatible with OpenSearch Dashboards */ getNodeRange() { return this.nodeRange; } + /** + * Get the node version required by OpenSearch Dashboards + */ + getNodeVersion() { + return this.nodeVersion; + } + /** * Convert an absolute path to a relative path, based from the repo */ diff --git a/src/dev/build/tasks/bin/scripts/opensearch-dashboards b/src/dev/build/tasks/bin/scripts/opensearch-dashboards index 49c41011da7d..c7eae94b4996 100755 --- a/src/dev/build/tasks/bin/scripts/opensearch-dashboards +++ b/src/dev/build/tasks/bin/scripts/opensearch-dashboards @@ -28,4 +28,4 @@ done # Get an absolute path for OSD_HOME OSD_HOME="$(cd "$(dirname "${SCRIPT}")/.."; pwd)" -OSD_NODE_OPTS_PREFIX="--no-warnings --max-http-header-size=65536" OSD_USE_NODE_JS_FILE_PATH=/src/cli/dist NODE_ENV=production ${OSD_HOME}/bin/use_node "${@}" +OSD_NODE_OPTS_PREFIX="--no-warnings --max-http-header-size=65536" OSD_USE_NODE_JS_FILE_PATH=/src/cli/dist NODE_ENV=production exec ${OSD_HOME}/bin/use_node "${@}" diff --git a/src/dev/build/tasks/bin/scripts/opensearch-dashboards-plugin b/src/dev/build/tasks/bin/scripts/opensearch-dashboards-plugin index 6af40438d13b..a8d9829cc3c1 100755 --- a/src/dev/build/tasks/bin/scripts/opensearch-dashboards-plugin +++ b/src/dev/build/tasks/bin/scripts/opensearch-dashboards-plugin @@ -28,4 +28,4 @@ done # Get an absolute path for OSD_HOME OSD_HOME="$(cd "$(dirname "${SCRIPT}")/.."; pwd)" -OSD_NODE_OPTS_PREFIX="--no-warnings" OSD_USE_NODE_JS_FILE_PATH=/src/cli_plugin/dist NODE_ENV=production ${OSD_HOME}/bin/use_node "${@}" +OSD_NODE_OPTS_PREFIX="--no-warnings" OSD_USE_NODE_JS_FILE_PATH=/src/cli_plugin/dist NODE_ENV=production exec ${OSD_HOME}/bin/use_node "${@}" diff --git a/src/dev/build/tasks/create_archives_sources_task.ts b/src/dev/build/tasks/create_archives_sources_task.ts index bf53c2915560..55d9b5313f12 100644 --- a/src/dev/build/tasks/create_archives_sources_task.ts +++ b/src/dev/build/tasks/create_archives_sources_task.ts @@ -29,7 +29,7 @@ */ import { scanCopy, Task } from '../lib'; -import { getNodeDownloadInfo } from './nodejs'; +import { getNodeDownloadInfo, getNodeVersionDownloadInfo, NODE14_FALLBACK_VERSION } from './nodejs'; export const CreateArchivesSources: Task = { description: 'Creating platform-specific archive source directories', @@ -54,6 +54,20 @@ export const CreateArchivesSources: Task = { destination: build.resolvePathForPlatform(platform, 'node'), }); + // ToDo [NODE14]: Remove this Node.js 14 fallback download + // Copy the Node.js 14 binaries into node/fallback to be used by `use_node` + await scanCopy({ + source: ( + await getNodeVersionDownloadInfo( + NODE14_FALLBACK_VERSION, + platform.getNodeArch(), + platform.isWindows(), + config.resolveFromRepo() + ) + ).extractDir, + destination: build.resolvePathForPlatform(platform, 'node', 'fallback'), + }); + log.debug('Node.js copied into', platform.getNodeArch(), 'specific build directory'); }) ); diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts index 3aff61e7cf4f..f5905534e121 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts @@ -44,7 +44,9 @@ jest.mock('../../lib/get_build_number'); expect.addSnapshotSerializer(createAnyInstanceSerializer(ToolingLog)); -const { getNodeDownloadInfo } = jest.requireMock('./node_download_info'); +const { getNodeDownloadInfo, getNodeVersionDownloadInfo } = jest.requireMock( + './node_download_info' +); const { getNodeShasums } = jest.requireMock('./node_shasums'); const { download } = jest.requireMock('../../lib/download'); @@ -76,6 +78,16 @@ async function setup({ failOnUrl }: { failOnUrl?: string } = {}) { }; }); + getNodeVersionDownloadInfo.mockImplementation((version, architecture, isWindows, repoRoot) => { + return { + url: `https://mirrors.nodejs.org/dist/v${version}/node-v${version}-${architecture}.tar.gz`, + downloadName: `node-v${version}-${architecture}.tar.gz`, + downloadPath: `/mocked/path/.node_binaries/${version}/node-v${version}-${architecture}.tar.gz`, + extractDir: `/mocked/path/.node_binaries/${version}/${architecture}`, + version, + }; + }); + getNodeShasums.mockReturnValue({ 'linux:downloadName': 'linux:sha256', 'darwin:downloadName': 'darwin:sha256', @@ -134,6 +146,42 @@ it('downloads node builds for each platform', async () => { "url": "win32:url", }, ], + Array [ + Object { + "destination": "/mocked/path/.node_binaries/14.21.3/node-v14.21.3-linux-x64.tar.gz", + "log": <ToolingLog>, + "retries": 3, + "sha256": undefined, + "url": "https://mirrors.nodejs.org/dist/v14.21.3/node-v14.21.3-linux-x64.tar.gz", + }, + ], + Array [ + Object { + "destination": "/mocked/path/.node_binaries/14.21.3/node-v14.21.3-linux-arm64.tar.gz", + "log": <ToolingLog>, + "retries": 3, + "sha256": undefined, + "url": "https://mirrors.nodejs.org/dist/v14.21.3/node-v14.21.3-linux-arm64.tar.gz", + }, + ], + Array [ + Object { + "destination": "/mocked/path/.node_binaries/14.21.3/node-v14.21.3-darwin-x64.tar.gz", + "log": <ToolingLog>, + "retries": 3, + "sha256": undefined, + "url": "https://mirrors.nodejs.org/dist/v14.21.3/node-v14.21.3-darwin-x64.tar.gz", + }, + ], + Array [ + Object { + "destination": "/mocked/path/.node_binaries/14.21.3/node-v14.21.3-win32-x64.tar.gz", + "log": <ToolingLog>, + "retries": 3, + "sha256": undefined, + "url": "https://mirrors.nodejs.org/dist/v14.21.3/node-v14.21.3-win32-x64.tar.gz", + }, + ], ] `); expect(testWriter.messages).toMatchInlineSnapshot(`Array []`); diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.ts b/src/dev/build/tasks/nodejs/download_node_builds_task.ts index 9771e92e209f..393a02176e17 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.ts @@ -30,16 +30,25 @@ import { download, GlobalTask } from '../../lib'; import { getNodeShasums } from './node_shasums'; -import { getLatestNodeVersion, getNodeDownloadInfo } from './node_download_info'; +import { + getNodeDownloadInfo, + getNodeVersionDownloadInfo, + getRequiredVersion, + NODE14_FALLBACK_VERSION, +} from './node_download_info'; export const DownloadNodeBuilds: GlobalTask = { global: true, description: 'Downloading node.js builds for all platforms', async run(config, log) { - const latestNodeVersion = await getLatestNodeVersion(config); - const shasums = await getNodeShasums(log, latestNodeVersion); - await Promise.all( - config.getTargetPlatforms().map(async (platform) => { + const requiredNodeVersion = getRequiredVersion(config); + const shasums = await getNodeShasums(log, requiredNodeVersion); + + // ToDo [NODE14]: Remove this Node.js 14 fallback download + const node14ShaSums = await getNodeShasums(log, NODE14_FALLBACK_VERSION); + + await Promise.all([ + ...config.getTargetPlatforms().map(async (platform) => { const { url, downloadPath, downloadName } = await getNodeDownloadInfo(config, platform); await download({ log, @@ -48,7 +57,23 @@ export const DownloadNodeBuilds: GlobalTask = { destination: downloadPath, retries: 3, }); - }) - ); + }), + // ToDo [NODE14]: Remove this Node.js 14 fallback download + ...config.getTargetPlatforms().map(async (platform) => { + const { url, downloadPath, downloadName } = await getNodeVersionDownloadInfo( + NODE14_FALLBACK_VERSION, + platform.getNodeArch(), + platform.isWindows(), + config.resolveFromRepo() + ); + await download({ + log, + url, + sha256: node14ShaSums[downloadName], + destination: downloadPath, + retries: 3, + }); + }), + ]); }, }; diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts index 02a6a7bfa723..e68539310903 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts @@ -37,7 +37,7 @@ import { import { Config } from '../../lib'; import { ExtractNodeBuilds } from './extract_node_builds_task'; -import { getLatestNodeVersion } from './node_download_info'; +import { getRequiredVersion } from './node_download_info'; jest.mock('../../lib/fs'); jest.mock('../../lib/get_build_number'); @@ -62,7 +62,7 @@ async function setup() { }, }); - const realNodeVersion = await getLatestNodeVersion(config); + const realNodeVersion = getRequiredVersion(config); if (realNodeVersion) { expect.addSnapshotSerializer( createRecursiveSerializer( @@ -123,6 +123,27 @@ it('runs expected fs operations', async () => { "strip": 1, }, ], + Array [ + <absolute path>/.node_binaries/14.21.3/node-v14.21.3-linux-x64.tar.gz, + <absolute path>/.node_binaries/14.21.3/linux-x64, + Object { + "strip": 1, + }, + ], + Array [ + <absolute path>/.node_binaries/14.21.3/node-v14.21.3-linux-arm64.tar.gz, + <absolute path>/.node_binaries/14.21.3/linux-arm64, + Object { + "strip": 1, + }, + ], + Array [ + <absolute path>/.node_binaries/14.21.3/node-v14.21.3-darwin-x64.tar.gz, + <absolute path>/.node_binaries/14.21.3/darwin-x64, + Object { + "strip": 1, + }, + ], ], "unzip": Array [ Array [ @@ -132,6 +153,13 @@ it('runs expected fs operations', async () => { "strip": 1, }, ], + Array [ + <absolute path>/.node_binaries/14.21.3/node-v14.21.3-win-x64.zip, + <absolute path>/.node_binaries/14.21.3/win32-x64, + Object { + "strip": 1, + }, + ], ], } `); diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.ts b/src/dev/build/tasks/nodejs/extract_node_builds_task.ts index 28b2ebe24d4b..7934718c0bce 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.ts @@ -29,21 +29,39 @@ */ import { untar, unzip, GlobalTask } from '../../lib'; -import { getNodeDownloadInfo } from './node_download_info'; +import { + getNodeDownloadInfo, + getNodeVersionDownloadInfo, + NODE14_FALLBACK_VERSION, +} from './node_download_info'; export const ExtractNodeBuilds: GlobalTask = { global: true, description: 'Extracting node.js builds for all platforms', async run(config) { - await Promise.all( - config.getTargetPlatforms().map(async (platform) => { + await Promise.all([ + ...config.getTargetPlatforms().map(async (platform) => { const { downloadPath, extractDir } = await getNodeDownloadInfo(config, platform); if (platform.isWindows()) { await unzip(downloadPath, extractDir, { strip: 1 }); } else { await untar(downloadPath, extractDir, { strip: 1 }); } - }) - ); + }), + // ToDo [NODE14]: Remove this Node.js 14 fallback download + ...config.getTargetPlatforms().map(async (platform) => { + const { downloadPath, extractDir } = await getNodeVersionDownloadInfo( + NODE14_FALLBACK_VERSION, + platform.getNodeArch(), + platform.isWindows(), + config.resolveFromRepo() + ); + if (platform.isWindows()) { + await unzip(downloadPath, extractDir, { strip: 1 }); + } else { + await untar(downloadPath, extractDir, { strip: 1 }); + } + }), + ]); }, }; diff --git a/src/dev/build/tasks/nodejs/node_download_info.ts b/src/dev/build/tasks/nodejs/node_download_info.ts index 73163bc3c30a..37eba4c29a58 100644 --- a/src/dev/build/tasks/nodejs/node_download_info.ts +++ b/src/dev/build/tasks/nodejs/node_download_info.ts @@ -28,7 +28,7 @@ * under the License. */ -import { basename } from 'path'; +import { basename, resolve } from 'path'; import fetch from 'node-fetch'; import semver from 'semver'; @@ -36,8 +36,10 @@ import { Config, Platform } from '../../lib'; const NODE_RANGE_CACHE: { [key: string]: string } = {}; +export const NODE14_FALLBACK_VERSION = '14.21.3'; + export async function getNodeDownloadInfo(config: Config, platform: Platform) { - const version = await getLatestNodeVersion(config); + const version = getRequiredVersion(config); const arch = platform.getNodeArch(); const downloadName = platform.isWindows() @@ -57,6 +59,29 @@ export async function getNodeDownloadInfo(config: Config, platform: Platform) { }; } +export async function getNodeVersionDownloadInfo( + version: string, + architecture: string, + isWindows: boolean, + repoRoot: string +) { + const downloadName = isWindows + ? `node-v${version}-win-x64.zip` + : `node-v${version}-${architecture}.tar.gz`; + + const url = `https://mirrors.nodejs.org/dist/v${version}/${downloadName}`; + const downloadPath = resolve(repoRoot, '.node_binaries', version, basename(downloadName)); + const extractDir = resolve(repoRoot, '.node_binaries', version, architecture); + + return { + url, + downloadName, + downloadPath, + extractDir, + version, + }; +} + export async function getLatestNodeVersion(config: Config) { const range = config.getNodeRange(); // Check cache and return if known @@ -76,3 +101,7 @@ export async function getLatestNodeVersion(config: Config) { return maxVersion; } + +export const getRequiredVersion = (config: Config) => { + return config.getNodeVersion(); +}; diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts index 4724fa73e9f0..1d516ce457ad 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts @@ -43,7 +43,9 @@ jest.mock('../../lib/fs'); jest.mock('../../lib/get_build_number'); const { getNodeShasums } = jest.requireMock('./node_shasums'); -const { getNodeDownloadInfo, getLatestNodeVersion } = jest.requireMock('./node_download_info'); +const { getNodeDownloadInfo, getLatestNodeVersion, getRequiredVersion } = jest.requireMock( + './node_download_info' +); const { getFileHash } = jest.requireMock('../../lib/fs'); const log = new ToolingLog(); @@ -83,6 +85,8 @@ async function setup(actualShaSums?: Record<string, string>) { getLatestNodeVersion.mockReturnValue('<node version>'); + getRequiredVersion.mockReturnValue('<node version>'); + getFileHash.mockImplementation((downloadPath: string) => { if (actualShaSums?.[downloadPath]) { return actualShaSums[downloadPath]; diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts index 4c64c4bbd25d..9eec2d4af8ce 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts @@ -29,15 +29,15 @@ */ import { getFileHash, GlobalTask } from '../../lib'; -import { getNodeDownloadInfo, getLatestNodeVersion } from './node_download_info'; +import { getNodeDownloadInfo, getRequiredVersion } from './node_download_info'; import { getNodeShasums } from './node_shasums'; export const VerifyExistingNodeBuilds: GlobalTask = { global: true, description: 'Verifying previously downloaded node.js build for all platforms', async run(config, log) { - const latestNodeVersion = await getLatestNodeVersion(config); - const shasums = await getNodeShasums(log, latestNodeVersion); + const requiredNodeVersion = getRequiredVersion(config); + const shasums = await getNodeShasums(log, requiredNodeVersion); await Promise.all( config.getTargetPlatforms().map(async (platform) => { diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/opensearch-dashboards-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/opensearch-dashboards-docker index de9ec4e4b5de..124a5e074842 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/opensearch-dashboards-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/opensearch-dashboards-docker @@ -135,7 +135,6 @@ opensearch_dashboards_vars=( tilemap.options.minZoom tilemap.options.subdomains tilemap.url - timeline.enabled vega.enableExternalUrls apm_oss.apmAgentConfigurationIndex apm_oss.indexPattern diff --git a/src/dev/build/tasks/patch_native_modules_task.test.ts b/src/dev/build/tasks/patch_native_modules_task.test.ts index f3a3daa432c3..71553d39cd9c 100644 --- a/src/dev/build/tasks/patch_native_modules_task.test.ts +++ b/src/dev/build/tasks/patch_native_modules_task.test.ts @@ -9,8 +9,8 @@ import { createAnyInstanceSerializer, createAbsolutePathSerializer, } from '@osd/dev-utils'; -import { Build, Config } from '../lib'; -import { PatchNativeModules } from './patch_native_modules_task'; +import { Build, Config, read, download, untar, gunzip } from '../lib'; +import { createPatchNativeModulesTask } from './patch_native_modules_task'; const log = new ToolingLog(); const testWriter = new ToolingLogCollectingWriter(); @@ -19,16 +19,16 @@ expect.addSnapshotSerializer(createAnyInstanceSerializer(Config)); expect.addSnapshotSerializer(createAnyInstanceSerializer(ToolingLog)); expect.addSnapshotSerializer(createAbsolutePathSerializer()); -jest.mock('../lib/download'); -jest.mock('../lib/fs', () => ({ - ...jest.requireActual('../lib/fs'), - untar: jest.fn(), - gunzip: jest.fn(), -})); - -const { untar } = jest.requireMock('../lib/fs'); -const { gunzip } = jest.requireMock('../lib/fs'); -const { download } = jest.requireMock('../lib/download'); +jest.mock('../lib', () => { + const originalModule = jest.requireActual('../lib'); + return { + ...originalModule, + download: jest.fn(), + gunzip: jest.fn(), + untar: jest.fn(), + read: jest.fn(), + }; +}); async function setup() { const config = await Config.create({ @@ -38,14 +38,15 @@ async function setup() { linux: false, linuxArm: false, darwin: false, + windows: false, }, }); const build = new Build(config); - download.mockImplementation(() => {}); - untar.mockImplementation(() => {}); - gunzip.mockImplementation(() => {}); + (read as jest.MockedFunction<typeof read>).mockImplementation(async () => { + return JSON.stringify({ version: mockPackage.version }); + }); return { config, build }; } @@ -55,38 +56,77 @@ beforeEach(() => { jest.clearAllMocks(); }); -it('patch native modules task downloads the correct platform package', async () => { - const { config, build } = await setup(); - config.targetPlatforms.linuxArm = true; - await PatchNativeModules.run(config, log, build); - expect(download.mock.calls.length).toBe(1); - expect(download.mock.calls).toMatchInlineSnapshot(` +const mockPackage = { + name: 'mock-native-module', + version: '1.0.0', + destinationPath: 'path/to/destination', + extractMethod: 'untar', + archives: { + 'linux-arm64': { + url: 'https://example.com/mock-native-module/linux-arm64.tar.gz', + sha256: 'mock-sha256', + }, + 'linux-x64': { + url: 'https://example.com/mock-native-module/linux-x64.gz', + sha256: 'mock-sha256', + }, + }, +}; + +describe('patch native modules task', () => { + it('patch native modules task downloads the correct platform package', async () => { + const { config, build } = await setup(); + config.targetPlatforms.linuxArm = true; + const PatchNativeModulesWithMock = createPatchNativeModulesTask([mockPackage]); + await PatchNativeModulesWithMock.run(config, log, build); + expect((download as jest.MockedFunction<typeof download>).mock.calls.length).toBe(1); + expect((download as jest.MockedFunction<typeof download>).mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { - "destination": <absolute path>/.native_modules/re2/linux-arm64-83.tar.gz, + "destination": <absolute path>/.native_modules/mock-native-module/linux-arm64.tar.gz, "log": <ToolingLog>, "retries": 3, - "sha256": "d86ced75b794fbf518b90908847b3c09a50f3ff5a2815aa30f53080f926a2873", - "url": "https://d1v1sj258etie.cloudfront.net/node-re2/releases/download/1.17.4/linux-arm64-83.tar.gz", + "sha256": "mock-sha256", + "url": "https://example.com/mock-native-module/linux-arm64.tar.gz", }, ], ] `); -}); + }); -it('for .tar.gz artifact, patch native modules task unzip it via untar', async () => { - const { config, build } = await setup(); - config.targetPlatforms.linuxArm = true; - await PatchNativeModules.run(config, log, build); - expect(untar.mock.calls.length).toBe(1); - expect(gunzip.mock.calls.length).toBe(0); -}); + it('for .tar.gz artifact, patch native modules task unzip it via untar', async () => { + const { config, build } = await setup(); + config.targetPlatforms.linuxArm = true; + const PatchNativeModulesWithMock = createPatchNativeModulesTask([mockPackage]); + await PatchNativeModulesWithMock.run(config, log, build); + expect(untar).toHaveBeenCalled(); + expect(gunzip).not.toHaveBeenCalled(); + }); -it('for .gz artifact, patch native modules task unzip it via gunzip', async () => { - const { config, build } = await setup(); - config.targetPlatforms.linux = true; - await PatchNativeModules.run(config, log, build); - expect(untar.mock.calls.length).toBe(0); - expect(gunzip.mock.calls.length).toBe(1); + it('for .gz artifact, patch native modules task unzip it via gunzip', async () => { + const mockPackageGZ = { + ...mockPackage, + extractMethod: 'gunzip', + }; + const { config, build } = await setup(); + config.targetPlatforms.linux = true; + const PatchNativeModulesWithMock = createPatchNativeModulesTask([mockPackageGZ]); + await PatchNativeModulesWithMock.run(config, log, build); + expect(gunzip).toHaveBeenCalled(); + expect(untar).not.toHaveBeenCalled(); + }); + + it('throws error for unsupported extract methods', async () => { + const mockPackageUnsupported = { + ...mockPackage, + extractMethod: 'unsupported', + }; + const { config, build } = await setup(); + config.targetPlatforms.linux = true; + const PatchNativeModulesWithMock = createPatchNativeModulesTask([mockPackageUnsupported]); + await expect(PatchNativeModulesWithMock.run(config, log, build)).rejects.toThrow( + 'Extract method of unsupported is not supported' + ); + }); }); diff --git a/src/dev/build/tasks/patch_native_modules_task.ts b/src/dev/build/tasks/patch_native_modules_task.ts index 3bd9fa63c358..b8c8d8a5b9fb 100644 --- a/src/dev/build/tasks/patch_native_modules_task.ts +++ b/src/dev/build/tasks/patch_native_modules_task.ts @@ -52,45 +52,7 @@ interface Package { >; } -/* Process for updating URLs and checksums after bumping the version of `re2` or NodeJS: - * 1. Match the `version` with the version in the yarn.lock file. - * 2. Match the module version, the digits at the end of the filename, with the output of - * `node -p process.versions.modules`. - * 3. Confirm that the URLs exist for each platform-architecture combo on - * https://github.com/uhop/node-re2/releases/tag/[VERSION]; reach out to maintainers for ARM - * releases of `re2` as they currently don't have an official ARM release. - * 4. Generate new checksums for each artifact by downloading each one and calling - * `shasum -a 256` or `sha256sum` on the downloaded file. - */ -const packages: Package[] = [ - { - name: 're2', - version: '1.17.4', - destinationPath: 'node_modules/re2/build/Release/re2.node', - extractMethod: 'gunzip', - archives: { - 'darwin-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.17.4/darwin-x64-83.gz', - sha256: '9112ed93c1544ecc6397f7ff20bd2b28f3b04c7fbb54024e10f9a376a132a87d', - }, - 'linux-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.17.4/linux-x64-83.gz', - sha256: '86e03540783a18c41f81df0aec320b1f64aca6cbd3a87fc1b7a9b4109c5f5986', - }, - 'linux-arm64': { - url: - 'https://d1v1sj258etie.cloudfront.net/node-re2/releases/download/1.17.4/linux-arm64-83.tar.gz', - sha256: 'd86ced75b794fbf518b90908847b3c09a50f3ff5a2815aa30f53080f926a2873', - overriddenExtractMethod: 'untar', - overriddenDestinationPath: 'node_modules/re2/build/Release', - }, - 'win32-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.17.4/win32-x64-83.gz', - sha256: '2f842d9757288afd4bd5dec0e7b370a4c3e89ac98050598b17abb9e8e00e3294', - }, - }, - }, -]; +export const packages: Package[] = []; async function getInstalledVersion(config: Config, packageName: string) { const packageJSONPath = config.resolveFromRepo( @@ -145,15 +107,20 @@ async function patchModule( } } -export const PatchNativeModules: Task = { - description: 'Patching platform-specific native modules', - async run(config, log, build) { - for (const pkg of packages) { - await Promise.all( - config.getTargetPlatforms().map(async (platform) => { - await patchModule(config, log, build, platform, pkg); - }) - ); - } - }, -}; +export function createPatchNativeModulesTask(customPackages?: Package[]): Task { + return { + description: 'Patching platform-specific native modules', + async run(config, log, build) { + const targetPackages = customPackages || packages; + for (const pkg of targetPackages) { + await Promise.all( + config.getTargetPlatforms().map(async (platform) => { + await patchModule(config, log, build, platform, pkg); + }) + ); + } + }, + }; +} + +export const PatchNativeModules = createPatchNativeModulesTask(); diff --git a/src/dev/jest/junit_reporter.js b/src/dev/jest/junit_reporter.js index 65f244042a12..1add8e722a5a 100644 --- a/src/dev/jest/junit_reporter.js +++ b/src/dev/jest/junit_reporter.js @@ -53,7 +53,7 @@ export default class JestJUnitReporter { /** * Called by jest when all tests complete * @param {Object} contexts - * @param {JestResults} results see https://facebook.github.io/jest/docs/en/configuration.html#testresultsprocessor-string + * @param {JestResults} results see https://jestjs.io/docs/configuration/#testresultsprocessor-string * @return {undefined} */ onRunComplete(contexts, results) { diff --git a/src/dev/stylelint/lint_files.js b/src/dev/stylelint/lint_files.js index ead1fde5bb23..2d94f98b396c 100644 --- a/src/dev/stylelint/lint_files.js +++ b/src/dev/stylelint/lint_files.js @@ -30,13 +30,13 @@ import stylelint from 'stylelint'; import path from 'path'; -import { safeLoad } from 'js-yaml'; +import { load } from 'js-yaml'; import fs from 'fs'; import { createFailError } from '@osd/dev-utils'; // load the include globs from .stylelintrc.yml and convert them to regular expressions for filtering files const stylelintPath = path.resolve(__dirname, '..', '..', '..', '.stylelintrc.yml'); -const styleLintConfig = safeLoad(fs.readFileSync(stylelintPath)); +const styleLintConfig = load(fs.readFileSync(stylelintPath)); /** * Lints a list of files with eslint. eslint reports are written to the log diff --git a/src/legacy/server/logging/rotate/log_rotator.test.ts b/src/legacy/server/logging/rotate/log_rotator.test.ts index 9dbe960d405d..efb499352635 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.ts +++ b/src/legacy/server/logging/rotate/log_rotator.test.ts @@ -72,9 +72,12 @@ describe('LogRotator', () => { writeFileSync(testFilePath, ''); }); - afterEach(() => { + afterEach((done) => { del.sync(dirname(testFilePath), { force: true }); mockOn.mockClear(); + + // Tests occasionally fail complaining that `testFilePath` doesn't exist; this tries to help + setTimeout(done, 500); }); it('rotates log file when bigger than set limit on start', async () => { diff --git a/src/legacy/server/logging/rotate/log_rotator.ts b/src/legacy/server/logging/rotate/log_rotator.ts index 7502631d22dd..ac2123036b44 100644 --- a/src/legacy/server/logging/rotate/log_rotator.ts +++ b/src/legacy/server/logging/rotate/log_rotator.ts @@ -97,7 +97,7 @@ export class LogRotator { await this._startLogFileSizeMonitor(); } - stop = () => { + async stop() { if (!this.running) { return; } @@ -106,10 +106,10 @@ export class LogRotator { this._deleteExitListener(); // stop log file size monitor - this._stopLogFileSizeMonitor(); + await this._stopLogFileSizeMonitor(); this.running = false; - }; + } async _shouldUsePolling() { try { @@ -202,12 +202,12 @@ export class LogRotator { await this.throttledRotate(); }; - _stopLogFileSizeMonitor() { + async _stopLogFileSizeMonitor() { if (!this.stalker) { return; } - this.stalker.close(); + await this.stalker.close(); if (this.stalkerUsePollingPolicyTestTimeout) { clearTimeout(this.stalkerUsePollingPolicyTestTimeout); 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 82704fc93062..a33082c3ef64 100644 --- a/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss +++ b/src/plugins/advanced_settings/public/management_app/_advanced_settings.scss @@ -76,7 +76,3 @@ .mgtAdvancedSettingsForm__button { width: 100%; } - -.osdBody--mgtAdvancedSettingsHasBottomBar .mgtPage__body { - padding-bottom: $euiSizeXL * 2; -} diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx index c5a8a89157fe..4f17ab378d3c 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx @@ -36,7 +36,7 @@ import { FieldSetting } from '../../types'; import { UiSettingsType, StringValidation } from '../../../../../../core/public'; import { notificationServiceMock, docLinksServiceMock } from '../../../../../../core/public/mocks'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { Field, getEditableValue } from './field'; jest.mock('brace/theme/textmate', () => 'brace/theme/textmate'); diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx index 650d8b259f0c..a0edaa5ab602 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx @@ -29,15 +29,21 @@ */ import React from 'react'; +import ReactDOM from 'react-dom'; import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers'; import { UiSettingsType } from '../../../../../../core/public'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { notificationServiceMock } from '../../../../../../core/public/mocks'; import { SettingsChanges } from '../../types'; import { Form } from './form'; +jest.mock('react-dom', () => ({ + ...jest.requireActual('react-dom'), + createPortal: jest.fn((element) => element), +})); + jest.mock('../field', () => ({ Field: () => { return 'field'; @@ -45,6 +51,8 @@ jest.mock('../field', () => ({ })); beforeAll(() => { + ReactDOM.createPortal = jest.fn((children: any) => children); + const localStorage: Record<string, any> = { 'core.chrome.isLocked': true, }; @@ -60,6 +68,7 @@ beforeAll(() => { }); afterAll(() => { + (ReactDOM.createPortal as jest.Mock).mockClear(); delete (window as any).localStorage; }); diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx index 92b7a792d2d2..a74199771d2a 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx @@ -47,8 +47,9 @@ import { import { FormattedMessage } from '@osd/i18n/react'; import { isEmpty } from 'lodash'; import { i18n } from '@osd/i18n'; +import { DocLinksStart, ToastsStart } from 'opensearch-dashboards/public'; +import { createPortal } from 'react-dom'; import { toMountPoint } from '../../../../../opensearch_dashboards_react/public'; -import { DocLinksStart, ToastsStart } from '../../../../../../core/public'; import { getCategoryName } from '../../lib'; import { Field, getEditableValue } from '../field'; @@ -336,63 +337,69 @@ export class Form extends PureComponent<FormProps> { }; renderBottomBar = () => { - const areChangesInvalid = this.areChangesInvalid(); - return ( - <EuiBottomBar data-test-subj="advancedSetting-bottomBar"> - <EuiFlexGroup - justifyContent="spaceBetween" - alignItems="center" - responsive={false} - gutterSize="s" - > - <EuiFlexItem grow={false} className="mgtAdvancedSettingsForm__unsavedCount"> - <p id="aria-describedby.countOfUnsavedSettings">{this.renderCountOfUnsaved()}</p> - </EuiFlexItem> - <EuiFlexItem /> - <EuiFlexItem grow={false}> - <EuiButtonEmpty - color="ghost" - size="s" - iconType="cross" - onClick={this.clearAllUnsaved} - aria-describedby="aria-describedby.countOfUnsavedSettings" - data-test-subj="advancedSetting-cancelButton" - > - {i18n.translate('advancedSettings.form.cancelButtonLabel', { - defaultMessage: 'Cancel changes', - })} - </EuiButtonEmpty> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiToolTip - content={ - areChangesInvalid && - i18n.translate('advancedSettings.form.saveButtonTooltipWithInvalidChanges', { - defaultMessage: 'Fix invalid settings before saving.', - }) - } - > - <EuiButton - className="mgtAdvancedSettingsForm__button" - disabled={areChangesInvalid} - color="secondary" - fill + try { + const areChangesInvalid = this.areChangesInvalid(); + const bottomBar = ( + <EuiBottomBar data-test-subj="advancedSetting-bottomBar" position="sticky"> + <EuiFlexGroup + justifyContent="spaceBetween" + alignItems="center" + responsive={false} + gutterSize="s" + > + <EuiFlexItem grow={false} className="mgtAdvancedSettingsForm__unsavedCount"> + <p id="aria-describedby.countOfUnsavedSettings">{this.renderCountOfUnsaved()}</p> + </EuiFlexItem> + <EuiFlexItem /> + <EuiFlexItem grow={false}> + <EuiButtonEmpty + color="ghost" size="s" - iconType="check" - onClick={this.saveAll} + iconType="cross" + onClick={this.clearAllUnsaved} aria-describedby="aria-describedby.countOfUnsavedSettings" - isLoading={this.state.loading} - data-test-subj="advancedSetting-saveButton" + data-test-subj="advancedSetting-cancelButton" > - {i18n.translate('advancedSettings.form.saveButtonLabel', { - defaultMessage: 'Save changes', + {i18n.translate('advancedSettings.form.cancelButtonLabel', { + defaultMessage: 'Cancel changes', })} - </EuiButton> - </EuiToolTip> - </EuiFlexItem> - </EuiFlexGroup> - </EuiBottomBar> - ); + </EuiButtonEmpty> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiToolTip + content={ + areChangesInvalid && + i18n.translate('advancedSettings.form.saveButtonTooltipWithInvalidChanges', { + defaultMessage: 'Fix invalid settings before saving.', + }) + } + > + <EuiButton + className="mgtAdvancedSettingsForm__button" + disabled={areChangesInvalid} + color="secondary" + fill + size="s" + iconType="check" + onClick={this.saveAll} + aria-describedby="aria-describedby.countOfUnsavedSettings" + isLoading={this.state.loading} + data-test-subj="advancedSetting-saveButton" + > + {i18n.translate('advancedSettings.form.saveButtonLabel', { + defaultMessage: 'Save changes', + })} + </EuiButton> + </EuiToolTip> + </EuiFlexItem> + </EuiFlexGroup> + </EuiBottomBar> + ); + + return createPortal(bottomBar, document.getElementById('app-wrapper')!); + } catch (e) { + return null; + } }; render() { @@ -401,12 +408,6 @@ export class Form extends PureComponent<FormProps> { const currentCategories: Category[] = []; const hasUnsavedChanges = !isEmpty(unsavedChanges); - if (hasUnsavedChanges) { - document.body.classList.add('osdBody--mgtAdvancedSettingsHasBottomBar'); - } else { - document.body.classList.remove('osdBody--mgtAdvancedSettingsHasBottomBar'); - } - categories.forEach((category) => { if (visibleSettings[category] && visibleSettings[category].length) { currentCategories.push(category); diff --git a/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx b/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx index 34b69888b3be..f362af408e0c 100644 --- a/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx @@ -31,7 +31,7 @@ import React from 'react'; import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { Query } from '@elastic/eui'; import { Search } from './search'; diff --git a/src/plugins/advanced_settings/public/management_app/components/search/search.tsx b/src/plugins/advanced_settings/public/management_app/components/search/search.tsx index a957fa793f8f..0b1de3bbfb24 100644 --- a/src/plugins/advanced_settings/public/management_app/components/search/search.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/search/search.tsx @@ -31,7 +31,6 @@ import React, { Fragment, PureComponent } from 'react'; import { i18n } from '@osd/i18n'; import { EuiSearchBar, EuiFormErrorText, Query } from '@elastic/eui'; - import { getCategoryName } from '../../lib'; interface SearchProps { @@ -111,6 +110,7 @@ export class Search extends PureComponent<SearchProps> { return ( <Fragment> + {/* @ts-ignore The Query types that typescript complains about here are identical and is a false flag. Once OUI migration is complete, this ignore can be removed */} <EuiSearchBar box={box} filters={filters} onChange={this.onChange} query={query} /> {queryParseError} </Fragment> diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 876e94d4cbcb..1c47cc41e920 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -231,7 +231,6 @@ function EditorUI({ initialTextValue, dataSourceId }: EditorProps) { return ( <div style={abs} className="conApp"> <div className="conApp__editor"> - <ul className="conApp__autoComplete" id="autocomplete" /> <EuiFlexGroup className="conApp__editorActions" id="ConAppEditorActions" diff --git a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input.test.js b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input.test.js index 653e34aa0073..7accc948e6cd 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input.test.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input.test.js @@ -31,6 +31,7 @@ import '../legacy_core_editor.test.mocks'; import RowParser from '../../../../lib/row_parser'; import { createTokenIterator } from '../../../factories'; +import $ from 'jquery'; import { create } from '../create'; describe('Input', () => { @@ -45,10 +46,10 @@ describe('Input', () => { coreEditor = create(document.querySelector('#ConAppEditor')); - coreEditor.getContainer().style.display = ''; + $(coreEditor.getContainer()).show(); }); afterEach(() => { - coreEditor.getContainer().style.display = 'none'; + $(coreEditor.getContainer()).hide(); }); describe('.getLineCount', () => { diff --git a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js index d143d72e15c2..4973011a2aaa 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/output_tokenization.test.js @@ -29,6 +29,7 @@ */ import '../legacy_core_editor.test.mocks'; +import $ from 'jquery'; import RowParser from '../../../../lib/row_parser'; import ace from 'brace'; import { createReadOnlyAceEditor } from '../create_readonly'; @@ -38,11 +39,11 @@ const tokenIterator = ace.acequire('ace/token_iterator'); describe('Output Tokenization', () => { beforeEach(() => { output = createReadOnlyAceEditor(document.querySelector('#ConAppOutput')); - output.container.style.display = ''; + $(output.container).show(); }); afterEach(() => { - output.container.style.display = 'none'; + $(output.container).hide(); }); function tokensAsList() { diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 5fe93ca4e094..55ee5fe2a343 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -30,6 +30,7 @@ import ace from 'brace'; import { Editor as IAceEditor, IEditSession as IAceEditSession } from 'brace'; +import $ from 'jquery'; import { CoreEditor, Position, @@ -53,11 +54,11 @@ const rangeToAceRange = ({ start, end }: Range) => export class LegacyCoreEditor implements CoreEditor { private _aceOnPaste: any; - actions: any; + $actions: any; resize: () => void; constructor(private readonly editor: IAceEditor, actions: HTMLElement) { - this.actions = actions; + this.$actions = $(actions); this.editor.setShowPrintMargin(false); const session = this.editor.getSession(); @@ -273,16 +274,20 @@ export class LegacyCoreEditor implements CoreEditor { private setActionsBar = (value?: any, topOrBottom: 'top' | 'bottom' = 'top') => { if (value === null) { - this.actions.style.visibility = 'hidden'; + this.$actions.css('visibility', 'hidden'); } else { if (topOrBottom === 'top') { - this.actions.style.bottom = 'auto'; - this.actions.style.top = value; - this.actions.style.visibility = 'visible'; + this.$actions.css({ + bottom: 'auto', + top: value, + visibility: 'visible', + }); } else { - this.actions.style.top = 'auto'; - this.actions.style.bottom = value; - this.actions.style.visibility = 'visible'; + this.$actions.css({ + top: 'auto', + bottom: value, + visibility: 'visible', + }); } } }; @@ -313,14 +318,14 @@ export class LegacyCoreEditor implements CoreEditor { } legacyUpdateUI(range: any) { - if (!this.actions) { + if (!this.$actions) { return; } if (range) { // elements are positioned relative to the editor's container // pageY is relative to page, so subtract the offset // from pageY to get the new top value - const offsetFromPage = this.editor.container.offsetTop; + const offsetFromPage = $(this.editor.container).offset()!.top; const startLine = range.start.lineNumber; const startColumn = range.start.column; const firstLine = this.getLineValue(startLine); @@ -340,11 +345,11 @@ export class LegacyCoreEditor implements CoreEditor { let offset = 0; if (isWrapping) { // Try get the line height of the text area in pixels. - const textArea = this.editor.container.querySelector('textArea'); + const textArea = $(this.editor.container.querySelector('textArea')!); const hasRoomOnNextLine = this.getLineValue(startLine).length < maxLineLength; if (textArea && hasRoomOnNextLine) { // Line height + the number of wraps we have on a line. - offset += this.getLineValue(startLine).length * textArea.getBoundingClientRect().height; + offset += this.getLineValue(startLine).length * textArea.height()!; } else { if (startLine > 1) { this.setActionsBar(getScreenCoords(startLine - 1)); diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js index 88f9acc27e7f..cf6df4d31b06 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js @@ -44,11 +44,11 @@ describe('Integration', () => { '<div><div id="ConAppEditor" /><div id="ConAppEditorActions" /><div id="ConCopyAsCurl" /></div>'; senseEditor = create(document.querySelector('#ConAppEditor')); - senseEditor.getCoreEditor().getContainer().style.display = ''; + $(senseEditor.getCoreEditor().getContainer()).show(); senseEditor.autocomplete._test.removeChangeListener(); }); afterEach(() => { - senseEditor.getCoreEditor().getContainer().style.display = 'none'; + $(senseEditor.getCoreEditor().getContainer()).hide(); senseEditor.autocomplete._test.addChangeListener(); }); diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js index de67ae1a8908..18d798c28c94 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js @@ -30,6 +30,7 @@ import '../sense_editor.test.mocks'; +import $ from 'jquery'; import _ from 'lodash'; import { create } from '../create'; @@ -50,11 +51,11 @@ describe('Editor', () => { </div>`; input = create(document.querySelector('#ConAppEditor')); - input.getCoreEditor().getContainer().style.display = ''; + $(input.getCoreEditor().getContainer()).show(); input.autocomplete._test.removeChangeListener(); }); afterEach(function () { - input.getCoreEditor().getContainer().style.display = 'none'; + $(input.getCoreEditor().getContainer()).hide(); input.autocomplete._test.addChangeListener(); }); diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts index 92e86d60104c..183878e7a582 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts @@ -31,3 +31,9 @@ /* eslint no-undef: 0 */ import '../legacy_core_editor/legacy_core_editor.test.mocks'; + +global.fetch = jest.fn(() => + Promise.resolve({ + json: () => Promise.resolve({}), + }) +); diff --git a/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts b/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts index a933fdeb8010..55c18381cb9c 100644 --- a/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts +++ b/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts @@ -30,6 +30,8 @@ import '../../application/models/sense_editor/sense_editor.test.mocks'; +import $ from 'jquery'; + // TODO: // We import from application models as a convenient way to bootstrap loading up of an editor using // this lib. We also need to import application specific mocks which is not ideal. @@ -57,14 +59,14 @@ describe('Ace (legacy) token provider', () => { senseEditor = create(document.querySelector<HTMLElement>('#ConAppEditor')!); - senseEditor.getCoreEditor().getContainer().style.display = ''; + $(senseEditor.getCoreEditor().getContainer())!.show(); (senseEditor as any).autocomplete._test.removeChangeListener(); tokenProvider = senseEditor.getCoreEditor().getTokenProvider(); }); afterEach(async () => { - senseEditor.getCoreEditor().getContainer().style.display = 'none'; + $(senseEditor.getCoreEditor().getContainer())!.hide(); (senseEditor as any).autocomplete._test.addChangeListener(); await senseEditor.update('', true); }); diff --git a/src/plugins/console/public/lib/osd/osd.js b/src/plugins/console/public/lib/osd/osd.js index cf8126271072..529fba754a93 100644 --- a/src/plugins/console/public/lib/osd/osd.js +++ b/src/plugins/console/public/lib/osd/osd.js @@ -38,6 +38,7 @@ import { UsernameAutocompleteComponent, } from '../autocomplete/components'; +import $ from 'jquery'; import _ from 'lodash'; import Api from './api'; @@ -173,19 +174,20 @@ function loadApisFromJson( // like this, it looks like a minor security issue. export function setActiveApi(api) { if (!api) { - fetch('../api/console/api_server', { - method: 'GET', + $.ajax({ + url: '../api/console/api_server', + dataType: 'json', // disable automatic guessing headers: { 'osd-xsrf': 'opensearch-dashboards', }, - }) - .then(function (response) { - response.json(); - }) - .then(function (data) { + }).then( + function (data) { setActiveApi(loadApisFromJson(data)); - }) - .catch((error) => console.log(`failed to load API '${api}': ${error}`)); + }, + function (jqXHR) { + console.log("failed to load API '" + api + "': " + jqXHR.responseText); + } + ); return; } diff --git a/src/plugins/console/public/styles/_app.scss b/src/plugins/console/public/styles/_app.scss index c149832ec1db..161d1f200afd 100644 --- a/src/plugins/console/public/styles/_app.scss +++ b/src/plugins/console/public/styles/_app.scss @@ -77,18 +77,6 @@ z-index: $euiZLevel1; } -// SASSTODO: This component seems to not be used anymore? -// Possibly replaced by the Ace version -.conApp__autoComplete { - position: absolute; - left: -1000px; - visibility: hidden; - - /* by pass any other element in ace and resize bar, but not modal popups */ - z-index: $euiZLevel1 + 2; - margin-top: 22px; -} - .conApp__settingsModal { min-width: 460px; } diff --git a/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx b/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx index fd4291b29455..ac22b069135c 100644 --- a/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx +++ b/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx @@ -31,7 +31,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { coreMock } from '../../../../core/public/mocks'; describe('DashboardEmptyScreen', () => { diff --git a/src/plugins/dashboard/public/application/legacy_app.js b/src/plugins/dashboard/public/application/legacy_app.js index 3ed961e54049..0c10653d7f41 100644 --- a/src/plugins/dashboard/public/application/legacy_app.js +++ b/src/plugins/dashboard/public/application/legacy_app.js @@ -150,7 +150,7 @@ export function initDashboardApp(app, deps) { type: $scope.dashboardListTypes, search: search ? `${search}*` : undefined, fields: ['title', 'type', 'description', 'updated_at'], - perPage: $scope.initialPageSize, + perPage: $scope.listingLimit, page: 1, searchFields: ['title^3', 'type', 'description'], defaultSearchOperator: 'AND', @@ -163,11 +163,19 @@ export function initDashboardApp(app, deps) { }; }; - $scope.editItem = ({ editUrl }) => { - history.push(editUrl); + $scope.editItem = ({ appId, editUrl }) => { + if (appId === 'dashboard') { + history.push(editUrl); + } else { + deps.core.application.navigateToUrl(editUrl); + } }; - $scope.viewItem = ({ viewUrl }) => { - history.push(deps.addBasePath(viewUrl)); + $scope.viewItem = ({ appId, viewUrl }) => { + if (appId === 'dashboard') { + history.push(viewUrl); + } else { + deps.core.application.navigateToUrl(viewUrl); + } }; $scope.delete = (dashboards) => { const ids = dashboards.map((d) => ({ id: d.id, appId: d.appId })); diff --git a/src/plugins/dashboard/public/application/listing/create_button.test.tsx b/src/plugins/dashboard/public/application/listing/create_button.test.tsx index 5d2a200f55df..9521df8590e6 100644 --- a/src/plugins/dashboard/public/application/listing/create_button.test.tsx +++ b/src/plugins/dashboard/public/application/listing/create_button.test.tsx @@ -57,7 +57,7 @@ describe('create button with props', () => { expect(createButtons.length).toBe(0); const createDropdown = findTestSubject(component, 'createMenuDropdown'); createDropdown.simulate('click'); - const contextMenus = findTestSubject(component, 'contextMenuItem'); + const contextMenus = findTestSubject(component, 'contextMenuItem-test'); expect(contextMenus.length).toBe(2); expect(contextMenus.at(0).prop('href')).toBe('test1'); }); diff --git a/src/plugins/dashboard/public/application/listing/create_button.tsx b/src/plugins/dashboard/public/application/listing/create_button.tsx index 04e6df883779..4959603fa271 100644 --- a/src/plugins/dashboard/public/application/listing/create_button.tsx +++ b/src/plugins/dashboard/public/application/listing/create_button.tsx @@ -38,7 +38,7 @@ const CreateButton = (props: CreateButtonProps) => { <EuiContextMenuItem key={provider.savedObjectsType} href={provider.createUrl} - data-test-subj="contextMenuItem" + data-test-subj={`contextMenuItem-${provider.appId}`} > {provider.createLinkText} </EuiContextMenuItem> diff --git a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx index d3198f6e1e31..2746997947c9 100644 --- a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx @@ -28,7 +28,7 @@ * under the License. */ -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import React from 'react'; import { mount } from 'enzyme'; import { nextTick } from 'test_utils/enzyme_helpers'; diff --git a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx index 1ff83cf474c2..9a6b71ae6459 100644 --- a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx +++ b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx @@ -34,7 +34,7 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; import ShardFailureOpenModalButton from './shard_failure_open_modal_button'; import { shardFailureRequest } from './__mocks__/shard_failure_request'; import { shardFailureResponse } from './__mocks__/shard_failure_response'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; describe('ShardFailureOpenModalButton', () => { it('triggers the openModal function when "Show details" button is clicked', () => { diff --git a/src/plugins/data_source/common/data_sources/types.ts b/src/plugins/data_source/common/data_sources/types.ts index 366e5a0f3f55..8763c5306c15 100644 --- a/src/plugins/data_source/common/data_sources/types.ts +++ b/src/plugins/data_source/common/data_sources/types.ts @@ -25,6 +25,7 @@ export interface SigV4Content extends SavedObjectAttributes { accessKey: string; secretKey: string; region: string; + service?: SigV4ServiceName; } export interface UsernamePasswordTypedContent extends SavedObjectAttributes { @@ -37,3 +38,8 @@ export enum AuthType { UsernamePasswordType = 'username_password', SigV4 = 'sigv4', } + +export enum SigV4ServiceName { + OpenSearch = 'es', + OpenSearchServerless = 'aoss', +} diff --git a/src/plugins/data_source/config.ts b/src/plugins/data_source/config.ts index 1fc4e00c3e23..09ce35978921 100644 --- a/src/plugins/data_source/config.ts +++ b/src/plugins/data_source/config.ts @@ -37,6 +37,7 @@ export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: false }), appender: fileAppenderSchema, }), + endpointDeniedIPs: schema.maybe(schema.arrayOf(schema.string())), }); export type DataSourcePluginConfigType = TypeOf<typeof configSchema>; diff --git a/src/plugins/data_source/opensearch_dashboards.json b/src/plugins/data_source/opensearch_dashboards.json index 71183a411c79..871858403cf3 100644 --- a/src/plugins/data_source/opensearch_dashboards.json +++ b/src/plugins/data_source/opensearch_dashboards.json @@ -5,5 +5,6 @@ "server": true, "ui": true, "requiredPlugins": [], - "optionalPlugins": [] + "optionalPlugins": [], + "extraPublicDirs": ["common/data_sources"] } diff --git a/src/plugins/data_source/server/client/configure_client.test.ts b/src/plugins/data_source/server/client/configure_client.test.ts index 1499ccd411c2..aa367f0a6f89 100644 --- a/src/plugins/data_source/server/client/configure_client.test.ts +++ b/src/plugins/data_source/server/client/configure_client.test.ts @@ -167,6 +167,30 @@ describe('configureClient', () => { expect(decodeAndDecryptSpy).toHaveBeenCalledTimes(2); }); + test('configure client with auth.type == sigv4, service == aoss, should successfully call new Client()', async () => { + savedObjectsMock.get.mockReset().mockResolvedValueOnce({ + id: DATA_SOURCE_ID, + type: DATA_SOURCE_SAVED_OBJECT_TYPE, + attributes: { + ...dataSourceAttr, + auth: { + type: AuthType.SigV4, + credentials: { ...sigV4AuthContent, service: 'aoss' }, + }, + }, + references: [], + }); + + jest.spyOn(cryptographyMock, 'decodeAndDecrypt').mockResolvedValue({ + decryptedText: 'accessKey', + encryptionContext: { endpoint: 'http://localhost' }, + }); + + await configureClient(dataSourceClientParams, clientPoolSetup, config, logger); + + expect(ClientMock).toHaveBeenCalledTimes(1); + }); + test('configure test client for non-exist datasource should not call saved object api, nor decode any credential', async () => { const decodeAndDecryptSpy = jest.spyOn(cryptographyMock, 'decodeAndDecrypt').mockResolvedValue({ decryptedText: 'password', diff --git a/src/plugins/data_source/server/client/configure_client.ts b/src/plugins/data_source/server/client/configure_client.ts index 8b43ffa80b23..77a0e067bbc0 100644 --- a/src/plugins/data_source/server/client/configure_client.ts +++ b/src/plugins/data_source/server/client/configure_client.ts @@ -160,7 +160,7 @@ const getBasicAuthClient = ( }; const getAWSClient = (credential: SigV4Content, clientOptions: ClientOptions): Client => { - const { accessKey, secretKey, region } = credential; + const { accessKey, secretKey, region, service } = credential; const credentialProvider = (): Promise<Credentials> => { return new Promise((resolve) => { @@ -172,6 +172,7 @@ const getAWSClient = (credential: SigV4Content, clientOptions: ClientOptions): C ...AwsSigv4Signer({ region, getCredentials: credentialProvider, + service, }), ...clientOptions, }); diff --git a/src/plugins/data_source/server/client/configure_client_utils.ts b/src/plugins/data_source/server/client/configure_client_utils.ts index 3ef8acc97b58..2ca6c6f2a83f 100644 --- a/src/plugins/data_source/server/client/configure_client_utils.ts +++ b/src/plugins/data_source/server/client/configure_client_utils.ts @@ -91,7 +91,7 @@ export const getAWSCredential = async ( cryptography: CryptographyServiceSetup ): Promise<SigV4Content> => { const { endpoint } = dataSource; - const { accessKey, secretKey, region } = dataSource.auth.credentials! as SigV4Content; + const { accessKey, secretKey, region, service } = dataSource.auth.credentials! as SigV4Content; const { decryptedText: accessKeyText, @@ -122,6 +122,7 @@ export const getAWSCredential = async ( region, accessKey: accessKeyText, secretKey: secretKeyText, + service, }; return credential; diff --git a/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts b/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts index c047da70b285..59c110d06dc5 100644 --- a/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts +++ b/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts @@ -6,7 +6,7 @@ import { SavedObjectsClientContract } from '../../../../core/server'; import { loggingSystemMock, savedObjectsClientMock } from '../../../../core/server/mocks'; import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../../common'; -import { AuthType, DataSourceAttributes } from '../../common/data_sources'; +import { AuthType, DataSourceAttributes, SigV4Content } from '../../common/data_sources'; import { DataSourcePluginConfigType } from '../../config'; import { cryptographyServiceSetupMock } from '../cryptography_service.mocks'; import { CryptographyServiceSetup } from '../cryptography_service'; @@ -27,6 +27,7 @@ describe('configureLegacyClient', () => { let clientPoolSetup: OpenSearchClientPoolSetup; let configOptions: ConfigOptions; let dataSourceAttr: DataSourceAttributes; + let sigV4AuthContent: SigV4Content; let mockOpenSearchClientInstance: { close: jest.Mock; @@ -71,6 +72,12 @@ describe('configureLegacyClient', () => { }, } as DataSourceAttributes; + sigV4AuthContent = { + region: 'us-east-1', + accessKey: 'accessKey', + secretKey: 'secretKey', + }; + clientPoolSetup = { getClientFromPool: jest.fn(), addClientToPool: jest.fn(), @@ -157,6 +164,42 @@ describe('configureLegacyClient', () => { expect(mockResult).toBeDefined(); }); + test('configure client with auth.type == sigv4 and service param, should call new Client() with service param', async () => { + savedObjectsMock.get.mockReset().mockResolvedValueOnce({ + id: DATA_SOURCE_ID, + type: DATA_SOURCE_SAVED_OBJECT_TYPE, + attributes: { + ...dataSourceAttr, + auth: { + type: AuthType.SigV4, + credentials: { ...sigV4AuthContent, service: 'aoss' }, + }, + }, + references: [], + }); + + parseClientOptionsMock.mockReturnValue(configOptions); + + jest.spyOn(cryptographyMock, 'decodeAndDecrypt').mockResolvedValue({ + decryptedText: 'accessKey', + encryptionContext: { endpoint: 'http://localhost' }, + }); + + await configureLegacyClient( + dataSourceClientParams, + callApiParams, + clientPoolSetup, + config, + logger + ); + + expect(parseClientOptionsMock).toHaveBeenCalled(); + expect(ClientMock).toHaveBeenCalledTimes(1); + expect(ClientMock).toHaveBeenCalledWith(expect.objectContaining({ service: 'aoss' })); + + expect(savedObjectsMock.get).toHaveBeenCalledTimes(1); + }); + test('configure client with auth.type == username_password and password contaminated', async () => { const decodeAndDecryptSpy = jest .spyOn(cryptographyMock, 'decodeAndDecrypt') diff --git a/src/plugins/data_source/server/legacy/configure_legacy_client.ts b/src/plugins/data_source/server/legacy/configure_legacy_client.ts index 3a9b65634a28..0d074cf77d4a 100644 --- a/src/plugins/data_source/server/legacy/configure_legacy_client.ts +++ b/src/plugins/data_source/server/legacy/configure_legacy_client.ts @@ -5,10 +5,9 @@ import { Client } from '@opensearch-project/opensearch'; import { Client as LegacyClient, ConfigOptions } from 'elasticsearch'; -import { Credentials } from 'aws-sdk'; +import { Credentials, Config } from 'aws-sdk'; import { get } from 'lodash'; import HttpAmazonESConnector from 'http-aws-es'; -import { Config } from 'aws-sdk'; import { Headers, LegacyAPICaller, @@ -27,7 +26,7 @@ import { CryptographyServiceSetup } from '../cryptography_service'; import { DataSourceClientParams, LegacyClientCallAPIParams } from '../types'; import { OpenSearchClientPoolSetup } from '../client'; import { parseClientOptions } from './client_config'; -import { createDataSourceError, DataSourceError } from '../lib/error'; +import { createDataSourceError } from '../lib/error'; import { getRootClient, getAWSCredential, @@ -195,13 +194,14 @@ const getBasicAuthClient = async ( }; const getAWSClient = (credential: SigV4Content, clientOptions: ConfigOptions): LegacyClient => { - const { accessKey, secretKey, region } = credential; + const { accessKey, secretKey, region, service } = credential; const client = new LegacyClient({ connectionClass: HttpAmazonESConnector, awsConfig: new Config({ region, credentials: new Credentials({ accessKeyId: accessKey, secretAccessKey: secretKey }), }), + service, ...clientOptions, }); return client; diff --git a/src/plugins/data_source/server/plugin.ts b/src/plugins/data_source/server/plugin.ts index e038a0f7685e..0f3c47be4b4c 100644 --- a/src/plugins/data_source/server/plugin.ts +++ b/src/plugins/data_source/server/plugin.ts @@ -58,7 +58,8 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc const dataSourceSavedObjectsClientWrapper = new DataSourceSavedObjectsClientWrapper( cryptographyServiceSetup, - this.logger.get('data-source-saved-objects-client-wrapper-factory') + this.logger.get('data-source-saved-objects-client-wrapper-factory'), + config.endpointDeniedIPs ); // Add data source saved objects client wrapper factory diff --git a/src/plugins/data_source/server/routes/data_source_connection_validator.ts b/src/plugins/data_source/server/routes/data_source_connection_validator.ts index ecec07dafcc4..f3233859e854 100644 --- a/src/plugins/data_source/server/routes/data_source_connection_validator.ts +++ b/src/plugins/data_source/server/routes/data_source_connection_validator.ts @@ -5,13 +5,19 @@ import { OpenSearchClient } from 'opensearch-dashboards/server'; import { createDataSourceError } from '../lib/error'; - +import { SigV4ServiceName } from '../../common/data_sources'; export class DataSourceConnectionValidator { - constructor(private readonly callDataCluster: OpenSearchClient) {} + constructor( + private readonly callDataCluster: OpenSearchClient, + private readonly dataSourceAttr: any + ) {} async validate() { try { - return await this.callDataCluster.info<OpenSearchClient>(); + // Amazon OpenSearch Serverless does not support .info() API + if (this.dataSourceAttr.auth?.credentials?.service === SigV4ServiceName.OpenSearchServerless) + return await this.callDataCluster.cat.indices(); + return await this.callDataCluster.info(); } catch (e) { throw createDataSourceError(e); } diff --git a/src/plugins/data_source/server/routes/test_connection.ts b/src/plugins/data_source/server/routes/test_connection.ts index c15bb8945015..33f588b5a5ff 100644 --- a/src/plugins/data_source/server/routes/test_connection.ts +++ b/src/plugins/data_source/server/routes/test_connection.ts @@ -5,6 +5,9 @@ import { schema } from '@osd/config-schema'; import { IRouter, OpenSearchClient } from 'opensearch-dashboards/server'; +import { AuthType, DataSourceAttributes, SigV4ServiceName } from '../../common/data_sources'; +import { DataSourceConnectionValidator } from './data_source_connection_validator'; +import { DataSourceServiceSetup } from '../data_source_service'; import { CryptographyServiceSetup } from '../cryptography_service'; export const registerTestConnectionRoute = ( router: IRouter, @@ -36,6 +39,10 @@ export const registerTestConnectionRoute = ( region: schema.string(), accessKey: schema.string(), secretKey: schema.string(), + service: schema.oneOf([ + schema.literal(SigV4ServiceName.OpenSearch), + schema.literal(SigV4ServiceName.OpenSearchServerless), + ]), }), ]) ), @@ -57,9 +64,13 @@ export const registerTestConnectionRoute = ( testClientDataSourceAttr: dataSourceAttr as DataSourceAttributes, } ); - const dsValidator = new DataSourceConnectionValidator(dataSourceClient); - await dsValidator.validate(); + const dataSourceValidator = new DataSourceConnectionValidator( + dataSourceClient, + dataSourceAttr + ); + + await dataSourceValidator.validate(); return response.ok({ body: { diff --git a/src/plugins/data_source/server/saved_objects/data_source_saved_objects_client_wrapper.ts b/src/plugins/data_source/server/saved_objects/data_source_saved_objects_client_wrapper.ts index 6b79248d1a94..12d60b8da51e 100644 --- a/src/plugins/data_source/server/saved_objects/data_source_saved_objects_client_wrapper.ts +++ b/src/plugins/data_source/server/saved_objects/data_source_saved_objects_client_wrapper.ts @@ -24,14 +24,13 @@ import { UsernamePasswordTypedContent, } from '../../common/data_sources'; import { EncryptionContext, CryptographyServiceSetup } from '../cryptography_service'; +import { isValidURL } from '../util/endpoint_validator'; /** * Describes the Credential Saved Objects Client Wrapper class, * which contains the factory used to create Saved Objects Client Wrapper instances */ export class DataSourceSavedObjectsClientWrapper { - constructor(private cryptography: CryptographyServiceSetup, private logger: Logger) {} - /** * Describes the factory used to create instances of Saved Objects Client Wrappers * for data source specific operations such as credentials encryption @@ -138,14 +137,11 @@ export class DataSourceSavedObjectsClientWrapper { }; }; - private isValidUrl(endpoint: string) { - try { - const url = new URL(endpoint); - return Boolean(url) && (url.protocol === 'http:' || url.protocol === 'https:'); - } catch (e) { - return false; - } - } + constructor( + private cryptography: CryptographyServiceSetup, + private logger: Logger, + private endpointBlockedIps?: string[] + ) {} private async validateAndEncryptAttributes<T = unknown>(attributes: T) { this.validateAttributes(attributes); @@ -254,8 +250,10 @@ export class DataSourceSavedObjectsClientWrapper { ); } - if (!this.isValidUrl(endpoint)) { - throw SavedObjectsErrorHelpers.createBadRequestError('"endpoint" attribute is not valid'); + if (!isValidURL(endpoint, this.endpointBlockedIps)) { + throw SavedObjectsErrorHelpers.createBadRequestError( + '"endpoint" attribute is not valid or allowed' + ); } if (!auth) { @@ -303,7 +301,7 @@ export class DataSourceSavedObjectsClientWrapper { ); } - const { accessKey, secretKey, region } = credentials as SigV4Content; + const { accessKey, secretKey, region, service } = credentials as SigV4Content; if (!accessKey) { throw SavedObjectsErrorHelpers.createBadRequestError( @@ -322,6 +320,12 @@ export class DataSourceSavedObjectsClientWrapper { '"auth.credentials.region" attribute is required' ); } + + if (!service) { + throw SavedObjectsErrorHelpers.createBadRequestError( + '"auth.credentials.service" attribute is required' + ); + } break; default: throw SavedObjectsErrorHelpers.createBadRequestError(`Invalid auth type: '${type}'`); @@ -459,7 +463,7 @@ export class DataSourceSavedObjectsClientWrapper { private async encryptSigV4Credential<T = unknown>(auth: T, encryptionContext: EncryptionContext) { const { - credentials: { accessKey, secretKey, region }, + credentials: { accessKey, secretKey, region, service }, } = auth; return { @@ -468,6 +472,7 @@ export class DataSourceSavedObjectsClientWrapper { region, accessKey: await this.cryptography.encryptAndEncode(accessKey, encryptionContext), secretKey: await this.cryptography.encryptAndEncode(secretKey, encryptionContext), + service, }, }; } diff --git a/src/plugins/data_source/server/util/endpoint_validator.test.js b/src/plugins/data_source/server/util/endpoint_validator.test.js new file mode 100644 index 000000000000..618bf52d4d95 --- /dev/null +++ b/src/plugins/data_source/server/util/endpoint_validator.test.js @@ -0,0 +1,34 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as validator from './endpoint_validator'; + +describe('endpoint_validator', function () { + it('Url1 that should be blocked should return false', function () { + expect(validator.isValidURL('http://127.0.0.1', ['127.0.0.0/8'])).toEqual(false); + }); + + it('Url2 that is invalid should return false', function () { + expect(validator.isValidURL('www.test.com', [])).toEqual(false); + }); + + it('Url3 that is invalid should return false', function () { + expect(validator.isValidURL('ftp://www.test.com', [])).toEqual(false); + }); + + it('Url4 that should be blocked should return false', function () { + expect( + validator.isValidURL('http://169.254.169.254/latest/meta-data/', ['169.254.0.0/16']) + ).toEqual(false); + }); + + it('Url5 that should not be blocked should return true', function () { + expect(validator.isValidURL('https://www.opensearch.org', ['127.0.0.0/8'])).toEqual(true); + }); + + it('Url6 that should not be blocked should return true when null IPs', function () { + expect(validator.isValidURL('https://www.opensearch.org')).toEqual(true); + }); +}); diff --git a/src/plugins/data_source/server/util/endpoint_validator.ts b/src/plugins/data_source/server/util/endpoint_validator.ts new file mode 100644 index 000000000000..1c032037d2f5 --- /dev/null +++ b/src/plugins/data_source/server/util/endpoint_validator.ts @@ -0,0 +1,59 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import dns from 'dns-sync'; +import IPCIDR from 'ip-cidr'; + +export function isValidURL(endpoint: string, deniedIPs?: string[]) { + // Check the format of URL, URL has be in the format as + // scheme://server/path/resource otherwise an TypeError + // would be thrown. + let url; + try { + url = new URL(endpoint); + } catch (err) { + return false; + } + + if (!(Boolean(url) && (url.protocol === 'http:' || url.protocol === 'https:'))) { + return false; + } + + const ip = getIpAddress(url); + if (!ip) { + return false; + } + + // IP CIDR check if a specific IP address fall in the + // range of an IP address block + for (const deniedIP of deniedIPs ?? []) { + const cidr = new IPCIDR(deniedIP); + if (cidr.contains(ip)) { + return false; + } + } + return true; +} + +/** + * Resolve hostname to IP address + * @param {object} urlObject + * @returns {string} configuredIP + * or null if it cannot be resolve + * According to RFC, all IPv6 IP address needs to be in [] + * such as [::1]. + * So if we detect a IPv6 address, we remove brackets. + */ +function getIpAddress(urlObject: URL) { + const hostname = urlObject.hostname; + const configuredIP = dns.resolve(hostname); + if (configuredIP) { + return configuredIP; + } + if (hostname.startsWith('[') && hostname.endsWith(']')) { + return hostname.substr(1).slice(0, -1); + } + return null; +} diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx index efb3453b6f35..ad1d02c87db4 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx @@ -27,6 +27,7 @@ describe('Datasource Management: Create Datasource form', () => { let component: ReactWrapper<any, Readonly<{}>, React.Component<{}, {}, any>>; const mockSubmitHandler = jest.fn(); const mockTestConnectionHandler = jest.fn(); + const mockCancelHandler = jest.fn(); const getFields = (comp: ReactWrapper<any, Readonly<{}>, React.Component<{}, {}, any>>) => { return { @@ -65,6 +66,7 @@ describe('Datasource Management: Create Datasource form', () => { <CreateDataSourceForm handleTestConnection={mockTestConnectionHandler} handleSubmit={mockSubmitHandler} + handleCancel={mockCancelHandler} existingDatasourceNamesList={['dup20']} /> ), diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx index 1c08da5d6371..3107ee84006a 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx @@ -5,7 +5,9 @@ import React from 'react'; import { + EuiBottomBar, EuiButton, + EuiButtonEmpty, EuiFieldPassword, EuiFieldText, EuiFlexGroup, @@ -19,13 +21,14 @@ import { } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; +import { SigV4Content, SigV4ServiceName } from '../../../../../../data_source/common/data_sources'; import { AuthType, credentialSourceOptions, DataSourceAttributes, DataSourceManagementContextValue, UsernamePasswordTypedContent, - SigV4Content, + sigV4ServiceOptions, } from '../../../../types'; import { Header } from '../header'; import { context as contextType } from '../../../../../../opensearch_dashboards_react/public'; @@ -41,6 +44,7 @@ export interface CreateDataSourceProps { existingDatasourceNamesList: string[]; handleSubmit: (formValues: DataSourceAttributes) => void; handleTestConnection: (formValues: DataSourceAttributes) => void; + handleCancel: () => void; } export interface CreateDataSourceState { /* Validation */ @@ -121,7 +125,31 @@ export class CreateDataSourceForm extends React.Component< }; onChangeAuthType = (e: React.ChangeEvent<HTMLSelectElement>) => { - this.setState({ auth: { ...this.state.auth, type: e.target.value as AuthType } }); + const authType = e.target.value as AuthType; + this.setState({ + auth: { + ...this.state.auth, + type: authType, + credentials: { + ...this.state.auth.credentials, + service: + (this.state.auth.credentials.service as SigV4ServiceName) || + SigV4ServiceName.OpenSearch, + }, + }, + }); + }; + + onChangeSigV4ServiceName = (e: React.ChangeEvent<HTMLSelectElement>) => { + this.setState({ + auth: { + ...this.state.auth, + credentials: { + ...this.state.auth.credentials, + service: e.target.value as SigV4ServiceName, + }, + }, + }); }; onChangeUsername = (e: { target: { value: any } }) => { @@ -267,6 +295,7 @@ export class CreateDataSourceForm extends React.Component< region: this.state.auth.credentials.region, accessKey: this.state.auth.credentials.accessKey, secretKey: this.state.auth.credentials.secretKey, + service: this.state.auth.credentials.service || SigV4ServiceName.OpenSearch, } as SigV4Content; } @@ -391,6 +420,19 @@ export class CreateDataSourceForm extends React.Component< data-test-subj="createDataSourceFormRegionField" /> </EuiFormRow> + <EuiFormRow + label={i18n.translate('dataSourcesManagement.createDataSource.serviceName', { + defaultMessage: 'Service Name', + })} + > + <EuiSelect + options={sigV4ServiceOptions} + value={this.state.auth.credentials.service} + onChange={(e) => this.onChangeSigV4ServiceName(e)} + name="ServiceName" + data-test-subj="createDataSourceFormSigV4ServiceTypeSelect" + /> + </EuiFormRow> <EuiFormRow label={i18n.translate('dataSourcesManagement.createDataSource.accessKey', { defaultMessage: 'Access Key', @@ -447,169 +489,207 @@ export class CreateDataSourceForm extends React.Component< renderContent = () => { return ( - <EuiPageContent> - {this.renderHeader()} - <EuiSpacer size="m" /> - <EuiForm data-test-subj="data-source-creation"> - {/* Endpoint section */} - {this.renderSectionHeader( - 'dataSourceManagement.connectToDataSource.connectionDetails', - 'Connection Details' - )} - <EuiSpacer size="s" /> - - {/* Title */} - <EuiFormRow - label={i18n.translate('dataSourcesManagement.createDataSource.title', { - defaultMessage: 'Title', - })} - isInvalid={!!this.state.formErrorsByField.title.length} - error={this.state.formErrorsByField.title} - > - <EuiFieldText - name="dataSourceTitle" - value={this.state.title || ''} - placeholder={i18n.translate( - 'dataSourcesManagement.createDataSource.titlePlaceHolder', - { - defaultMessage: 'Title', - } - )} + <> + <EuiPageContent> + {this.renderHeader()} + <EuiSpacer size="m" /> + <EuiForm data-test-subj="data-source-creation"> + {/* Endpoint section */} + {this.renderSectionHeader( + 'dataSourceManagement.connectToDataSource.connectionDetails', + 'Connection Details' + )} + <EuiSpacer size="s" /> + + {/* Title */} + <EuiFormRow + label={i18n.translate('dataSourcesManagement.createDataSource.title', { + defaultMessage: 'Title', + })} isInvalid={!!this.state.formErrorsByField.title.length} - onChange={this.onChangeTitle} - onBlur={this.validateTitle} - data-test-subj="createDataSourceFormTitleField" - /> - </EuiFormRow> + error={this.state.formErrorsByField.title} + > + <EuiFieldText + name="dataSourceTitle" + value={this.state.title || ''} + placeholder={i18n.translate( + 'dataSourcesManagement.createDataSource.titlePlaceHolder', + { + defaultMessage: 'Title', + } + )} + isInvalid={!!this.state.formErrorsByField.title.length} + onChange={this.onChangeTitle} + onBlur={this.validateTitle} + data-test-subj="createDataSourceFormTitleField" + /> + </EuiFormRow> - {/* Description */} - <EuiFormRow - label={this.renderFieldLabelAsOptional( - 'dataSourceManagement.createDataSource.description', - 'Description' - )} - > - <EuiFieldText - name="dataSourceDescription" - value={this.state.description || ''} - placeholder={i18n.translate( - 'dataSourcesManagement.createDataSource.descriptionPlaceholder', - { - defaultMessage: 'Description of the data source', - } - )} - onChange={this.onChangeDescription} - data-test-subj="createDataSourceFormDescriptionField" - /> - </EuiFormRow> - - {/* Endpoint URL */} - <EuiFormRow - label={i18n.translate('dataSourcesManagement.createDataSource.endpointURL', { - defaultMessage: 'Endpoint URL', - })} - isInvalid={!!this.state.formErrorsByField.endpoint.length} - error={this.state.formErrorsByField.endpoint} - > - <EuiFieldText - name="endpoint" - value={this.state.endpoint || ''} - placeholder={i18n.translate( - 'dataSourcesManagement.createDataSource.endpointPlaceholder', - { - defaultMessage: 'https://connectionurl.com', - } + {/* Description */} + <EuiFormRow + label={this.renderFieldLabelAsOptional( + 'dataSourceManagement.createDataSource.description', + 'Description' )} + > + <EuiFieldText + name="dataSourceDescription" + value={this.state.description || ''} + placeholder={i18n.translate( + 'dataSourcesManagement.createDataSource.descriptionPlaceholder', + { + defaultMessage: 'Description of the data source', + } + )} + onChange={this.onChangeDescription} + data-test-subj="createDataSourceFormDescriptionField" + /> + </EuiFormRow> + + {/* Endpoint URL */} + <EuiFormRow + label={i18n.translate('dataSourcesManagement.createDataSource.endpointURL', { + defaultMessage: 'Endpoint URL', + })} isInvalid={!!this.state.formErrorsByField.endpoint.length} - onChange={this.onChangeEndpoint} - onBlur={this.validateEndpoint} - data-test-subj="createDataSourceFormEndpointField" - /> - </EuiFormRow> + error={this.state.formErrorsByField.endpoint} + > + <EuiFieldText + name="endpoint" + value={this.state.endpoint || ''} + placeholder={i18n.translate( + 'dataSourcesManagement.createDataSource.endpointPlaceholder', + { + defaultMessage: 'https://connectionurl.com', + } + )} + isInvalid={!!this.state.formErrorsByField.endpoint.length} + onChange={this.onChangeEndpoint} + onBlur={this.validateEndpoint} + data-test-subj="createDataSourceFormEndpointField" + /> + </EuiFormRow> - {/* Authentication Section: */} + {/* Authentication Section: */} - <EuiSpacer size="xl" /> + <EuiSpacer size="xl" /> - {this.renderSectionHeader( - 'dataSourceManagement.connectToDataSource.authenticationHeader', - 'Authentication Method' - )} - <EuiSpacer size="m" /> + {this.renderSectionHeader( + 'dataSourceManagement.connectToDataSource.authenticationHeader', + 'Authentication Method' + )} + <EuiSpacer size="m" /> - <EuiFormRow> - <EuiText> - <FormattedMessage - id="dataSourcesManagement.createDataSource.authenticationMethodDescription" - defaultMessage="Provide authentication details require to gain access to the endpoint. If no authentication is required, choose " - /> - <b> + <EuiFormRow> + <EuiText> <FormattedMessage - id="dataSourcesManagement.createDataSource.noAuthentication" - defaultMessage="No authentication" + id="dataSourcesManagement.createDataSource.authenticationMethodDescription" + defaultMessage="Enter the authentication details to access the endpoint. If no authentication is required, select " /> - </b> - </EuiText> - </EuiFormRow> - - {/* Credential source */} - <EuiSpacer size="l" /> - <EuiFormRow> - <EuiSelect - options={credentialSourceOptions} - value={this.state.auth.type} - onChange={(e) => this.onChangeAuthType(e)} - name="Credential" - data-test-subj="createDataSourceFormAuthTypeSelect" - /> - </EuiFormRow> - - {/* Create New credentials */} - {this.state.auth.type === AuthType.UsernamePasswordType - ? this.renderCreateNewCredentialsForm(this.state.auth.type) - : null} - - {this.state.auth.type === AuthType.SigV4 - ? this.renderCreateNewCredentialsForm(this.state.auth.type) - : null} - - <EuiSpacer size="xl" /> - <EuiFormRow> - <EuiFlexGroup> - <EuiFlexItem> - {/* Test Connection button*/} - <EuiButton - type="submit" - fill={false} - disabled={!this.isFormValid()} - onClick={this.onClickTestConnection} - data-test-subj="createDataSourceTestConnectionButton" - > - <FormattedMessage - id="dataSourcesManagement.createDataSource.testConnectionButton" - defaultMessage="Test connection" - /> - </EuiButton> - </EuiFlexItem> - {/* Create Data Source button*/} - <EuiFlexItem> - <EuiButton - type="submit" - fill={this.isFormValid()} - disabled={!this.isFormValid()} - onClick={this.onClickCreateNewDataSource} - data-test-subj="createDataSourceButton" - > + <b> <FormattedMessage - id="dataSourcesManagement.createDataSource.createButton" - defaultMessage="Create data source connection" + id="dataSourcesManagement.createDataSource.noAuthentication" + defaultMessage="No authentication" /> - </EuiButton> - </EuiFlexItem> - </EuiFlexGroup> - </EuiFormRow> - </EuiForm> - </EuiPageContent> + </b> + </EuiText> + </EuiFormRow> + + {/* Credential source */} + <EuiSpacer size="l" /> + <EuiFormRow> + <EuiSelect + options={credentialSourceOptions} + value={this.state.auth.type} + onChange={(e) => this.onChangeAuthType(e)} + name="Credential" + data-test-subj="createDataSourceFormAuthTypeSelect" + /> + </EuiFormRow> + + {/* Create New credentials */} + {this.state.auth.type === AuthType.UsernamePasswordType + ? this.renderCreateNewCredentialsForm(this.state.auth.type) + : null} + + {this.state.auth.type === AuthType.SigV4 + ? this.renderCreateNewCredentialsForm(this.state.auth.type) + : null} + + <EuiSpacer size="xl" /> + <EuiFormRow> + <EuiFlexGroup> + <EuiFlexItem grow={false}> + {/* Test Connection button*/} + <EuiButton + type="submit" + fill={false} + disabled={!this.isFormValid()} + onClick={this.onClickTestConnection} + data-test-subj="createDataSourceTestConnectionButton" + > + <FormattedMessage + id="dataSourcesManagement.createDataSource.testConnectionButton" + defaultMessage="Test connection" + /> + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFormRow> + </EuiForm> + </EuiPageContent> + <EuiSpacer size="xxl" /> + <EuiSpacer size="xl" /> + {this.renderBottomBar()} + </> + ); + }; + + renderBottomBar = () => { + return ( + <EuiBottomBar data-test-subj="datasource-create-bottomBar" affordForDisplacement={true}> + <EuiFlexGroup + justifyContent="spaceBetween" + alignItems="center" + responsive={false} + gutterSize="s" + > + <EuiFlexItem /> + <EuiFlexItem /> + <EuiFlexItem grow={false}> + <EuiButtonEmpty + color="ghost" + size="s" + iconType="cross" + onClick={this.props.handleCancel} + aria-describedby="aria-describedby.countOfUnsavedSettings" + data-test-subj="cancelCreateDataSourceButton" + > + <FormattedMessage + id="dataSourcesManagement.createDataSource.cancelButtonLabel" + defaultMessage="Cancel" + /> + </EuiButtonEmpty> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiButton + className="mgtAdvancedSettingsForm__button" + disabled={!this.isFormValid()} + color="secondary" + fill={this.isFormValid()} + size="s" + iconType="check" + onClick={this.onClickCreateNewDataSource} + data-test-subj="createDataSourceButton" + > + <FormattedMessage + id="dataSourcesManagement.createDataSource.createButtonLabel" + defaultMessage="Create data source" + /> + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + </EuiBottomBar> ); }; diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx index 162af4c891f7..adfbe8808637 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx @@ -46,6 +46,7 @@ describe('Datasource Management: Create Datasource Wizard', () => { }); component.update(); }); + test('should create datasource successfully', async () => { spyOn(utils, 'createSingleDataSource').and.returnValue({}); @@ -58,6 +59,7 @@ describe('Datasource Management: Create Datasource Wizard', () => { expect(utils.createSingleDataSource).toHaveBeenCalled(); expect(history.push).toBeCalledWith(''); }); + test('should fail to create datasource', async () => { spyOn(utils, 'createSingleDataSource').and.throwError('error'); await act(async () => { @@ -93,7 +95,17 @@ describe('Datasource Management: Create Datasource Wizard', () => { component.update(); expect(utils.testConnection).toHaveBeenCalled(); }); + + test('should go back to listing page if clicked on cancel button', async () => { + await act(async () => { + // @ts-ignore + await component.find(formIdentifier).first().prop('handleCancel')(); + }); + + expect(history.push).toBeCalledWith(''); + }); }); + describe('case2: should fail to load resources', () => { beforeEach(async () => { spyOn(utils, 'getDataSources').and.throwError(''); @@ -116,6 +128,7 @@ describe('Datasource Management: Create Datasource Wizard', () => { }); component.update(); }); + test('should not render component and go back to listing page', () => { expect(history.push).toBeCalledWith(''); }); diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx index 83477b7a2426..05489ca6258a 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx @@ -116,6 +116,7 @@ export const CreateDataSourceWizard: React.FunctionComponent<CreateDataSourceWiz <CreateDataSourceForm handleSubmit={handleSubmit} handleTestConnection={handleTestConnection} + handleCancel={() => props.history.push('')} existingDatasourceNamesList={existingDatasourceNamesList} /> {isLoading ? <LoadingMask /> : null} diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx index 8e3128c7e987..e05813ca6f04 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx @@ -23,13 +23,14 @@ import { } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; +import { SigV4Content, SigV4ServiceName } from '../../../../../../data_source/common/data_sources'; import { Header } from '../header'; import { AuthType, credentialSourceOptions, DataSourceAttributes, DataSourceManagementContextValue, - SigV4Content, + sigV4ServiceOptions, ToastMessageItem, UsernamePasswordTypedContent, } from '../../../../types'; @@ -46,9 +47,9 @@ import { UpdateAwsCredentialModal } from '../update_aws_credential_modal'; export interface EditDataSourceProps { existingDataSource: DataSourceAttributes; existingDatasourceNamesList: string[]; - handleSubmit: (formValues: DataSourceAttributes) => void; - handleTestConnection: (formValues: DataSourceAttributes) => void; - onDeleteDataSource?: () => void; + handleSubmit: (formValues: DataSourceAttributes) => Promise<void>; + handleTestConnection: (formValues: DataSourceAttributes) => Promise<void>; + onDeleteDataSource?: () => Promise<void>; displayToastMessage: (info: ToastMessageItem) => void; } export interface EditDataSourceState { @@ -58,7 +59,7 @@ export interface EditDataSourceState { endpoint: string; auth: { type: AuthType; - credentials: UsernamePasswordTypedContent | SigV4Content | undefined; + credentials: UsernamePasswordTypedContent | SigV4Content; }; showUpdatePasswordModal: boolean; showUpdateAwsCredentialModal: boolean; @@ -123,7 +124,10 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi credentials: { username: authTypeCheckResults.isUserNamePassword ? auth.credentials?.username : '', password: authTypeCheckResults.isUserNamePassword ? this.maskedPassword : '', - region: authTypeCheckResults.isSigV4 ? auth.credentials?.region : '', + service: authTypeCheckResults.isSigV4 + ? auth.credentials?.service || SigV4ServiceName.OpenSearch + : '', + region: authTypeCheckResults.isSigV4 ? auth.credentials!.region : '', accessKey: authTypeCheckResults.isSigV4 ? this.maskedPassword : '', secretKey: authTypeCheckResults.isSigV4 ? this.maskedPassword : '', }, @@ -163,9 +167,24 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi }; onChangeAuthType = (e: React.ChangeEvent<HTMLSelectElement>) => { - this.setState({ auth: { ...this.state.auth, type: e.target.value as AuthType } }, () => { - this.onChangeFormValues(); - }); + const authType = e.target.value as AuthType; + this.setState( + { + auth: { + ...this.state.auth, + type: authType, + credentials: { + ...this.state.auth.credentials, + service: + (this.state.auth.credentials?.service as SigV4ServiceName) || + SigV4ServiceName.OpenSearch, + }, + }, + }, + () => { + this.onChangeFormValues(); + } + ); }; onChangeDescription = (e: { target: { value: any } }) => { @@ -183,6 +202,7 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi }, }); }; + validateUsername = () => { const isValid = !!this.state.auth.credentials.username?.trim().length; this.setState({ @@ -221,6 +241,18 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi }); }; + onChangeSigV4ServiceName = (e: React.ChangeEvent<HTMLSelectElement>) => { + this.setState({ + auth: { + ...this.state.auth, + credentials: { + ...this.state.auth.credentials, + service: e.target.value as SigV4ServiceName, + } as SigV4Content, + }, + }); + }; + onChangeRegion = (e: { target: { value: any } }) => { this.setState({ auth: { @@ -253,7 +285,7 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi }; validateAccessKey = () => { - const isValid = !!this.state.auth.credentials.accessKey; + const isValid = !!this.state.auth.credentials?.accessKey; this.setState({ formErrorsByField: { ...this.state.formErrorsByField, @@ -275,7 +307,7 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi }; validateSecretKey = () => { - const isValid = !!this.state.auth.credentials.secretKey; + const isValid = !!this.state.auth.credentials?.secretKey; this.setState({ formErrorsByField: { ...this.state.formErrorsByField, @@ -338,9 +370,9 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi } }; - onClickDeleteDataSource = () => { + onClickDeleteDataSource = async () => { if (this.props.onDeleteDataSource) { - this.props.onDeleteDataSource(); + await this.props.onDeleteDataSource(); } }; @@ -359,6 +391,7 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi break; case AuthType.SigV4: credentials = { + service: this.state.auth.credentials?.service, region: this.state.auth.credentials?.region, accessKey: isNewCredential ? this.state.auth.credentials?.accessKey : '', secretKey: isNewCredential ? this.state.auth.credentials?.secretKey : '', @@ -533,7 +566,8 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi {this.state.showUpdateAwsCredentialModal ? ( <UpdateAwsCredentialModal - region={this.state.auth?.credentials?.region || ''} + region={this.state.auth.credentials!.region} + service={this.state.auth.credentials!.service} handleUpdateAwsCredential={this.updateAwsCredential} closeUpdateAwsCredentialModal={this.closeAwsCredentialModal} /> @@ -785,7 +819,20 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi value={this.state.auth.credentials?.region || ''} onChange={this.onChangeRegion} onBlur={this.validateRegion} - data-test-subj="updateDataSourceFormRegionField" + data-test-subj="editDataSourceFormRegionField" + /> + </EuiFormRow> + <EuiFormRow + label={i18n.translate('dataSourcesManagement.createDataSource.serviceName', { + defaultMessage: 'Service Name', + })} + > + <EuiSelect + options={sigV4ServiceOptions} + value={this.state.auth.credentials?.service} + onChange={(e) => this.onChangeSigV4ServiceName(e)} + name="ServiceName" + data-test-subj="editDataSourceFormSigV4ServiceTypeSelect" /> </EuiFormRow> <EuiFormRow @@ -813,7 +860,7 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi onBlur={this.validateAccessKey} spellCheck={false} disabled={this.props.existingDataSource.auth.type === AuthType.SigV4} - data-test-subj="updateDataSourceFormAccessKeyField" + data-test-subj="editDataSourceFormAccessKeyField" /> </EuiFormRow> <EuiFormRow @@ -841,7 +888,7 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi onBlur={this.validateSecretKey} spellCheck={false} disabled={this.props.existingDataSource.auth.type === AuthType.SigV4} - data-test-subj="updateDataSourceFormSecretKeyField" + data-test-subj="editDataSourceFormSecretKeyField" /> </EuiFormRow> <EuiSpacer /> @@ -930,17 +977,21 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi auth.type === formValues.auth.type && auth.type === AuthType.UsernamePasswordType && formValues.auth.credentials?.username !== auth.credentials?.username; + const isAuthTypeSigV4Unchanged = + auth.type === formValues.auth.type && auth.type === AuthType.SigV4; const isRegionChanged = - auth.type === formValues.auth.type && - auth.type === AuthType.SigV4 && - formValues.auth.credentials?.region !== auth.credentials?.region; + isAuthTypeSigV4Unchanged && formValues.auth.credentials?.region !== auth.credentials?.region; + const isServiceNameChanged = + isAuthTypeSigV4Unchanged && + formValues.auth.credentials?.service !== auth.credentials?.service; if ( formValues.title !== title || formValues.description !== description || formValues.auth.type !== auth.type || isUsernameChanged || - isRegionChanged + isRegionChanged || + isServiceNameChanged ) { this.setState({ showUpdateOptions: true }); } else { diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/update_aws_credential_modal/update_aws_credential_modal.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/update_aws_credential_modal/update_aws_credential_modal.tsx index 5997a0bae388..2fbae293132f 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/update_aws_credential_modal/update_aws_credential_modal.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/update_aws_credential_modal/update_aws_credential_modal.tsx @@ -20,15 +20,19 @@ import { } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; +import { SigV4ServiceName } from '../../../../../../data_source/common/data_sources'; +import { sigV4ServiceOptions } from '../../../../../../data_source_management/public/types'; export interface UpdateAwsCredentialModalProps { region: string; + service: SigV4ServiceName; handleUpdateAwsCredential: (accessKey: string, secretKey: string) => void; closeUpdateAwsCredentialModal: () => void; } export const UpdateAwsCredentialModal = ({ region, + service, handleUpdateAwsCredential, closeUpdateAwsCredentialModal, }: UpdateAwsCredentialModalProps) => { @@ -87,6 +91,16 @@ export const UpdateAwsCredentialModal = ({ <EuiSpacer size="m" /> <EuiForm data-test-subj="data-source-update-aws-credential"> + {/* Service Name */} + <EuiFormRow + label={i18n.translate('dataSourcesManagement.editDataSource.serviceName', { + defaultMessage: 'Service Name', + })} + > + <EuiText size="s" data-test-subj="data-source-update-credential-service-name"> + {sigV4ServiceOptions.find((option) => option.value === service)?.text} + </EuiText> + </EuiFormRow> {/* Region */} <EuiFormRow label={i18n.translate('dataSourcesManagement.editDataSource.region', { diff --git a/src/plugins/data_source_management/public/components/validation/datasource_form_validation.ts b/src/plugins/data_source_management/public/components/validation/datasource_form_validation.ts index 1abde2d54edb..aecf6e517305 100644 --- a/src/plugins/data_source_management/public/components/validation/datasource_form_validation.ts +++ b/src/plugins/data_source_management/public/components/validation/datasource_form_validation.ts @@ -20,6 +20,7 @@ export interface CreateEditDataSourceValidation { region: string[]; accessKey: string[]; secretKey: string[]; + service: string[]; }; } @@ -34,6 +35,7 @@ export const defaultValidation: CreateEditDataSourceValidation = { region: [], accessKey: [], secretKey: [], + service: [], }, }; @@ -110,6 +112,11 @@ export const performDataSourceFormValidation = ( if (!formValues.auth.credentials?.region) { return false; } + + /* Service Name */ + if (!formValues.auth.credentials?.service) { + return false; + } } return true; diff --git a/src/plugins/data_source_management/public/types.ts b/src/plugins/data_source_management/public/types.ts index b6d937e5000e..1bede8fbfca9 100644 --- a/src/plugins/data_source_management/public/types.ts +++ b/src/plugins/data_source_management/public/types.ts @@ -16,6 +16,7 @@ import { import { ManagementAppMountParams } from 'src/plugins/management/public'; import { SavedObjectAttributes } from 'src/core/types'; import { i18n } from '@osd/i18n'; +import { SigV4ServiceName } from '../../data_source/common/data_sources'; import { OpenSearchDashboardsReactContextValue } from '../../opensearch_dashboards_react/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -78,6 +79,21 @@ export const credentialSourceOptions = [ }, ]; +export const sigV4ServiceOptions = [ + { + value: SigV4ServiceName.OpenSearch, + text: i18n.translate('dataSourceManagement.SigV4ServiceOptions.OpenSearch', { + defaultMessage: 'Amazon OpenSearch Service', + }), + }, + { + value: SigV4ServiceName.OpenSearchServerless, + text: i18n.translate('dataSourceManagement.SigV4ServiceOptions.OpenSearchServerless', { + defaultMessage: 'Amazon OpenSearch Serverless', + }), + }, +]; + export interface DataSourceAttributes extends SavedObjectAttributes { title: string; description?: string; @@ -97,4 +113,5 @@ export interface SigV4Content extends SavedObjectAttributes { accessKey: string; secretKey: string; region: string; + service?: SigV4ServiceName; } diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts index 0c0b3b07f5c1..02663da57686 100644 --- a/src/plugins/dev_tools/public/plugin.ts +++ b/src/plugins/dev_tools/public/plugin.ts @@ -78,7 +78,7 @@ export class DevToolsPlugin implements Plugin<DevToolsSetup, void> { defaultMessage: 'Dev Tools', }), updater$: this.appStateUpdater, - euiIconType: '/plugins/home/assets/logos/opensearch_mark_default.svg', + icon: '/plugins/home/public/assets/logos/opensearch_mark_default.svg', order: 9010, category: DEFAULT_APP_CATEGORIES.management, mount: async (params: AppMountParameters) => { diff --git a/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx index db5abf3dbed4..2f7cc40b7d9a 100644 --- a/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx +++ b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx @@ -31,7 +31,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ActionBar, ActionBarProps } from './action_bar'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from '../../query_parameters/constants'; describe('Test Discover Context ActionBar for successor | predecessor records', () => { diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx index 204d0b161c68..2ac06b2b6ebf 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx @@ -31,7 +31,7 @@ import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { ToolBarPagerButtons } from './tool_bar_pager_buttons'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; test('it renders ToolBarPagerButtons', () => { const props = { diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx index 2e486976bbdf..0e8efc18efd5 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx @@ -31,7 +31,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TableHeader } from './table_header'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { SortOrder } from './helpers'; import { IndexPattern, IFieldType } from '../../../../../opensearch_dashboards_services'; diff --git a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx index 30b57eaf0dc5..a1ef06b81cf2 100644 --- a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx +++ b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx @@ -34,7 +34,7 @@ import { ReactWrapper } from 'enzyme'; import { ContextErrorMessage } from './context_error_message'; // @ts-ignore import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; describe('loading spinner', function () { let component: ReactWrapper; diff --git a/src/plugins/discover/public/application/components/doc/doc.test.tsx b/src/plugins/discover/public/application/components/doc/doc.test.tsx index 7385f0d360a1..4a3fb740492a 100644 --- a/src/plugins/discover/public/application/components/doc/doc.test.tsx +++ b/src/plugins/discover/public/application/components/doc/doc.test.tsx @@ -33,7 +33,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { Doc, DocProps } from './doc'; const mockSearchApi = jest.fn(); diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx index 843ad5788253..ccab0be41ed2 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx @@ -31,7 +31,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { DocViewer } from './doc_viewer'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { getDocViewsRegistry } from '../../../opensearch_dashboards_services'; import { DocViewRenderProps } from '../../doc_views/doc_views_types'; diff --git a/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx b/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx index 05435ccae99e..998ababbc47f 100644 --- a/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx +++ b/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx @@ -32,7 +32,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; import { HitsCounter, HitsCounterProps } from './hits_counter'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; describe('hits counter', function () { let props: HitsCounterProps; diff --git a/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx b/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx index 5bc6aa29136b..fbc98e2550e0 100644 --- a/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx +++ b/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx @@ -32,7 +32,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; import { LoadingSpinner } from './loading_spinner'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; describe('loading spinner', function () { let component: ReactWrapper; diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx index 9170adccc7e7..f78505e11f1e 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx @@ -31,7 +31,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { DiscoverFieldSearch, Props } from './discover_field_search'; import { EuiButtonGroupProps, EuiPopover } from '@elastic/eui'; import { ReactWrapper } from 'enzyme'; diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx index dbc8c8962466..fa692ca22b5b 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx @@ -30,7 +30,7 @@ import _ from 'lodash'; import { ReactWrapper } from 'enzyme'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; // @ts-ignore import realHits from 'fixtures/real_hits.js'; // @ts-ignore diff --git a/src/plugins/discover/public/application/components/table/table.test.tsx b/src/plugins/discover/public/application/components/table/table.test.tsx index bb10fc137bcb..220ac57feae2 100644 --- a/src/plugins/discover/public/application/components/table/table.test.tsx +++ b/src/plugins/discover/public/application/components/table/table.test.tsx @@ -30,7 +30,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { DocViewTable } from './table'; import { indexPatterns, IndexPattern } from '../../../../../data/public'; diff --git a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx index 4a88e469a086..9011c38a6acb 100644 --- a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx +++ b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx @@ -33,7 +33,7 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; import { TimechartHeader, TimechartHeaderProps } from './timechart_header'; import { EuiIconTip } from '@elastic/eui'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; describe('timechart header', function () { let props: TimechartHeaderProps; diff --git a/src/plugins/embeddable/docs/input_and_output_state.md b/src/plugins/embeddable/docs/input_and_output_state.md index 2fd8799099b6..a0cd4cc5c0ef 100644 --- a/src/plugins/embeddable/docs/input_and_output_state.md +++ b/src/plugins/embeddable/docs/input_and_output_state.md @@ -274,9 +274,6 @@ There are no real life examples showcasing this, it may not even be really neede the thinking being that it would support any type of rendering of child embeddables - whether in a "snap to grid" style like dashboard, or in a free form layout like canvas. -The only real implementation of a container in production code at the time this is written is Dashboard however, with no plans to migrate -Canvas over to use it (this was the original impetus for an abstraction). The container code is quite complicated with child management, -so it makes creating a new container very easy, as you can see in the developer examples of containers. But, it's possible this layer was - an over abstraction without a real prod use case (I can say that because I wrote it, I'm only insulting myself!) :). +The only real implementation of a container in production code at the time is written in the Dashboard plugin, however, with no plans to migrate over to Canvas (this was the original impetus for an abstraction). The container code is quite complicated with child management, so it makes creating a new container very easy, as you can see in the developer examples of containers. But, it's possible this layer was an over abstraction without a real prod use case (I can say that because I wrote it, I'm only insulting myself!) :). Be sure to read [Common mistakes with embeddable containers and inherited input state](./containers_and_inherited_state.md) next! \ No newline at end of file diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx index badeda5d4aef..486e99b1c281 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx @@ -32,7 +32,7 @@ import React from 'react'; import { HelloWorldEmbeddable } from '../../../../../../examples/embeddable_examples/public'; import { EmbeddableRoot } from './embeddable_root'; import { mount } from 'enzyme'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; test('EmbeddableRoot renders an embeddable', async () => { const embeddable = new HelloWorldEmbeddable({ id: 'hello' }); diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx index 072bcd93d32f..4db6b30c9b57 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx @@ -32,7 +32,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { nextTick } from 'test_utils/enzyme_helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { I18nProvider } from '@osd/i18n/react'; import { CONTEXT_MENU_TRIGGER } from '../triggers'; import { Action, UiActionsStart, ActionType } from '../../../../ui_actions/public'; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx index 232c2ca82d15..ac7d6e7aa082 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx @@ -41,7 +41,7 @@ import { ContainerInput } from '../../../../containers'; import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; import { coreMock } from '../../../../../../../../core/public/mocks'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { embeddablePluginMock } from '../../../../../mocks'; function DummySavedObjectFinder(props: { children: React.ReactNode }) { diff --git a/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx b/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx index e2355799d72d..06ed4a66606c 100644 --- a/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx +++ b/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx @@ -30,7 +30,7 @@ import React from 'react'; import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { getDepsMock, getIndexPatternMock } from '../../test_utils'; import { ControlsTab, ControlsTabUiProps } from './controls_tab'; import { Vis } from '../../../../visualizations/public'; diff --git a/src/plugins/input_control_vis/public/components/editor/list_control_editor.test.tsx b/src/plugins/input_control_vis/public/components/editor/list_control_editor.test.tsx index d8a36ac9b6c9..6837c14fafd2 100644 --- a/src/plugins/input_control_vis/public/components/editor/list_control_editor.test.tsx +++ b/src/plugins/input_control_vis/public/components/editor/list_control_editor.test.tsx @@ -32,7 +32,7 @@ import React from 'react'; import sinon from 'sinon'; import { shallow } from 'enzyme'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { getIndexPatternMock } from '../../test_utils/get_index_pattern_mock'; diff --git a/src/plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx b/src/plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx index a8dcc0761f2c..76b1e15a9df7 100644 --- a/src/plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx +++ b/src/plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx @@ -33,7 +33,7 @@ import { shallow } from 'enzyme'; import { SinonSpy, spy, assert } from 'sinon'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { RangeControlEditor } from './range_control_editor'; import { ControlParams } from '../../editor_utils'; diff --git a/src/plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx b/src/plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx index 1e931c084a55..ea1ef17d79f4 100644 --- a/src/plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx +++ b/src/plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx @@ -32,7 +32,7 @@ import React from 'react'; import sinon from 'sinon'; import { shallow } from 'enzyme'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; +import { findTestSubject } from 'test_utils/helpers'; import { InputControlVis } from './input_control_vis'; import { ListControl } from '../../control/list_control_factory'; diff --git a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/application_usage/schema.ts index 2c221aa6a0b5..2e0a76c56cd0 100644 --- a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/application_usage/schema.ts @@ -69,6 +69,5 @@ export const applicationUsageSchema = { opensearch_dashboards: commonSchema, // It's a forward app so we'll likely never report it management: commonSchema, short_url_redirect: commonSchema, // It's a forward app so we'll likely never report it - timelion: commonSchema, visualize: commonSchema, }; diff --git a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts index 45fbc7aaaa87..5be224b85204 100644 --- a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/management/schema.ts @@ -49,11 +49,8 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = { 'timelion:max_buckets': { type: 'long' }, 'timelion:es.timefield': { type: 'keyword' }, 'timelion:min_interval': { type: 'keyword' }, - 'timelion:default_rows': { type: 'long' }, - 'timelion:default_columns': { type: 'long' }, 'timelion:quandl.key': { type: 'keyword' }, 'timelion:es.default_index': { type: 'keyword' }, - 'timelion:showTutorial': { type: 'boolean' }, 'securitySolution:timeDefaults': { type: 'keyword' }, 'securitySolution:defaultAnomalyScore': { type: 'long' }, 'securitySolution:defaultIndex': { type: 'keyword' }, // it's an array diff --git a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.test.ts b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.test.ts index cdfb918a1618..12b8d978fcbe 100644 --- a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.test.ts +++ b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.test.ts @@ -41,7 +41,6 @@ describe('getSavedObjectsCounts', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 0 }, - timelion_sheet: { total: 0 }, }); }); @@ -51,7 +50,6 @@ describe('getSavedObjectsCounts', () => { types: { buckets: [ { key: 'dashboard', doc_count: 1 }, - { key: 'timelion-sheet', doc_count: 2 }, { key: 'index-pattern', value: 2 }, // Malformed on purpose { key: 'graph_workspace', doc_count: 3 }, // already snake_cased ], @@ -66,7 +64,6 @@ describe('getSavedObjectsCounts', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 3 }, - timelion_sheet: { total: 2 }, }); }); }); diff --git a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.ts b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.ts index e987af3bfc3c..040257cb7e78 100644 --- a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.ts +++ b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/get_saved_object_counts.ts @@ -40,14 +40,7 @@ import { snakeCase } from 'lodash'; import { LegacyAPICaller } from 'opensearch-dashboards/server'; -const TYPES = [ - 'dashboard', - 'visualization', - 'search', - 'index-pattern', - 'graph-workspace', - 'timelion-sheet', -]; +const TYPES = ['dashboard', 'visualization', 'search', 'index-pattern', 'graph-workspace']; export interface OpenSearchDashboardsSavedObjectCounts { dashboard: { total: number }; @@ -55,7 +48,6 @@ export interface OpenSearchDashboardsSavedObjectCounts { search: { total: number }; index_pattern: { total: number }; graph_workspace: { total: number }; - timelion_sheet: { total: number }; } export async function getSavedObjectsCounts( diff --git a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/index.test.ts b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/index.test.ts index 0e20b1cd8111..105487ee5b08 100644 --- a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/index.test.ts +++ b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/opensearch_dashboards/index.test.ts @@ -64,7 +64,6 @@ describe('telemetry_opensearch_dashboards', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 0 }, - timelion_sheet: { total: 0 }, }); }); @@ -76,7 +75,6 @@ describe('telemetry_opensearch_dashboards', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 0 }, - timelion_sheet: { total: 0 }, }; expect(collector.formatForBulkUpload!(resultFromFetch)).toStrictEqual({ diff --git a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/ui_metric/schema.ts b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/ui_metric/schema.ts index e542d08d2056..2c398bc23708 100644 --- a/src/plugins/opensearch_dashboards_usage_collection/server/collectors/ui_metric/schema.ts +++ b/src/plugins/opensearch_dashboards_usage_collection/server/collectors/ui_metric/schema.ts @@ -50,7 +50,6 @@ const uiMetricFromDataPluginSchema: MakeSchemaFrom<UIMetricUsage> = { opensearch_dashboards: commonSchema, // It's a forward app so we'll likely never report it management: commonSchema, short_url_redirect: commonSchema, // It's a forward app so we'll likely never report it - timelion: commonSchema, visualize: commonSchema, }; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 1e25048ed03d..c4ff960b62e6 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -252,34 +252,6 @@ } } }, - "timeline": { - "properties": { - "clicks_total": { - "type": "long" - }, - "clicks_7_days": { - "type": "long" - }, - "clicks_30_days": { - "type": "long" - }, - "clicks_90_days": { - "type": "long" - }, - "minutes_on_screen_total": { - "type": "float" - }, - "minutes_on_screen_7_days": { - "type": "float" - }, - "minutes_on_screen_30_days": { - "type": "float" - }, - "minutes_on_screen_90_days": { - "type": "float" - } - } - }, "visualize": { "properties": { "clicks_total": { @@ -1342,13 +1314,6 @@ "type": "long" } } - }, - "timeline_sheet": { - "properties": { - "total": { - "type": "long" - } - } } } }, @@ -1399,21 +1364,12 @@ "timeline:min_interval": { "type": "keyword" }, - "timeline:default_rows": { - "type": "long" - }, - "timeline:default_columns": { - "type": "long" - }, "timeline:quandl.key": { "type": "keyword" }, "timeline:es.default_index": { "type": "keyword" }, - "timeline:showTutorial": { - "type": "boolean" - }, "securitySolution:timeDefaults": { "type": "keyword" }, @@ -1943,19 +1899,6 @@ } } }, - "timeline": { - "type": "array", - "items": { - "properties": { - "key": { - "type": "keyword" - }, - "value": { - "type": "long" - } - } - } - }, "csm": { "type": "array", "items": { diff --git a/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts index 7347975dbc4a..e15a1921b734 100644 --- a/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts +++ b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts @@ -29,7 +29,7 @@ */ import { accessSync, constants, readFileSync, statSync } from 'fs'; -import { safeLoad } from 'js-yaml'; +import { load } from 'js-yaml'; import { dirname, join } from 'path'; import { Observable } from 'rxjs'; @@ -78,7 +78,7 @@ export async function readTelemetryFile<T extends object>( try { if (isFileReadable(configPath)) { const yaml = readFileSync(configPath); - const data = safeLoad(yaml.toString()); + const data = load(yaml.toString()); // don't bother returning empty objects if (Object.keys(data).length) { diff --git a/src/plugins/telemetry_management_section/public/components/opt_in_example_flyout.tsx b/src/plugins/telemetry_management_section/public/components/opt_in_example_flyout.tsx index 56b0fd1e7ee0..d00e5d351c4d 100644 --- a/src/plugins/telemetry_management_section/public/components/opt_in_example_flyout.tsx +++ b/src/plugins/telemetry_management_section/public/components/opt_in_example_flyout.tsx @@ -62,6 +62,7 @@ interface State { * React component for displaying the example data associated with the Telemetry opt-in banner. */ export class OptInExampleFlyout extends React.PureComponent<Props, State> { + private _isMounted: boolean = false; public readonly state: State = { data: null, isLoading: true, @@ -69,15 +70,21 @@ export class OptInExampleFlyout extends React.PureComponent<Props, State> { }; async componentDidMount() { + this._isMounted = true; try { const { fetchExample } = this.props; const clusters = await fetchExample(); + + if (!this._isMounted) return; + this.setState({ data: Array.isArray(clusters) ? clusters : null, isLoading: false, hasPrivilegeToRead: true, }); } catch (err) { + if (!this._isMounted) return; + this.setState({ isLoading: false, hasPrivilegeToRead: err.status !== 403, @@ -85,6 +92,10 @@ export class OptInExampleFlyout extends React.PureComponent<Props, State> { } } + componentWillUnmount() { + this._isMounted = false; + } + renderBody({ data, isLoading, hasPrivilegeToRead }: State) { if (isLoading) { return ( diff --git a/src/plugins/tile_map/public/_tile_map.scss b/src/plugins/tile_map/public/_tile_map.scss deleted file mode 100644 index 5e4b20f79fed..000000000000 --- a/src/plugins/tile_map/public/_tile_map.scss +++ /dev/null @@ -1,15 +0,0 @@ -// SASSTODO: Does this selector exist today? -.tilemap { - margin-bottom: 6px; - border: $euiBorderThin; - position: relative; -} - -/** -* 1. Visualizations have some padding by default but tilemaps look nice flush against the edge to maximize viewing -* space. -*/ -// SASSTODO: Does this selector exist today? -.tile_map { - padding: 0; /* 1. */ -} diff --git a/src/plugins/tile_map/public/index.scss b/src/plugins/tile_map/public/index.scss deleted file mode 100644 index aa2117cb0c01..000000000000 --- a/src/plugins/tile_map/public/index.scss +++ /dev/null @@ -1,8 +0,0 @@ -// Prefix all styles with "tlm" to avoid conflicts. -// Examples -// tlmChart -// tlmChart__legend -// tlmChart__legend--small -// tlmChart__legend-isLoading - -@import "tile_map"; diff --git a/src/plugins/timeline/README.md b/src/plugins/timeline/README.md deleted file mode 100644 index 22ed15ebf5c6..000000000000 --- a/src/plugins/timeline/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Contains the deprecated timeline application. For the timeline visualization, -which also contains the timeline APIs and backend, look at the vis_type_timeline plugin. diff --git a/src/plugins/timeline/opensearch_dashboards.json b/src/plugins/timeline/opensearch_dashboards.json deleted file mode 100644 index 73cfbd3ee086..000000000000 --- a/src/plugins/timeline/opensearch_dashboards.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "timeline", - "version": "opensearchDashboards", - "ui": true, - "server": true, - "requiredBundles": [ - "opensearchDashboardsLegacy", - "opensearchDashboardsUtils", - "savedObjects", - "visTypeTimeline" - ], - "requiredPlugins": [ - "visualizations", - "data", - "navigation", - "visTypeTimeline", - "opensearchDashboardsLegacy" - ] -} diff --git a/src/plugins/timeline/public/_app.scss b/src/plugins/timeline/public/_app.scss deleted file mode 100644 index 8b9078caba5a..000000000000 --- a/src/plugins/timeline/public/_app.scss +++ /dev/null @@ -1,21 +0,0 @@ -.timApp { - position: relative; - background: $euiColorEmptyShade; - - [ng-click] { - cursor: pointer; - } -} - -.timApp__container { - margin: $euiSizeM; -} - -.timApp__menus { - margin: $euiSizeM; -} - -.timApp__stats { - font-weight: $euiFontWeightRegular; - color: $euiColorMediumShade; -} diff --git a/src/plugins/timeline/public/_base.scss b/src/plugins/timeline/public/_base.scss deleted file mode 100644 index 4e64d237271a..000000000000 --- a/src/plugins/timeline/public/_base.scss +++ /dev/null @@ -1,18 +0,0 @@ -// Angular form states -.ng-invalid { - &.ng-dirty, - &.ng-touched { - border-color: $euiColorDanger; - } -} - -input[type="radio"], -input[type="checkbox"], -.radio, -.checkbox { - &[disabled], - fieldset[disabled] & { - cursor: default; - opacity: 0.8; - } -} diff --git a/src/plugins/timeline/public/app.js b/src/plugins/timeline/public/app.js deleted file mode 100644 index d0e940b9dded..000000000000 --- a/src/plugins/timeline/public/app.js +++ /dev/null @@ -1,678 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; - -import { i18n } from '@osd/i18n'; - -import { createHashHistory } from 'history'; - -import { - createOsdUrlStateStorage, - withNotifyOnErrors, -} from '../../opensearch_dashboards_utils/public'; -import { syncQueryStateWithUrl } from '../../data/public'; - -import { getSavedSheetBreadcrumbs, getCreateBreadcrumbs } from './breadcrumbs'; -import { - addFatalError, - registerListenEventListener, - watchMultiDecorator, -} from '../../opensearch_dashboards_legacy/public'; -import { getTimezone } from '../../vis_type_timeline/public'; -import { initCellsDirective } from './directives/cells/cells'; -import { initFullscreenDirective } from './directives/fullscreen/fullscreen'; -import { initFixedElementDirective } from './directives/fixed_element'; -import { initTimelineLoadSheetDirective } from './directives/timeline_load_sheet'; -import { initTimelineHelpDirective } from './directives/timeline_help/timeline_help'; -import { initTimelineSaveSheetDirective } from './directives/timeline_save_sheet'; -import { initTimelineOptionsSheetDirective } from './directives/timeline_options_sheet'; -import { initSavedObjectSaveAsCheckBoxDirective } from './directives/saved_object_save_as_checkbox'; -import { initSavedObjectFinderDirective } from './directives/saved_object_finder'; -import { initTimelineTabsDirective } from './components/timelinehelp_tabs_directive'; -import { initTimelineTDeprecationDirective } from './components/timeline_deprecation_directive'; -import { initInputFocusDirective } from './directives/input_focus'; -import { Chart } from './directives/chart/chart'; -import { TimelineInterval } from './directives/timeline_interval/timeline_interval'; -import { timelineExpInput } from './directives/timeline_expression_input'; -import { TimelineExpressionSuggestions } from './directives/timeline_expression_suggestions/timeline_expression_suggestions'; -import { initSavedSheetService } from './services/saved_sheets'; -import { initTimelineAppState } from './timeline_app_state'; - -import rootTemplate from './index.html'; - -export function initTimelineApp(app, deps) { - app.run(registerListenEventListener); - - const savedSheetLoader = initSavedSheetService(app, deps); - - app.factory('history', () => createHashHistory()); - app.factory('osdUrlStateStorage', (history) => - createOsdUrlStateStorage({ - history, - useHash: deps.core.uiSettings.get('state:storeInSessionStorage'), - ...withNotifyOnErrors(deps.core.notifications.toasts), - }) - ); - app.config(watchMultiDecorator); - - app - .controller('TimelineVisController', function ($scope) { - $scope.$on('timelineChartRendered', (event) => { - event.stopPropagation(); - $scope.renderComplete(); - }); - }) - .constant('timelinePanels', deps.timelinePanels) - .directive('chart', Chart) - .directive('timelineInterval', TimelineInterval) - .directive('timelineExpressionSuggestions', TimelineExpressionSuggestions) - .directive('timelineExpressionInput', timelineExpInput(deps)); - - initTimelineHelpDirective(app); - initInputFocusDirective(app); - initTimelineTabsDirective(app, deps); - initTimelineTDeprecationDirective(app, deps); - initSavedObjectFinderDirective(app, savedSheetLoader, deps.core.uiSettings); - initSavedObjectSaveAsCheckBoxDirective(app); - initCellsDirective(app); - initFixedElementDirective(app); - initFullscreenDirective(app); - initTimelineSaveSheetDirective(app); - initTimelineLoadSheetDirective(app); - initTimelineOptionsSheetDirective(app); - - const location = 'Timeline'; - - app.directive('timelineApp', function () { - return { - restrict: 'E', - controllerAs: 'timelineApp', - controller: timelineController, - }; - }); - - function timelineController( - $http, - $route, - $routeParams, - $scope, - $timeout, - history, - osdUrlStateStorage - ) { - // Keeping this at app scope allows us to keep the current page when the user - // switches to say, the timepicker. - $scope.page = deps.core.uiSettings.get('timeline:showTutorial', true) ? 1 : 0; - $scope.setPage = (page) => ($scope.page = page); - const timefilter = deps.plugins.data.query.timefilter.timefilter; - - timefilter.enableAutoRefreshSelector(); - timefilter.enableTimeRangeSelector(); - - deps.core.chrome.docTitle.change('Timeline - OpenSearch Dashboards'); - - // starts syncing `_g` portion of url with query services - const { stop: stopSyncingQueryServiceStateWithUrl } = syncQueryStateWithUrl( - deps.plugins.data.query, - osdUrlStateStorage - ); - - const savedSheet = $route.current.locals.savedSheet; - - function getStateDefaults() { - return { - sheet: savedSheet.timelion_sheet, - selected: 0, - columns: savedSheet.timelion_columns, - rows: savedSheet.timelion_rows, - interval: savedSheet.timelion_interval, - }; - } - - const { stateContainer, stopStateSync } = initTimelineAppState({ - stateDefaults: getStateDefaults(), - osdUrlStateStorage, - }); - - $scope.state = _.cloneDeep(stateContainer.getState()); - $scope.expression = _.clone($scope.state.sheet[$scope.state.selected]); - $scope.updatedSheets = []; - - const savedVisualizations = deps.plugins.visualizations.savedVisualizationsLoader; - const timezone = getTimezone(deps.core.uiSettings); - - const defaultExpression = '.opensearch(*)'; - - $scope.topNavMenu = getTopNavMenu(); - - $timeout(function () { - if (deps.core.uiSettings.get('timeline:showTutorial', true)) { - $scope.toggleMenu('showHelp'); - } - }, 0); - - $scope.transient = {}; - - function getTopNavMenu() { - const newSheetAction = { - id: 'new', - label: i18n.translate('timeline.topNavMenu.newSheetButtonLabel', { - defaultMessage: 'New', - }), - description: i18n.translate('timeline.topNavMenu.newSheetButtonAriaLabel', { - defaultMessage: 'New Sheet', - }), - run: function () { - history.push('/'); - $route.reload(); - }, - testId: 'timelineNewButton', - }; - - const addSheetAction = { - id: 'add', - label: i18n.translate('timeline.topNavMenu.addChartButtonLabel', { - defaultMessage: 'Add', - }), - description: i18n.translate('timeline.topNavMenu.addChartButtonAriaLabel', { - defaultMessage: 'Add a chart', - }), - run: function () { - $scope.$evalAsync(() => $scope.newCell()); - }, - testId: 'timelineAddChartButton', - }; - - const saveSheetAction = { - id: 'save', - label: i18n.translate('timeline.topNavMenu.saveSheetButtonLabel', { - defaultMessage: 'Save', - }), - description: i18n.translate('timeline.topNavMenu.saveSheetButtonAriaLabel', { - defaultMessage: 'Save Sheet', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showSave')); - }, - testId: 'timelineSaveButton', - }; - - const deleteSheetAction = { - id: 'delete', - label: i18n.translate('timeline.topNavMenu.deleteSheetButtonLabel', { - defaultMessage: 'Delete', - }), - description: i18n.translate('timeline.topNavMenu.deleteSheetButtonAriaLabel', { - defaultMessage: 'Delete current sheet', - }), - disableButton: function () { - return !savedSheet.id; - }, - run: function () { - const title = savedSheet.title; - function doDelete() { - savedSheet - .delete() - .then(() => { - deps.core.notifications.toasts.addSuccess( - i18n.translate('timeline.topNavMenu.delete.modal.successNotificationText', { - defaultMessage: `Deleted '{title}'`, - values: { title }, - }) - ); - history.push('/'); - }) - .catch((error) => addFatalError(deps.core.fatalErrors, error, location)); - } - - const confirmModalOptions = { - confirmButtonText: i18n.translate( - 'timeline.topNavMenu.delete.modal.confirmButtonLabel', - { - defaultMessage: 'Delete', - } - ), - title: i18n.translate('timeline.topNavMenu.delete.modalTitle', { - defaultMessage: `Delete Timeline sheet '{title}'?`, - values: { title }, - }), - }; - - $scope.$evalAsync(() => { - deps.core.overlays - .openConfirm( - i18n.translate('timeline.topNavMenu.delete.modal.warningText', { - defaultMessage: `You can't recover deleted sheets.`, - }), - confirmModalOptions - ) - .then((isConfirmed) => { - if (isConfirmed) { - doDelete(); - } - }); - }); - }, - testId: 'timelineDeleteButton', - }; - - const openSheetAction = { - id: 'open', - label: i18n.translate('timeline.topNavMenu.openSheetButtonLabel', { - defaultMessage: 'Open', - }), - description: i18n.translate('timeline.topNavMenu.openSheetButtonAriaLabel', { - defaultMessage: 'Open Sheet', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showLoad')); - }, - testId: 'timelineOpenButton', - }; - - const optionsAction = { - id: 'options', - label: i18n.translate('timeline.topNavMenu.optionsButtonLabel', { - defaultMessage: 'Options', - }), - description: i18n.translate('timeline.topNavMenu.optionsButtonAriaLabel', { - defaultMessage: 'Options', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showOptions')); - }, - testId: 'timelineOptionsButton', - }; - - const helpAction = { - id: 'help', - label: i18n.translate('timeline.topNavMenu.helpButtonLabel', { - defaultMessage: 'Help', - }), - description: i18n.translate('timeline.topNavMenu.helpButtonAriaLabel', { - defaultMessage: 'Help', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showHelp')); - }, - testId: 'timelineDocsButton', - }; - - if (deps.core.application.capabilities.timelion.save) { - return [ - newSheetAction, - addSheetAction, - saveSheetAction, - deleteSheetAction, - openSheetAction, - optionsAction, - helpAction, - ]; - } - return [newSheetAction, addSheetAction, openSheetAction, optionsAction, helpAction]; - } - - let refresher; - const setRefreshData = function () { - if (refresher) $timeout.cancel(refresher); - const interval = timefilter.getRefreshInterval(); - if (interval.value > 0 && !interval.pause) { - function startRefresh() { - refresher = $timeout(function () { - if (!$scope.running) $scope.search(); - startRefresh(); - }, interval.value); - } - startRefresh(); - } - }; - - const init = function () { - $scope.running = false; - $scope.search(); - setRefreshData(); - - $scope.model = { - timeRange: timefilter.getTime(), - refreshInterval: timefilter.getRefreshInterval(), - }; - - const unsubscribeStateUpdates = stateContainer.subscribe((state) => { - const clonedState = _.cloneDeep(state); - $scope.updatedSheets.forEach((updatedSheet) => { - clonedState.sheet[updatedSheet.id] = updatedSheet.expression; - }); - $scope.state = clonedState; - $scope.opts.state = clonedState; - $scope.expression = _.clone($scope.state.sheet[$scope.state.selected]); - $scope.search(); - }); - - timefilter.getFetch$().subscribe($scope.search); - - $scope.opts = { - saveExpression: saveExpression, - saveSheet: saveSheet, - savedSheet: savedSheet, - state: _.cloneDeep(stateContainer.getState()), - search: $scope.search, - dontShowHelp: function () { - deps.core.uiSettings.set('timeline:showTutorial', false); - $scope.setPage(0); - $scope.closeMenus(); - }, - }; - - $scope.$watch('opts.state.rows', function (newRow) { - const state = stateContainer.getState(); - if (state.rows !== newRow) { - stateContainer.transitions.set('rows', newRow); - } - }); - - $scope.$watch('opts.state.columns', function (newColumn) { - const state = stateContainer.getState(); - if (state.columns !== newColumn) { - stateContainer.transitions.set('columns', newColumn); - } - }); - - $scope.menus = { - showHelp: false, - showSave: false, - showLoad: false, - showOptions: false, - }; - - $scope.toggleMenu = (menuName) => { - const curState = $scope.menus[menuName]; - $scope.closeMenus(); - $scope.menus[menuName] = !curState; - }; - - $scope.closeMenus = () => { - _.forOwn($scope.menus, function (value, key) { - $scope.menus[key] = false; - }); - }; - - $scope.$on('$destroy', () => { - stopSyncingQueryServiceStateWithUrl(); - unsubscribeStateUpdates(); - stopStateSync(); - }); - }; - - $scope.onTimeUpdate = function ({ dateRange }) { - $scope.model.timeRange = { - ...dateRange, - }; - timefilter.setTime(dateRange); - if (!$scope.running) $scope.search(); - }; - - $scope.onRefreshChange = function ({ isPaused, refreshInterval }) { - $scope.model.refreshInterval = { - pause: isPaused, - value: refreshInterval, - }; - timefilter.setRefreshInterval({ - pause: isPaused, - value: refreshInterval ? refreshInterval : $scope.refreshInterval.value, - }); - - setRefreshData(); - }; - - $scope.$watch( - function () { - return savedSheet.lastSavedTitle; - }, - function (newTitle) { - if (savedSheet.id && newTitle) { - deps.core.chrome.docTitle.change(newTitle); - } - } - ); - - $scope.$watch('expression', function (newExpression) { - const state = stateContainer.getState(); - if (state.sheet[state.selected] !== newExpression) { - const updatedSheet = $scope.updatedSheets.find( - (updatedSheet) => updatedSheet.id === state.selected - ); - if (updatedSheet) { - updatedSheet.expression = newExpression; - } else { - $scope.updatedSheets.push({ - id: state.selected, - expression: newExpression, - }); - } - } - }); - - $scope.toggle = function (property) { - $scope[property] = !$scope[property]; - }; - - $scope.changeInterval = function (interval) { - $scope.currentInterval = interval; - }; - - $scope.updateChart = function () { - const state = stateContainer.getState(); - const newSheet = _.clone(state.sheet); - if ($scope.updatedSheets.length) { - $scope.updatedSheets.forEach((updatedSheet) => { - newSheet[updatedSheet.id] = updatedSheet.expression; - }); - $scope.updatedSheets = []; - } - stateContainer.transitions.updateState({ - interval: $scope.currentInterval ? $scope.currentInterval : state.interval, - sheet: newSheet, - }); - }; - - $scope.newSheet = function () { - history.push('/'); - }; - - $scope.removeSheet = function (removedIndex) { - const state = stateContainer.getState(); - const newSheet = state.sheet.filter((el, index) => index !== removedIndex); - $scope.updatedSheets = $scope.updatedSheets.filter((el) => el.id !== removedIndex); - stateContainer.transitions.updateState({ - sheet: newSheet, - selected: removedIndex ? removedIndex - 1 : removedIndex, - }); - }; - - $scope.newCell = function () { - const state = stateContainer.getState(); - const newSheet = [...state.sheet, defaultExpression]; - stateContainer.transitions.updateState({ sheet: newSheet, selected: newSheet.length - 1 }); - }; - - $scope.setActiveCell = function (cell) { - const state = stateContainer.getState(); - if (state.selected !== cell) { - stateContainer.transitions.updateState({ sheet: $scope.state.sheet, selected: cell }); - } - }; - - $scope.search = function () { - $scope.running = true; - const state = stateContainer.getState(); - - // parse the time range client side to make sure it behaves like other charts - const timeRangeBounds = timefilter.getBounds(); - - const httpResult = $http - .post('../api/timeline/run', { - sheet: state.sheet, - time: _.assignIn( - { - from: timeRangeBounds.min, - to: timeRangeBounds.max, - }, - { - interval: state.interval, - timezone: timezone, - } - ), - }) - .then((resp) => resp.data) - .catch((resp) => { - throw resp.data; - }); - - httpResult - .then(function (resp) { - $scope.stats = resp.stats; - $scope.sheet = resp.sheet; - _.forEach(resp.sheet, function (cell) { - if (cell.exception && cell.plot !== state.selected) { - stateContainer.transitions.set('selected', cell.plot); - } - }); - $scope.running = false; - }) - .catch(function (resp) { - $scope.sheet = []; - $scope.running = false; - - const err = new Error(resp.message); - err.stack = resp.stack; - deps.core.notifications.toasts.addError(err, { - title: i18n.translate('timeline.searchErrorTitle', { - defaultMessage: 'Timeline request error', - }), - }); - }); - }; - - $scope.safeSearch = _.debounce($scope.search, 500); - - function saveSheet() { - const state = stateContainer.getState(); - savedSheet.timeline_sheet = state.sheet; - savedSheet.timeline_interval = state.interval; - savedSheet.timeline_columns = state.columns; - savedSheet.timeline_rows = state.rows; - savedSheet.save().then(function (id) { - if (id) { - deps.core.notifications.toasts.addSuccess({ - title: i18n.translate('timeline.saveSheet.successNotificationText', { - defaultMessage: `Saved sheet '{title}'`, - values: { title: savedSheet.title }, - }), - 'data-test-subj': 'timelineSaveSuccessToast', - }); - - if (savedSheet.id !== $routeParams.id) { - history.push(`/${savedSheet.id}`); - } - } - }); - } - - async function saveExpression(title) { - const vis = await deps.plugins.visualizations.createVis('timelion', { - title, - params: { - expression: $scope.state.sheet[$scope.state.selected], - interval: $scope.state.interval, - }, - }); - const state = deps.plugins.visualizations.convertFromSerializedVis(vis.serialize()); - const visSavedObject = await savedVisualizations.get(); - Object.assign(visSavedObject, state); - const id = await visSavedObject.save(); - if (id) { - deps.core.notifications.toasts.addSuccess( - i18n.translate('timeline.saveExpression.successNotificationText', { - defaultMessage: `Saved expression '{title}'`, - values: { title: state.title }, - }) - ); - } - } - - init(); - } - - app.config(function ($routeProvider) { - $routeProvider - .when('/:id?', { - template: rootTemplate, - reloadOnSearch: false, - k7Breadcrumbs: ($injector, $route) => - $injector.invoke( - $route.current.params.id ? getSavedSheetBreadcrumbs : getCreateBreadcrumbs - ), - badge: () => { - if (deps.core.application.capabilities.timelion.save) { - return undefined; - } - - return { - text: i18n.translate('timeline.badge.readOnly.text', { - defaultMessage: 'Read only', - }), - tooltip: i18n.translate('timeline.badge.readOnly.tooltip', { - defaultMessage: 'Unable to save Timeline sheets', - }), - iconType: 'glasses', - }; - }, - resolve: { - savedSheet: function (savedSheets, $route) { - return savedSheets - .get($route.current.params.id) - .then((savedSheet) => { - if ($route.current.params.id) { - deps.core.chrome.recentlyAccessed.add( - savedSheet.getFullPath(), - savedSheet.title, - savedSheet.id - ); - } - return savedSheet; - }) - .catch(); - }, - }, - }) - .otherwise('/'); - }); -} diff --git a/src/plugins/timeline/public/application.ts b/src/plugins/timeline/public/application.ts deleted file mode 100644 index 5eb5d2fcb31c..000000000000 --- a/src/plugins/timeline/public/application.ts +++ /dev/null @@ -1,164 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 './index.scss'; - -import { EuiIcon } from '@elastic/eui'; -import angular, { IModule } from 'angular'; -// required for `ngSanitize` angular module -import 'angular-sanitize'; -// required for ngRoute -import 'angular-route'; -import 'angular-sortable-view'; -import { i18nDirective, i18nFilter, I18nProvider } from '@osd/i18n/angular'; -import { - IUiSettingsClient, - CoreStart, - PluginInitializerContext, - AppMountParameters, -} from 'opensearch-dashboards/public'; -import { getTimeChart } from './panels/timechart/timechart'; -import { Panel } from './panels/panel'; - -import { - configureAppAngularModule, - createTopNavDirective, - createTopNavHelper, -} from '../../opensearch_dashboards_legacy/public'; -import { TimelinePluginDependencies } from './plugin'; -import { DataPublicPluginStart } from '../../data/public'; -// @ts-ignore -import { initTimelineApp } from './app'; - -export interface RenderDeps { - pluginInitializerContext: PluginInitializerContext; - mountParams: AppMountParameters; - core: CoreStart; - plugins: TimelinePluginDependencies; - timelinePanels: Map<string, Panel>; -} - -export interface TimelineVisualizationDependencies { - uiSettings: IUiSettingsClient; - timelinePanels: Map<string, Panel>; - data: DataPublicPluginStart; - $rootScope: any; - $compile: any; -} - -let angularModuleInstance: IModule | null = null; - -export const renderApp = (deps: RenderDeps) => { - if (!angularModuleInstance) { - angularModuleInstance = createLocalAngularModule(deps); - // global routing stuff - configureAppAngularModule( - angularModuleInstance, - { core: deps.core, env: deps.pluginInitializerContext.env }, - true - ); - initTimelineApp(angularModuleInstance, deps); - } - - const $injector = mountTimelineApp(deps.mountParams.appBasePath, deps.mountParams.element, deps); - - return () => { - $injector.get('$rootScope').$destroy(); - }; -}; - -function registerPanels(dependencies: TimelineVisualizationDependencies) { - const timeChartPanel: Panel = getTimeChart(dependencies); - - dependencies.timelinePanels.set(timeChartPanel.name, timeChartPanel); -} - -const mainTemplate = (basePath: string) => `<div ng-view class="timelineAppContainer"> - <base href="${basePath}" /> -</div>`; - -const moduleName = 'app/timeline'; - -const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'angular-sortable-view']; - -function mountTimelineApp(appBasePath: string, element: HTMLElement, deps: RenderDeps) { - const mountpoint = document.createElement('div'); - mountpoint.setAttribute('class', 'timelineAppContainer'); - // eslint-disable-next-line no-unsanitized/property - mountpoint.innerHTML = mainTemplate(appBasePath); - // bootstrap angular into detached element and attach it later to - // make angular-within-angular possible - const $injector = angular.bootstrap(mountpoint, [moduleName]); - - registerPanels({ - uiSettings: deps.core.uiSettings, - timelinePanels: deps.timelinePanels, - data: deps.plugins.data, - $rootScope: $injector.get('$rootScope'), - $compile: $injector.get('$compile'), - }); - element.appendChild(mountpoint); - return $injector; -} - -function createLocalAngularModule(deps: RenderDeps) { - createLocalI18nModule(); - createLocalIconModule(); - createLocalTopNavModule(deps.plugins.navigation); - - const dashboardAngularModule = angular.module(moduleName, [ - ...thirdPartyAngularDependencies, - 'app/timeline/TopNav', - 'app/timeline/I18n', - 'app/timeline/icon', - ]); - return dashboardAngularModule; -} - -function createLocalIconModule() { - angular - .module('app/timeline/icon', ['react']) - .directive('icon', (reactDirective) => reactDirective(EuiIcon)); -} - -function createLocalTopNavModule(navigation: TimelinePluginDependencies['navigation']) { - angular - .module('app/timeline/TopNav', ['react']) - .directive('osdTopNav', createTopNavDirective) - .directive('osdTopNavHelper', createTopNavHelper(navigation.ui)); -} - -function createLocalI18nModule() { - angular - .module('app/timeline/I18n', []) - .provider('i18n', I18nProvider) - .filter('i18n', i18nFilter) - .directive('i18nId', i18nDirective); -} diff --git a/src/plugins/timeline/public/breadcrumbs.js b/src/plugins/timeline/public/breadcrumbs.js deleted file mode 100644 index dfee1407cef3..000000000000 --- a/src/plugins/timeline/public/breadcrumbs.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -const ROOT_BREADCRUMB = { - text: i18n.translate('timeline.breadcrumbs.root', { - defaultMessage: 'Timeline', - }), - href: '#', -}; - -export function getCreateBreadcrumbs() { - return [ - ROOT_BREADCRUMB, - { - text: i18n.translate('timeline.breadcrumbs.create', { - defaultMessage: 'Create', - }), - }, - ]; -} - -export function getSavedSheetBreadcrumbs($route) { - const { savedSheet } = $route.current.locals; - return [ - ROOT_BREADCRUMB, - { - text: savedSheet.title, - }, - ]; -} diff --git a/src/plugins/timeline/public/components/timeline_deprecation.tsx b/src/plugins/timeline/public/components/timeline_deprecation.tsx deleted file mode 100644 index e97e325b22f8..000000000000 --- a/src/plugins/timeline/public/components/timeline_deprecation.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { FormattedMessage } from '@osd/i18n/react'; -import { EuiSpacer, EuiCallOut, EuiLink } from '@elastic/eui'; -import React from 'react'; -import { DocLinksStart } from '../../../../core/public'; - -export const TimelineDeprecation = ({ links }: DocLinksStart) => { - const timelineDeprecationLink = links.opensearchDashboards.visualize.timelineDeprecation; - return ( - <> - <EuiCallOut - title={ - <FormattedMessage - id="timeline.deprecation.message" - defaultMessage="Deprecated since 7.0, the Timeline app will be removed in 8.0. To continue using your Timeline worksheets, {timelineDeprecationLink}." - values={{ - timelineDeprecationLink: ( - <EuiLink href={timelineDeprecationLink} target="_blank" external> - <FormattedMessage - id="timeline.deprecation.here" - defaultMessage="migrate them to a dashboard." - /> - </EuiLink> - ), - }} - /> - } - color="warning" - iconType="alert" - size="s" - /> - <EuiSpacer size="s" /> - </> - ); -}; diff --git a/src/plugins/timeline/public/components/timeline_deprecation_directive.js b/src/plugins/timeline/public/components/timeline_deprecation_directive.js deleted file mode 100644 index d45800db24b5..000000000000 --- a/src/plugins/timeline/public/components/timeline_deprecation_directive.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { TimelineDeprecation } from './timeline_deprecation'; - -export function initTimelineTDeprecationDirective(app, deps) { - app.directive('timelineDeprecation', function (reactDirective) { - return reactDirective( - () => { - return ( - <deps.core.i18n.Context> - <TimelineDeprecation links={deps.core.docLinks.links} /> - </deps.core.i18n.Context> - ); - }, - [], - { - restrict: 'E', - scope: { - docLinks: '=', - }, - } - ); - }); -} diff --git a/src/plugins/timeline/public/components/timelinehelp_tabs.js b/src/plugins/timeline/public/components/timelinehelp_tabs.js deleted file mode 100644 index 1680b4de7ebc..000000000000 --- a/src/plugins/timeline/public/components/timelinehelp_tabs.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 PropTypes from 'prop-types'; -import React from 'react'; - -import { EuiTabs, EuiTab } from '@elastic/eui'; - -import { FormattedMessage } from '@osd/i18n/react'; - -function handleClick(activateTab, tabName) { - activateTab(tabName); -} - -export function TimelineHelpTabs(props) { - return ( - <EuiTabs size="s"> - <EuiTab - isSelected={props.activeTab === 'funcref'} - onClick={() => handleClick(props.activateTab, 'funcref')} - > - <FormattedMessage - id="timeline.help.mainPage.functionReferenceTitle" - defaultMessage="Function reference" - /> - </EuiTab> - <EuiTab - isSelected={props.activeTab === 'keyboardtips'} - onClick={() => handleClick(props.activateTab, 'keyboardtips')} - > - <FormattedMessage - id="timeline.help.mainPage.keyboardTipsTitle" - defaultMessage="Keyboard tips" - /> - </EuiTab> - </EuiTabs> - ); -} - -TimelineHelpTabs.propTypes = { - activeTab: PropTypes.string, - activateTab: PropTypes.func, -}; diff --git a/src/plugins/timeline/public/components/timelinehelp_tabs_directive.js b/src/plugins/timeline/public/components/timelinehelp_tabs_directive.js deleted file mode 100644 index 772580e71bf6..000000000000 --- a/src/plugins/timeline/public/components/timelinehelp_tabs_directive.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { TimelineHelpTabs } from './timelinehelp_tabs'; - -export function initTimelineTabsDirective(app, deps) { - app.directive('timelineHelpTabs', function (reactDirective) { - return reactDirective( - (props) => { - return ( - <deps.core.i18n.Context> - <TimelineHelpTabs {...props} /> - </deps.core.i18n.Context> - ); - }, - [['activeTab'], ['activateTab', { watchDepth: 'reference' }]], - { - restrict: 'E', - scope: { - activeTab: '=', - activateTab: '=', - }, - } - ); - }); -} diff --git a/src/plugins/timeline/public/directives/_form.scss b/src/plugins/timeline/public/directives/_form.scss deleted file mode 100644 index 2b946ec94791..000000000000 --- a/src/plugins/timeline/public/directives/_form.scss +++ /dev/null @@ -1,36 +0,0 @@ -.form-control { - @include euiFontSizeS; - - display: block; - width: 100%; - height: $euiFormControlCompressedHeight; - padding: $euiSizeXS $euiSizeM; - border: $euiBorderThin; - background-color: $euiFormBackgroundColor; - color: $euiTextColor; - border-radius: $euiBorderRadius; - cursor: pointer; - - &:not([type="range"]) { - appearance: none; - } - - &:focus { - border-color: $euiColorPrimary; - outline: none; - box-shadow: none; - } -} - -select.form-control { - // 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; - background-repeat: no-repeat; - background-position: calc(100% - #{$euiSizeS}); - padding-right: $euiSizeXL; -} - -.fullWidth { - width: 100%; -} diff --git a/src/plugins/timeline/public/directives/_index.scss b/src/plugins/timeline/public/directives/_index.scss deleted file mode 100644 index 94f0b2286101..000000000000 --- a/src/plugins/timeline/public/directives/_index.scss +++ /dev/null @@ -1,7 +0,0 @@ -@import "./timeline_expression_input"; -@import "./cells/index"; -@import "./timeline_expression_suggestions/index"; -@import "./timeline_help/index"; -@import "./timeline_interval/index"; -@import "./saved_object_finder"; -@import "./form"; diff --git a/src/plugins/timeline/public/directives/_saved_object_finder.scss b/src/plugins/timeline/public/directives/_saved_object_finder.scss deleted file mode 100644 index a225f357949e..000000000000 --- a/src/plugins/timeline/public/directives/_saved_object_finder.scss +++ /dev/null @@ -1,95 +0,0 @@ -.list-group-menu { - &.select-mode a { - outline: none; - color: tintOrShade($euiColorPrimary, 10%, 10%); - } - - .list-group-menu-item { - list-style: none; - color: tintOrShade($euiColorPrimary, 10%, 10%); - - &.active { - font-weight: bold; - background-color: $euiColorLightShade; - } - - &:hover { - background-color: tintOrShade($euiColorPrimary, 90%, 90%); - } - - li { - list-style: none; - color: tintOrShade($euiColorPrimary, 10%, 10%); - } - } -} - -saved-object-finder { - .list-sort-button { - border-top-left-radius: 0; - border-top-right-radius: 0; - border: none; - padding: $euiSizeS $euiSize; - font-weight: $euiFontWeightRegular; - background-color: $euiColorLightestShade; - } - - .li-striped { - li { - border: none; - } - - li:nth-child(even) { - background-color: $euiColorLightestShade; - } - - li:nth-child(odd) { - background-color: $euiColorEmptyShade; - } - - .paginate-heading { - font-weight: $euiFontWeightRegular; - color: $euiColorDarkestShade; - } - - .list-group-item { - padding: $euiSizeS $euiSize; - - ul { - padding: 0; - display: flex; - flex-direction: row; - - .finder-type { - margin-right: $euiSizeS; - } - } - - a { - display: block; - color: $euiColorPrimary; - - i { - color: shade($euiColorPrimary, 10%); - margin-right: $euiSizeS; - } - } - - &:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - - &.list-group-no-results p { - margin-bottom: 0; - } - } - } - - /* stylelint-disable-next-line selector-type-no-unknown */ - paginate { - paginate-controls { - margin: $euiSize; - } - } -} diff --git a/src/plugins/timeline/public/directives/_timeline_expression_input.scss b/src/plugins/timeline/public/directives/_timeline_expression_input.scss deleted file mode 100644 index e4294d8454c7..000000000000 --- a/src/plugins/timeline/public/directives/_timeline_expression_input.scss +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 1. Anchor suggestions beneath input. - * 2. Allow for option of positioning suggestions absolutely. - */ - -.timExpressionInput__container { - flex: 1 1 auto; - display: flex; - flex-direction: column; /* 1 */ - position: relative; /* 2 */ -} - -.timExpressionInput { - min-height: 70px; // Matches buttons on the right with new vertical rhythm sizing -} diff --git a/src/plugins/timeline/public/directives/cells/_cells.scss b/src/plugins/timeline/public/directives/cells/_cells.scss deleted file mode 100644 index 61634a710260..000000000000 --- a/src/plugins/timeline/public/directives/cells/_cells.scss +++ /dev/null @@ -1,62 +0,0 @@ -.timCell { - display: inline-block; - cursor: pointer; - position: relative; - box-sizing: border-box; - border: 2px dashed transparent; - padding-left: 0 !important; - padding-right: 0 !important; - margin-bottom: $euiSizeM; - - &.active { - border-color: $euiColorLightShade; - } -} - -.timCell.running { - opacity: 0.5; -} - -.timCell__actions { - position: absolute; - bottom: $euiSizeXS; - left: $euiSizeXS; - - > .timCell__action, - > .timCell__id { - @include euiFontSizeXS; - - font-weight: $euiFontWeightBold; - color: $euiColorMediumShade; - display: inline-block; - text-align: center; - width: $euiSizeL; - height: $euiSizeL; - line-height: $euiSizeL; - border-radius: $euiSizeL / 2; - border: $euiBorderThin; - background-color: $euiColorLightestShade; - z-index: $euiZLevel1; - } - - > .timCell__action { - opacity: 0; - - &:focus { - opacity: 1; - } - - &:hover, - &:focus { - color: $euiTextColor; - border-color: $euiColorMediumShade; - background-color: $euiColorLightShade; - } - } -} - -.timCell:hover { - .timCell__action { - opacity: 1; - } -} diff --git a/src/plugins/timeline/public/directives/cells/_index.scss b/src/plugins/timeline/public/directives/cells/_index.scss deleted file mode 100644 index c537dd49d304..000000000000 --- a/src/plugins/timeline/public/directives/cells/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./cells"; diff --git a/src/plugins/timeline/public/directives/cells/cells.html b/src/plugins/timeline/public/directives/cells/cells.html deleted file mode 100644 index de30655b620f..000000000000 --- a/src/plugins/timeline/public/directives/cells/cells.html +++ /dev/null @@ -1,52 +0,0 @@ -<div sv-root - sv-part="state.sheet" - sv-on-sort="dropCell($item, $partFrom, $partTo, $indexFrom, $indexTo)" - > - - <div sv-element - ng-repeat="cell in state.sheet track by $index" - class="timCell col-md-{{12 / state.columns}} col-sm-12 col-xs-12" - timeline-grid timeline-grid-rows="state.rows" - ng-click="onSelect($index)" - ng-class="{active: $index === state.selected}" - osd-accessible-click - aria-label="Timeline chart {{$index + 1}}" - aria-current="{{$index === state.selected}}" - > - - <div chart="sheet[$index]" class="timChart" search="onSearch" interval="state.interval"></div> - <div class="timCell__actions"> - <div class="timCell__id"><span>{{$index + 1}}</span></div> - - <button - class="timCell__action" - ng-click="removeCell($index)" - tooltip="{{ ::'timeline.cells.actions.removeTooltip' | i18n: { defaultMessage: 'Remove' } }}" - tooltip-append-to-body="1" - aria-label="{{ ::'timeline.cells.actions.removeAriaLabel' | i18n: { defaultMessage: 'Remove chart' } }}" - > - <span class="fa fa-remove"></span> - </button> - <button - class="timCell__action" - tooltip="{{ ::'timeline.cells.actions.reorderTooltip' | i18n: { defaultMessage: 'Drag to reorder' } }}" - tooltip-append-to-body="1" - sv-handle - aria-label="{{ ::'timeline.cells.actions.reorderAriaLabel' | i18n: { defaultMessage: 'Drag to reorder' } }}" - tabindex="-1" - > - <span class="fa fa-arrows"></span> - </button> - <button - class="timCell__action" - ng-click="transient.fullscreen = true" - tooltip="{{ ::'timeline.cells.actions.fullscreenTooltip' | i18n: { defaultMessage: 'Full screen' } }}" - tooltip-append-to-body="1" - aria-label="{{ ::'timeline.cells.actions.fullscreenAriaLabel' | i18n: { defaultMessage: 'Full screen chart' } }}" - > - <span class="fa fa-expand"></span> - </button> - </div> - </div> - -</div> diff --git a/src/plugins/timeline/public/directives/cells/cells.js b/src/plugins/timeline/public/directives/cells/cells.js deleted file mode 100644 index 3e10d92a5349..000000000000 --- a/src/plugins/timeline/public/directives/cells/cells.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { move } from './collection'; -import { initTimelineGridDirective } from '../timeline_grid'; - -import html from './cells.html'; - -export function initCellsDirective(app) { - initTimelineGridDirective(app); - - app.directive('timelineCells', function () { - return { - restrict: 'E', - scope: { - sheet: '=', - state: '=', - transient: '=', - onSearch: '=', - onSelect: '=', - onRemoveSheet: '=', - }, - template: html, - link: function ($scope) { - $scope.removeCell = function (index) { - $scope.onRemoveSheet(index); - }; - - $scope.dropCell = function (item, partFrom, partTo, indexFrom, indexTo) { - move($scope.sheet, indexFrom, indexTo); - $scope.onSelect(indexTo); - }; - }, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/cells/collection.ts b/src/plugins/timeline/public/directives/cells/collection.ts deleted file mode 100644 index aeb4f83a75ca..000000000000 --- a/src/plugins/timeline/public/directives/cells/collection.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; - -/** - * move an obj either up or down in the collection by - * injecting it either before/after the prev/next obj that - * satisfied the qualifier - * - * or, just from one index to another... - * - * @param {array} objs - the list to move the object within - * @param {number|any} obj - the object that should be moved, or the index that the object is currently at - * @param {number|boolean} below - the index to move the object to, or whether it should be moved up or down - * @param {function} qualifier - a lodash-y callback, object = _.where, string = _.pluck - * @return {array} - the objs argument - */ -export function move( - objs: any[], - obj: object | number, - below: number | boolean, - qualifier?: ((object: object, index: number) => any) | Record<string, any> | string -): object[] { - const origI = _.isNumber(obj) ? obj : objs.indexOf(obj); - if (origI === -1) { - return objs; - } - - if (_.isNumber(below)) { - // move to a specific index - objs.splice(below, 0, objs.splice(origI, 1)[0]); - return objs; - } - - below = !!below; - qualifier = qualifier && _.iteratee(qualifier); - - const above = !below; - const finder = below ? _.findIndex : _.findLastIndex; - - // find the index of the next/previous obj that meets the qualifications - const targetI = finder(objs, (otherAgg, otherI) => { - if (below && otherI <= origI) { - return; - } - if (above && otherI >= origI) { - return; - } - return Boolean(_.isFunction(qualifier) && qualifier(otherAgg, otherI)); - }); - - if (targetI === -1) { - return objs; - } - - // place the obj at it's new index - objs.splice(targetI, 0, objs.splice(origI, 1)[0]); - return objs; -} diff --git a/src/plugins/timeline/public/directives/chart/chart.js b/src/plugins/timeline/public/directives/chart/chart.js deleted file mode 100644 index 0f26c92c2e17..000000000000 --- a/src/plugins/timeline/public/directives/chart/chart.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -export function Chart(timelinePanels) { - return { - restrict: 'A', - scope: { - seriesList: '=chart', // The flot object, data, config and all - search: '=', // The function to execute to kick off a search - interval: '=', // Required for formatting x-axis ticks - rerenderTrigger: '=', - }, - link: function ($scope, $elem) { - let panelScope = $scope.$new(true); - - function render() { - panelScope.$destroy(); - - if (!$scope.seriesList) return; - - $scope.seriesList.render = $scope.seriesList.render || { - type: 'timechart', - }; - - const panelSchema = timelinePanels.get($scope.seriesList.render.type); - - if (!panelSchema) { - $elem.text( - i18n.translate('timeline.chart.seriesList.noSchemaWarning', { - defaultMessage: 'No such panel type: {renderType}', - values: { renderType: $scope.seriesList.render.type }, - }) - ); - return; - } - - panelScope = $scope.$new(true); - panelScope.seriesList = $scope.seriesList; - panelScope.interval = $scope.interval; - panelScope.search = $scope.search; - - panelSchema.render(panelScope, $elem); - } - - $scope.$watchGroup(['seriesList', 'rerenderTrigger'], render); - }, - }; -} diff --git a/src/plugins/timeline/public/directives/fixed_element.js b/src/plugins/timeline/public/directives/fixed_element.js deleted file mode 100644 index b201b70e26a7..000000000000 --- a/src/plugins/timeline/public/directives/fixed_element.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 $ from 'jquery'; - -export function initFixedElementDirective(app) { - app.directive('fixedElementRoot', function () { - return { - restrict: 'A', - link: function ($elem) { - let fixedAt; - $(window).bind('scroll', function () { - const fixed = $('[fixed-element]', $elem); - const body = $('[fixed-element-body]', $elem); - const top = fixed.offset().top; - - if ($(window).scrollTop() > top) { - // This is a gross hack, but its better than it was. I guess - fixedAt = $(window).scrollTop(); - fixed.addClass(fixed.attr('fixed-element')); - body.addClass(fixed.attr('fixed-element-body')); - body.css({ top: fixed.height() }); - } - - if ($(window).scrollTop() < fixedAt) { - fixed.removeClass(fixed.attr('fixed-element')); - body.removeClass(fixed.attr('fixed-element-body')); - body.removeAttr('style'); - } - }); - }, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/fullscreen/fullscreen.html b/src/plugins/timeline/public/directives/fullscreen/fullscreen.html deleted file mode 100644 index 9b5332a84a8c..000000000000 --- a/src/plugins/timeline/public/directives/fullscreen/fullscreen.html +++ /dev/null @@ -1,14 +0,0 @@ -<div class="timCell col-md-12 col-sm-12 col-xs-12" timeline-grid timeline-grid-rows="1"> - <div chart="series" class="timChart" search="onSearch" interval="state.interval"></div> - <div class="timCell__actions"> - <button - class="timCell__action" - ng-click="transient.fullscreen = false" - tooltip="{{ ::'timeline.fullscreen.exitTooltip' | i18n: { defaultMessage: 'Exit full screen' } }}" - tooltip-append-to-body="1" - aria-label="{{ ::'timeline.fullscreen.exitAriaLabel' | i18n: { defaultMessage: 'Exit full screen' } }}" - > - <span class="fa fa-compress"></span> - </button> - </div> -</div> diff --git a/src/plugins/timeline/public/directives/fullscreen/fullscreen.js b/src/plugins/timeline/public/directives/fullscreen/fullscreen.js deleted file mode 100644 index eec336572bef..000000000000 --- a/src/plugins/timeline/public/directives/fullscreen/fullscreen.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 html from './fullscreen.html'; - -export function initFullscreenDirective(app) { - app.directive('timelineFullscreen', function () { - return { - restrict: 'E', - scope: { - expression: '=', - series: '=', - state: '=', - transient: '=', - onSearch: '=', - }, - template: html, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/input_focus.js b/src/plugins/timeline/public/directives/input_focus.js deleted file mode 100644 index 518208c02a2b..000000000000 --- a/src/plugins/timeline/public/directives/input_focus.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -export function initInputFocusDirective(app) { - app.directive('inputFocus', function ($parse, $timeout) { - return { - restrict: 'A', - link: function ($scope, $elem, attrs) { - const isDisabled = attrs.disableInputFocus && $parse(attrs.disableInputFocus)($scope); - if (!isDisabled) { - $timeout(function () { - $elem.focus(); - if (attrs.inputFocus === 'select') $elem.select(); - }); - } - }, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/key_map.ts b/src/plugins/timeline/public/directives/key_map.ts deleted file mode 100644 index e85deb4e2972..000000000000 --- a/src/plugins/timeline/public/directives/key_map.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -export const keyMap: { [key: number]: string } = { - 8: 'backspace', - 9: 'tab', - 13: 'enter', - 16: 'shift', - 17: 'ctrl', - 18: 'alt', - 19: 'pause', - 20: 'capsLock', - 27: 'escape', - 32: 'space', - 33: 'pageUp', - 34: 'pageDown', - 35: 'end', - 36: 'home', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 45: 'insert', - 46: 'delete', - 48: '0', - 49: '1', - 50: '2', - 51: '3', - 52: '4', - 53: '5', - 54: '6', - 55: '7', - 56: '8', - 57: '9', - 65: 'a', - 66: 'b', - 67: 'c', - 68: 'd', - 69: 'e', - 70: 'f', - 71: 'g', - 72: 'h', - 73: 'i', - 74: 'j', - 75: 'k', - 76: 'l', - 77: 'm', - 78: 'n', - 79: 'o', - 80: 'p', - 81: 'q', - 82: 'r', - 83: 's', - 84: 't', - 85: 'u', - 86: 'v', - 87: 'w', - 88: 'x', - 89: 'y', - 90: 'z', - 91: 'leftWindowKey', - 92: 'rightWindowKey', - 93: 'selectKey', - 96: '0', - 97: '1', - 98: '2', - 99: '3', - 100: '4', - 101: '5', - 102: '6', - 103: '7', - 104: '8', - 105: '9', - 106: 'multiply', - 107: 'add', - 109: 'subtract', - 110: 'period', - 111: 'divide', - 112: 'f1', - 113: 'f2', - 114: 'f3', - 115: 'f4', - 116: 'f5', - 117: 'f6', - 118: 'f7', - 119: 'f8', - 120: 'f9', - 121: 'f10', - 122: 'f11', - 123: 'f12', - 144: 'numLock', - 145: 'scrollLock', - 186: 'semiColon', - 187: 'equalSign', - 188: 'comma', - 189: 'dash', - 190: 'period', - 191: 'forwardSlash', - 192: 'graveAccent', - 219: 'openBracket', - 220: 'backSlash', - 221: 'closeBracket', - 222: 'singleQuote', - 224: 'meta', -}; diff --git a/src/plugins/timeline/public/directives/saved_object_finder.html b/src/plugins/timeline/public/directives/saved_object_finder.html deleted file mode 100644 index 79e6121f458c..000000000000 --- a/src/plugins/timeline/public/directives/saved_object_finder.html +++ /dev/null @@ -1,118 +0,0 @@ -<form - role="form" - class="kuiVerticalRhythm" -> - <div class="kuiBar"> - <div class="kuiBarSection"> - <div class="kuiSearchInput fullWidth"> - <div class="kuiSearchInput__icon kuiIcon fa-search"></div> - <input - class="kuiSearchInput__input" - input-focus - disable-input-focus="disableAutoFocus" - ng-model="filter" - ng-attr-placeholder="{{ finder.getLabel() }} Filter..." - ng-keydown="finder.filterKeyDown($event)" - name="filter" - type="text" - autocomplete="off" - data-test-subj="savedObjectFinderSearchInput" - > - </div> - </div> - - <div class="kuiBarSection"> - <p class="kuiText kuiSubduedText" - i18n-id="timeline.savedObjectFinder.pageItemsFromHitCountDescription" - i18n-default-message="{pageFirstItem}-{pageLastItem} of {hitCount}" - i18n-values="{pageFirstItem, pageLastItem, hitCount: finder.hitCount}" - ></p> - <div class="kuiButtonGroup"> - <button - class="kuiButton kuiButton--primary" - ng-if="onAddNew" - ng-click="onAddNew()" - data-test-subj="addNewSavedObjectLink" - i18n-id="timeline.savedObjectFinder.addNewItemButtonLabel" - i18n-default-message="Add new {item}" - i18n-values="{item: finder.properties.noun}" - i18n-description="{item} can be a type of object in OpenSearch Dashboards, like 'visualization', 'dashboard', etc" - ></button> - - <button - class="kuiButton kuiButton--secondary" - ng-if="!useLocalManagement" - ng-click="finder.manageObjects(finder.properties.name)" - i18n-id="timeline.savedObjectFinder.manageItemsButtonLabel" - i18n-default-message="Manage {items}" - i18n-values="{items: finder.properties.nouns}" - i18n-description="{items} can be a type of object in OpenSearch Dashboards, like 'visualizations', 'dashboards', etc" - ></button> - </div> - </div> - </div> -</form> - -<paginate - list="finder.hits" - per-page="20" - class="kuiVerticalRhythm" -> - <button - class="paginate-heading list-group-item list-sort-button" - ng-click="finder.sortHits(finder.hits)" - aria-live="assertive" - > - <span class="euiScreenReaderOnly" - i18n-id="timeline.savedObjectFinder.sortByButtonLabelScreenReaderOnly" - i18n-default-message="Sort by" - ></span> - <span - i18n-id="timeline.savedObjectFinder.sortByButtonLabel" - i18n-default-message="Name" - ></span> - <span - class="fa" - ng-class="finder.isAscending ? 'fa-caret-up' : 'fa-caret-down'" - > - <span class="euiScreenReaderOnly" - ng-if="finder.isAscending" - i18n-id="timeline.savedObjectFinder.sortByButtonLabeAscendingScreenReaderOnly" - i18n-default-message="ascending" - ></span> - <span class="euiScreenReaderOnly" - ng-if="!finder.isAscending" - i18n-id="timeline.savedObjectFinder.sortByButtonLabeDescendingScreenReaderOnly" - i18n-default-message="descending" - ></span> - </span> - </button> - <ul class="li-striped list-group list-group-menu" ng-class="{'select-mode': finder.selector.enabled}"> - <li - class="list-group-item list-group-menu-item" - ng-class="{'active': finder.selector.index === $index && finder.selector.enabled}" - ng-repeat="hit in page" - ng-keydown="finder.hitKeyDown($event, page, paginate)" - ng-click="finder.onChoose(hit, $event)"> - - <a ng-href="{{finder.makeUrl(hit)}}" - ng-blur="finder.hitBlur($event)" - ng-click="finder.preventClick($event)"> - <span aria-hidden="true" class="finder-type fa" ng-if="hit.icon" ng-class="hit.icon"></span> - <div class="kuiIcon fa-flask ng-scope" ng-if="hit.type.shouldMarkAsExperimentalInUI()"></div> - <span>{{hit.title}}</span> - <p ng-if="hit.description" ng-bind="hit.description"></p> - </a> - </li> - <li - class="list-group-item list-group-no-results" - ng-if="finder.hits.length === 0" - > - <p i18n-id="timeline.savedObjectFinder.noMatchesFoundDescription" - i18n-default-message="No matching {items} found." - i18n-values="{items: finder.properties.nouns}" - i18n-description="{items} can be a type of object in OpenSearch Dashboards, like 'visualizations', 'dashboards', etc" - ></p> - </li> - </ul> -</paginate> diff --git a/src/plugins/timeline/public/directives/saved_object_finder.js b/src/plugins/timeline/public/directives/saved_object_finder.js deleted file mode 100644 index 284b4e9d5fbd..000000000000 --- a/src/plugins/timeline/public/directives/saved_object_finder.js +++ /dev/null @@ -1,327 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; -import rison from 'rison-node'; -import savedObjectFinderTemplate from './saved_object_finder.html'; -import { keyMap } from './key_map'; -import { - PaginateControlsDirectiveProvider, - PaginateDirectiveProvider, -} from '../../../opensearch_dashboards_legacy/public'; -import { PER_PAGE_SETTING } from '../../../saved_objects/public'; -import { VISUALIZE_ENABLE_LABS_SETTING } from '../../../visualizations/public'; - -export function initSavedObjectFinderDirective(app, savedSheetLoader, uiSettings) { - app - .directive('paginate', PaginateDirectiveProvider) - .directive('paginateControls', PaginateControlsDirectiveProvider) - .directive('savedObjectFinder', function () { - return { - restrict: 'E', - scope: { - type: '@', - // optional make-url attr, sets the userMakeUrl in our scope - userMakeUrl: '=?makeUrl', - // optional on-choose attr, sets the userOnChoose in our scope - userOnChoose: '=?onChoose', - // optional useLocalManagement attr, removes link to management section - useLocalManagement: '=?useLocalManagement', - /** - * @type {function} - an optional function. If supplied an `Add new X` button is shown - * and this function is called when clicked. - */ - onAddNew: '=', - /** - * @{type} boolean - set this to true, if you don't want the search box above the - * table to automatically gain focus once loaded - */ - disableAutoFocus: '=', - }, - template: savedObjectFinderTemplate, - controllerAs: 'finder', - controller: function ($scope, $element, $location, history) { - const self = this; - - // the text input element - const $input = $element.find('input[ng-model=filter]'); - - // The number of items to show in the list - $scope.perPage = uiSettings.get(PER_PAGE_SETTING); - - // the list that will hold the suggestions - const $list = $element.find('ul'); - - // the current filter string, used to check that returned results are still useful - let currentFilter = $scope.filter; - - // the most recently entered search/filter - let prevSearch; - - // the list of hits, used to render display - self.hits = []; - - self.service = savedSheetLoader; - self.properties = self.service.loaderProperties; - - filterResults(); - - /** - * Boolean that keeps track of whether hits are sorted ascending (true) - * or descending (false) by title - * @type {Boolean} - */ - self.isAscending = true; - - /** - * Sorts saved object finder hits either ascending or descending - * @param {Array} hits Array of saved finder object hits - * @return {Array} Array sorted either ascending or descending - */ - self.sortHits = function (hits) { - self.isAscending = !self.isAscending; - self.hits = self.isAscending - ? _.sortBy(hits, ['title']) - : _.sortBy(hits, ['title']).reverse(); - }; - - /** - * Passed the hit objects and will determine if the - * hit should have a url in the UI, returns it if so - * @return {string|null} - the url or nothing - */ - self.makeUrl = function (hit) { - if ($scope.userMakeUrl) { - return $scope.userMakeUrl(hit); - } - - if (!$scope.userOnChoose) { - return hit.url; - } - - return '#'; - }; - - self.preventClick = function ($event) { - $event.preventDefault(); - }; - - /** - * Called when a hit object is clicked, can override the - * url behavior if necessary. - */ - self.onChoose = function (hit, $event) { - if ($scope.userOnChoose) { - $scope.userOnChoose(hit, $event); - } - - const url = self.makeUrl(hit); - if (!url || url === '#' || url.charAt(0) !== '#') return; - - $event.preventDefault(); - - history.push(url.substr(1)); - }; - - $scope.$watch('filter', function (newFilter) { - // ensure that the currentFilter changes from undefined to '' - // which triggers - currentFilter = newFilter || ''; - filterResults(); - }); - - $scope.pageFirstItem = 0; - $scope.pageLastItem = 0; - $scope.onPageChanged = (page) => { - $scope.pageFirstItem = page.firstItem; - $scope.pageLastItem = page.lastItem; - }; - - //manages the state of the keyboard selector - self.selector = { - enabled: false, - index: -1, - }; - - self.getLabel = function () { - return _.words(self.properties.nouns).map(_.capitalize).join(' '); - }; - - //key handler for the filter text box - self.filterKeyDown = function ($event) { - switch (keyMap[$event.keyCode]) { - case 'enter': - if (self.hitCount !== 1) return; - - const hit = self.hits[0]; - if (!hit) return; - - self.onChoose(hit, $event); - $event.preventDefault(); - break; - } - }; - - //key handler for the list items - self.hitKeyDown = function ($event, page, paginate) { - switch (keyMap[$event.keyCode]) { - case 'tab': - if (!self.selector.enabled) break; - - self.selector.index = -1; - self.selector.enabled = false; - - //if the user types shift-tab return to the textbox - //if the user types tab, set the focus to the currently selected hit. - if ($event.shiftKey) { - $input.focus(); - } else { - $list.find('li.active a').focus(); - } - - $event.preventDefault(); - break; - case 'down': - if (!self.selector.enabled) break; - - if (self.selector.index + 1 < page.length) { - self.selector.index += 1; - } - $event.preventDefault(); - break; - case 'up': - if (!self.selector.enabled) break; - - if (self.selector.index > 0) { - self.selector.index -= 1; - } - $event.preventDefault(); - break; - case 'right': - if (!self.selector.enabled) break; - - if (page.number < page.count) { - paginate.goToPage(page.number + 1); - self.selector.index = 0; - selectTopHit(); - } - $event.preventDefault(); - break; - case 'left': - if (!self.selector.enabled) break; - - if (page.number > 1) { - paginate.goToPage(page.number - 1); - self.selector.index = 0; - selectTopHit(); - } - $event.preventDefault(); - break; - case 'escape': - if (!self.selector.enabled) break; - - $input.focus(); - $event.preventDefault(); - break; - case 'enter': - if (!self.selector.enabled) break; - - const hitIndex = (page.number - 1) * paginate.perPage + self.selector.index; - const hit = self.hits[hitIndex]; - if (!hit) break; - - self.onChoose(hit, $event); - $event.preventDefault(); - break; - case 'shift': - break; - default: - $input.focus(); - break; - } - }; - - self.hitBlur = function () { - self.selector.index = -1; - self.selector.enabled = false; - }; - - self.manageObjects = function (type) { - $location.url( - '/management/opensearch-dashboards/objects?_a=' + rison.encode({ tab: type }) - ); - }; - - self.hitCountNoun = function () { - return (self.hitCount === 1 - ? self.properties.noun - : self.properties.nouns - ).toLowerCase(); - }; - - function selectTopHit() { - setTimeout(function () { - //triggering a focus event kicks off a new angular digest cycle. - $list.find('a:first').focus(); - }, 0); - } - - function filterResults() { - if (!self.service) return; - if (!self.properties) return; - - // track the filter that we use for this search, - // but ensure that we don't search for the same - // thing twice. This is called from multiple places - // and needs to be smart about when it actually searches - const filter = currentFilter; - if (prevSearch === filter) return; - - prevSearch = filter; - - const isLabsEnabled = uiSettings.get(VISUALIZE_ENABLE_LABS_SETTING); - self.service.find(filter).then(function (hits) { - hits.hits = hits.hits.filter( - (hit) => isLabsEnabled || _.get(hit, 'type.stage') !== 'experimental' - ); - hits.total = hits.hits.length; - - // ensure that we don't display old results - // as we can't really cancel requests - if (currentFilter === filter) { - self.hitCount = hits.total; - self.hits = _.sortBy(hits.hits, ['title']); - } - }); - } - }, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/saved_object_save_as_checkbox.html b/src/plugins/timeline/public/directives/saved_object_save_as_checkbox.html deleted file mode 100644 index da16aafdf7af..000000000000 --- a/src/plugins/timeline/public/directives/saved_object_save_as_checkbox.html +++ /dev/null @@ -1,28 +0,0 @@ -<div ng-hide="!savedObject.id || savedObject.isSaving"> - <div - ng-hide="!savedObject.isTitleChanged() || savedObject.copyOnSave" - class="kuiLocalDropdownWarning kuiVerticalRhythmSmall" - i18n-id="timeline.savedObjects.howToSaveAsNewDescription" - i18n-default-message="In previous versions of OpenSearch Dashboards, changing the name of a {savedObjectName} would make a copy with the new name. Use the 'Save as a new {savedObjectName}' checkbox to do this now." - i18n-values="{ savedObjectName: savedObject.getDisplayName() }" - i18n-description="'Save as a new {savedObjectName}' refers to timeline.savedObjects.saveAsNewLabel and should be the same text." - ></div> - - <label class="kuiCheckBoxLabel kuiVerticalRhythmSmall"> - <input - class="kuiCheckBox" - type="checkbox" - data-test-subj="saveAsNewCheckbox" - ng-model="savedObject.copyOnSave" - ng-checked="savedObject.copyOnSave" - > - - <span - class="kuiCheckBoxLabel__text" - i18n-id="timeline.savedObjects.saveAsNewLabel" - i18n-default-message="Save as a new {savedObjectName}" - i18n-values="{ savedObjectName: savedObject.getDisplayName() }" - ></span> - </label> -</div> - diff --git a/src/plugins/timeline/public/directives/saved_object_save_as_checkbox.js b/src/plugins/timeline/public/directives/saved_object_save_as_checkbox.js deleted file mode 100644 index 794ecb01f0a6..000000000000 --- a/src/plugins/timeline/public/directives/saved_object_save_as_checkbox.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 saveObjectSaveAsCheckboxTemplate from './saved_object_save_as_checkbox.html'; - -export function initSavedObjectSaveAsCheckBoxDirective(app) { - app.directive('savedObjectSaveAsCheckBox', function () { - return { - restrict: 'E', - template: saveObjectSaveAsCheckboxTemplate, - replace: true, - scope: { - savedObject: '=', - }, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/timeline_expression_input.html b/src/plugins/timeline/public/directives/timeline_expression_input.html deleted file mode 100644 index b565e6643fc6..000000000000 --- a/src/plugins/timeline/public/directives/timeline_expression_input.html +++ /dev/null @@ -1,19 +0,0 @@ -<div class="timExpressionInput__container" role="combobox" aria-expanded="{{suggestions.isVisible}}" - aria-owns="timelineSuggestionList" aria-haspopup="true"> - <!-- The `role=textbox` is required by VoiceOver to properly detect the autocompletion. - For some reasons it doesn't work without it (even though the default role of - the element is textbox anyway). --> - <textarea data-expression-input role="textbox" rows="{{ rows }}" class="timExpressionInput kuiTextArea fullWidth" - placeholder="{{ ::'timeline.expressionInputPlaceholder' | i18n: { defaultMessage: 'Try a query with {opensearchQuery}', values: { opensearchQuery: '.opensearch(*)' } } }}" - ng-model="sheet" ng-focus="onFocusInput()" ng-keydown="onKeyDownInput($event)" ng-keyup="onKeyUpInput($event)" - ng-blur="onBlurInput()" ng-mousedown="onMouseDownInput()" ng-mouseup="onMouseUpInput()" - ng-click="onClickExpression()" - aria-label="{{ ::'timeline.expressionInputAriaLabel' | i18n: { defaultMessage: 'Timeline expression' } }}" - aria-multiline="false" aria-autocomplete="list" aria-controls="timelineSuggestionList" - aria-activedescendant="{{ getActiveSuggestionId() }}" data-test-subj="timelineExpressionTextArea"></textarea> - - <timeline-expression-suggestions ng-show="suggestions.isVisible" suggestions="suggestions.list" - suggestions-type="suggestions.type" selected-index="suggestions.index" - on-click-suggestion="onClickSuggestion(suggestionIndex)" should-popover="shouldPopoverSuggestions"> - </timeline-expression-suggestions> -</div> diff --git a/src/plugins/timeline/public/directives/timeline_expression_input.js b/src/plugins/timeline/public/directives/timeline_expression_input.js deleted file mode 100644 index 0c4150c952a6..000000000000 --- a/src/plugins/timeline/public/directives/timeline_expression_input.js +++ /dev/null @@ -1,293 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -/** - * Timeline Expression Autocompleter - * - * This directive allows users to enter multiline timeline expressions. If the user has entered - * a valid expression and then types a ".", this directive will display a list of suggestions. - * - * Users can navigate suggestions using the arrow keys. When a user selects a suggestion, it's - * inserted into the expression and the caret position is updated to be inside of the newly- - * added function's parentheses. - * - * Beneath the hood, we use a PEG grammar to validate the Timeline expression and detect if - * the caret is in a position within the expression that allows functions to be suggested. - * - * NOTE: This directive doesn't work well with contenteditable divs. Challenges include: - * - You have to replace markup with newline characters and spaces when passing the expression - * to the grammar. - * - You have to do the opposite when loading a saved expression, so that it appears correctly - * within the contenteditable (i.e. replace newlines with <br> markup). - * - The Range and Selection APIs ignore newlines when providing caret position, so there is - * literally no way to insert suggestions into the correct place in a multiline expression - * that has more than a single consecutive newline. - */ - -import _ from 'lodash'; -import $ from 'jquery'; -import PEG from 'pegjs'; -import grammar from 'raw-loader!../../../vis_type_timeline/common/chain.peg'; -import timelineExpressionInputTemplate from './timeline_expression_input.html'; -import { - SUGGESTION_TYPE, - Suggestions, - suggest, - insertAtLocation, -} from './timeline_expression_input_helpers'; -import { comboBoxKeyCodes } from '@elastic/eui'; - -const Parser = PEG.generate(grammar); - -export function timelineExpInput(deps) { - return ($http, $timeout) => { - return { - restrict: 'E', - scope: { - rows: '=', - sheet: '=', - updateChart: '&', - shouldPopoverSuggestions: '@', - }, - replace: true, - template: timelineExpressionInputTemplate, - link: function (scope, elem) { - const argValueSuggestions = deps.plugins.visTypeTimeline.getArgValueSuggestions(); - const expressionInput = elem.find('[data-expression-input]'); - const functionReference = {}; - let suggestibleFunctionLocation = {}; - - scope.suggestions = new Suggestions(); - - function init() { - $http.get('../api/timeline/functions').then(function (resp) { - Object.assign(functionReference, { - byName: _.keyBy(resp.data, 'name'), - list: resp.data, - }); - }); - } - - function setCaretOffset(caretOffset) { - // Wait for Angular to update the input with the new expression and *then* we can set - // the caret position. - $timeout(() => { - expressionInput.focus(); - expressionInput[0].selectionStart = expressionInput[0].selectionEnd = caretOffset; - scope.$apply(); - }, 0); - } - - function insertSuggestionIntoExpression(suggestionIndex) { - if (scope.suggestions.isEmpty()) { - return; - } - - const { min, max } = suggestibleFunctionLocation; - let insertedValue; - let insertPositionMinOffset = 0; - - switch (scope.suggestions.type) { - case SUGGESTION_TYPE.FUNCTIONS: { - // Position the caret inside of the function parentheses. - insertedValue = `${scope.suggestions.list[suggestionIndex].name}()`; - - // min advanced one to not replace function '.' - insertPositionMinOffset = 1; - break; - } - case SUGGESTION_TYPE.ARGUMENTS: { - // Position the caret after the '=' - insertedValue = `${scope.suggestions.list[suggestionIndex].name}=`; - break; - } - case SUGGESTION_TYPE.ARGUMENT_VALUE: { - // Position the caret after the argument value - insertedValue = `${scope.suggestions.list[suggestionIndex].name}`; - break; - } - } - - const updatedExpression = insertAtLocation( - insertedValue, - scope.sheet, - min + insertPositionMinOffset, - max - ); - scope.sheet = updatedExpression; - - const newCaretOffset = min + insertedValue.length; - setCaretOffset(newCaretOffset); - } - - function scrollToSuggestionAt(index) { - // We don't cache these because the list changes based on user input. - const suggestionsList = $('[data-suggestions-list]'); - const suggestionListItem = $('[data-suggestion-list-item]')[index]; - // Scroll to the position of the item relative to the list, not to the window. - suggestionsList.scrollTop(suggestionListItem.offsetTop - suggestionsList[0].offsetTop); - } - - function getCursorPosition() { - if (expressionInput.length) { - return expressionInput[0].selectionStart; - } - return null; - } - - async function getSuggestions() { - const suggestions = await suggest( - scope.sheet, - functionReference.list, - Parser, - getCursorPosition(), - argValueSuggestions - ); - - // We're using ES6 Promises, not $q, so we have to wrap this in $apply. - scope.$apply(() => { - if (suggestions) { - scope.suggestions.setList(suggestions.list, suggestions.type); - scope.suggestions.show(); - suggestibleFunctionLocation = suggestions.location; - $timeout(() => { - const suggestionsList = $('[data-suggestions-list]'); - suggestionsList.scrollTop(0); - }, 0); - return; - } - - suggestibleFunctionLocation = undefined; - scope.suggestions.reset(); - }); - } - - function isNavigationalKey(keyCode) { - const keyCodes = _.values(comboBoxKeyCodes); - return keyCodes.includes(keyCode); - } - - scope.onFocusInput = () => { - // Wait for the caret position of the input to update and then we can get suggestions - // (which depends on the caret position). - $timeout(getSuggestions, 0); - }; - - scope.onBlurInput = () => { - scope.suggestions.hide(); - }; - - scope.onKeyDownInput = (e) => { - // If we've pressed any non-navigational keys, then the user has typed something and we - // can exit early without doing any navigation. The keyup handler will pull up suggestions. - if (!isNavigationalKey(e.keyCode)) { - return; - } - - switch (e.keyCode) { - case comboBoxKeyCodes.UP: - if (scope.suggestions.isVisible) { - // Up and down keys navigate through suggestions. - e.preventDefault(); - scope.suggestions.stepForward(); - scrollToSuggestionAt(scope.suggestions.index); - } - break; - - case comboBoxKeyCodes.DOWN: - if (scope.suggestions.isVisible) { - // Up and down keys navigate through suggestions. - e.preventDefault(); - scope.suggestions.stepBackward(); - scrollToSuggestionAt(scope.suggestions.index); - } - break; - - case comboBoxKeyCodes.TAB: - // If there are no suggestions or none is selected, the user tabs to the next input. - if (scope.suggestions.isEmpty() || scope.suggestions.index < 0) { - // Before letting the tab be handled to focus the next element - // we need to hide the suggestions, otherwise it will focus these - // instead of the time interval select. - scope.suggestions.hide(); - return; - } - - // If we have suggestions, complete the selected one. - e.preventDefault(); - insertSuggestionIntoExpression(scope.suggestions.index); - break; - - case comboBoxKeyCodes.ENTER: - if (e.metaKey || e.ctrlKey) { - // Re-render the chart when the user hits CMD+ENTER. - e.preventDefault(); - scope.updateChart(); - } else if (!scope.suggestions.isEmpty()) { - // If the suggestions are open, complete the expression with the suggestion. - e.preventDefault(); - insertSuggestionIntoExpression(scope.suggestions.index); - } - break; - - case comboBoxKeyCodes.ESCAPE: - e.preventDefault(); - scope.suggestions.hide(); - break; - } - }; - - scope.onKeyUpInput = (e) => { - // If the user isn't navigating, then we should update the suggestions based on their input. - if (!isNavigationalKey(e.keyCode)) { - getSuggestions(); - } - }; - - scope.onClickExpression = () => { - getSuggestions(); - }; - - scope.onClickSuggestion = (index) => { - insertSuggestionIntoExpression(index); - }; - - scope.getActiveSuggestionId = () => { - if (scope.suggestions.isVisible && scope.suggestions.index > -1) { - return `timelineSuggestion${scope.suggestions.index}`; - } - return ''; - }; - - init(); - }, - }; - }; -} diff --git a/src/plugins/timeline/public/directives/timeline_expression_input_helpers.js b/src/plugins/timeline/public/directives/timeline_expression_input_helpers.js deleted file mode 100644 index 62f496d0f6f0..000000000000 --- a/src/plugins/timeline/public/directives/timeline_expression_input_helpers.js +++ /dev/null @@ -1,291 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; - -export const SUGGESTION_TYPE = { - ARGUMENTS: 'arguments', - ARGUMENT_VALUE: 'argument_value', - FUNCTIONS: 'functions', -}; - -export class Suggestions { - constructor() { - this.reset(); - } - - reset() { - this.index = -1; - this.list = []; - this.type = null; - this.isVisible = false; - } - - setList(list, type) { - this.list = list.sort((a, b) => { - if (a.name < b.name) { - return -1; - } - if (a.name > b.name) { - return 1; - } - // names must be equal - return 0; - }); - this.type = type; - - // Only try to position index inside of list range, when it was already focused - // beforehand (i.e. not -1) - if (this.index > -1) { - // We may get a shorter list than the one we have now, so we need to make sure our index doesn't - // fall outside of the new list's range. - this.index = Math.max(0, Math.min(this.index, this.list.length - 1)); - } - } - - getCount() { - return this.list.length; - } - - isEmpty() { - return this.list.length === 0; - } - - show() { - this.isVisible = true; - } - - hide() { - this.isVisible = false; - } - - stepForward() { - if (this.index > 0) { - this.index -= 1; - } - } - - stepBackward() { - if (this.index < this.list.length - 1) { - this.index += 1; - } - } -} - -function inLocation(cursorPosition, location) { - return cursorPosition >= location.min && cursorPosition <= location.max; -} - -function getArgumentsHelp(functionHelp, functionArgs = []) { - if (!functionHelp) { - return []; - } - - // Do not provide 'inputSeries' as argument suggestion for chainable functions - const argsHelp = functionHelp.chainable ? functionHelp.args.slice(1) : functionHelp.args.slice(0); - - // ignore arguments that are already provided in function declaration - const functionArgNames = functionArgs.map((arg) => { - return arg.name; - }); - return argsHelp.filter((arg) => { - return !functionArgNames.includes(arg.name); - }); -} - -async function extractSuggestionsFromParsedResult( - result, - cursorPosition, - functionList, - argValueSuggestions -) { - const activeFunc = result.functions.find((func) => { - return cursorPosition >= func.location.min && cursorPosition < func.location.max; - }); - - if (!activeFunc) { - return; - } - - const functionHelp = functionList.find((func) => { - return func.name === activeFunc.function; - }); - - // return function suggestion when cursor is outside of parentheses - // location range includes '.', function name, and '('. - const openParen = activeFunc.location.min + activeFunc.function.length + 2; - if (cursorPosition < openParen) { - return { list: [functionHelp], location: activeFunc.location, type: SUGGESTION_TYPE.FUNCTIONS }; - } - - // return argument value suggestions when cursor is inside argument value - const activeArg = activeFunc.arguments.find((argument) => { - return inLocation(cursorPosition, argument.location); - }); - if ( - activeArg && - activeArg.type === 'namedArg' && - inLocation(cursorPosition, activeArg.value.location) - ) { - const { function: functionName, arguments: functionArgs } = activeFunc; - - const { - name: argName, - value: { text: partialInput }, - } = activeArg; - - let valueSuggestions; - if (argValueSuggestions.hasDynamicSuggestionsForArgument(functionName, argName)) { - valueSuggestions = await argValueSuggestions.getDynamicSuggestionsForArgument( - functionName, - argName, - functionArgs, - partialInput - ); - } else { - const { suggestions: staticSuggestions } = functionHelp.args.find((arg) => { - return arg.name === activeArg.name; - }); - valueSuggestions = argValueSuggestions.getStaticSuggestionsForInput( - partialInput, - staticSuggestions - ); - } - return { - list: valueSuggestions, - location: activeArg.value.location, - type: SUGGESTION_TYPE.ARGUMENT_VALUE, - }; - } - - // return argument suggestions - const argsHelp = getArgumentsHelp(functionHelp, activeFunc.arguments); - const argumentSuggestions = argsHelp.filter((arg) => { - if (_.get(activeArg, 'type') === 'namedArg') { - return _.startsWith(arg.name, activeArg.name); - } else if (activeArg) { - return _.startsWith(arg.name, activeArg.text); - } - return true; - }); - const location = activeArg ? activeArg.location : { min: cursorPosition, max: cursorPosition }; - return { list: argumentSuggestions, location: location, type: SUGGESTION_TYPE.ARGUMENTS }; -} - -export async function suggest( - expression, - functionList, - Parser, - cursorPosition, - argValueSuggestions -) { - try { - const result = await Parser.parse(expression); - return await extractSuggestionsFromParsedResult( - result, - cursorPosition, - functionList, - argValueSuggestions - ); - } catch (e) { - let message; - try { - // The grammar will throw an error containing a message if the expression is formatted - // correctly and is prepared to accept suggestions. If the expression is not formatted - // correctly the grammar will just throw a regular PEG SyntaxError, and this JSON.parse - // attempt will throw an error. - message = JSON.parse(e.message); - } catch (e) { - // The expression isn't correctly formatted, so JSON.parse threw an error. - return; - } - - switch (message.type) { - case 'incompleteFunction': { - let list; - if (message.function) { - // The user has start typing a function name, so we'll filter the list down to only - // possible matches. - list = functionList.filter((func) => _.startsWith(func.name, message.function)); - } else { - // The user hasn't typed anything yet, so we'll just return the entire list. - list = functionList; - } - return { list, location: message.location, type: SUGGESTION_TYPE.FUNCTIONS }; - } - case 'incompleteArgument': { - const { currentFunction: functionName, currentArgs: functionArgs } = message; - const functionHelp = functionList.find((func) => func.name === functionName); - return { - list: getArgumentsHelp(functionHelp, functionArgs), - location: message.location, - type: SUGGESTION_TYPE.ARGUMENTS, - }; - } - case 'incompleteArgumentValue': { - const { name: argName, currentFunction: functionName, currentArgs: functionArgs } = message; - let valueSuggestions = []; - if (argValueSuggestions.hasDynamicSuggestionsForArgument(functionName, argName)) { - valueSuggestions = await argValueSuggestions.getDynamicSuggestionsForArgument( - functionName, - argName, - functionArgs - ); - } else { - const functionHelp = functionList.find((func) => func.name === functionName); - if (functionHelp) { - const argHelp = functionHelp.args.find((arg) => arg.name === argName); - if (argHelp && argHelp.suggestions) { - valueSuggestions = argHelp.suggestions; - } - } - } - return { - list: valueSuggestions, - location: { min: cursorPosition, max: cursorPosition }, - type: SUGGESTION_TYPE.ARGUMENT_VALUE, - }; - } - } - } -} - -export function insertAtLocation( - valueToInsert, - destination, - replacementRangeStart, - replacementRangeEnd -) { - // Insert the value at a location caret within the destination. - const prefix = destination.slice(0, replacementRangeStart); - const suffix = destination.slice(replacementRangeEnd, destination.length); - const result = `${prefix}${valueToInsert}${suffix}`; - return result; -} diff --git a/src/plugins/timeline/public/directives/timeline_expression_suggestions/_index.scss b/src/plugins/timeline/public/directives/timeline_expression_suggestions/_index.scss deleted file mode 100644 index e49c9151ff73..000000000000 --- a/src/plugins/timeline/public/directives/timeline_expression_suggestions/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./timeline_expression_suggestions"; diff --git a/src/plugins/timeline/public/directives/timeline_expression_suggestions/_timeline_expression_suggestions.scss b/src/plugins/timeline/public/directives/timeline_expression_suggestions/_timeline_expression_suggestions.scss deleted file mode 100644 index 89b96d11c6fd..000000000000 --- a/src/plugins/timeline/public/directives/timeline_expression_suggestions/_timeline_expression_suggestions.scss +++ /dev/null @@ -1,36 +0,0 @@ -.timSuggestions { - @include euiBottomShadowMedium; - - background-color: $euiColorLightestShade; - color: $euiTextColor; - border: $euiBorderThin; - border-radius: 0 0 $euiBorderRadius $euiBorderRadius !important; - z-index: $euiZLevel9; - max-height: $euiSizeXL * 10; - overflow-y: auto; - - &.timSuggestions-isPopover { - position: absolute; - top: 100%; - } -} - -.timSuggestions__item { - border-bottom: $euiBorderThin; - padding: $euiSizeXS $euiSizeL; - - &:hover, - &.active { - background-color: $euiColorLightShade; - } -} - -.timSuggestions__details { - background-color: $euiColorLightestShade; - padding: $euiSizeM; - border-radius: $euiBorderRadius; - - > table { - margin-bottom: 0; - } -} diff --git a/src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.html b/src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.html deleted file mode 100644 index f2469b99815a..000000000000 --- a/src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.html +++ /dev/null @@ -1,109 +0,0 @@ -<div - id="timelineSuggestionList" - class="timSuggestions" - role="listbox" - ng-class="{ 'timSuggestions-isPopover': shouldPopover === 'true' }" - data-suggestions-list -> - <div - class="timSuggestions__item" - id="timelineSuggestion{{$index}}" - role="option" - tabindex="0" - data-suggestion-list-item - ng-class="{active: $index === selectedIndex}" - ng-repeat="suggestion in suggestions track by suggestion.name" - ng-mousedown="onMouseDown($event)" - ng-click="onClickSuggestion({ suggestionIndex: $index })" - aria-label="{{suggestion.name}}" - aria-describedby="timelineSuggestionDescription{{$index}}" - data-test-subj="timelineSuggestionListItem" - > - - <div ng-switch on="suggestionsType"> - - <div ng-switch-when="functions"> - <h4> - <strong>.{{suggestion.name}}()</strong> - <small id="timelineSuggestionDescription{{$index}}"> - <span - ng-if="suggestion.chainable" - i18n-id="timeline.expressionSuggestions.func.description.chainableText" - i18n-default-message="{help} (Chainable)" - i18n-values="{ help: suggestion.help }" - ></span> - <span - ng-if="!suggestion.chainable" - i18n-id="timeline.expressionSuggestions.func.description.dataSourceText" - i18n-default-message="{help} (Data Source)" - i18n-values="{ help: suggestion.help }" - ></span> - </small> - </h4> - - <div ng-show="suggestion.args.length > (suggestion.chainable ? 1: 0)"> - <div ng-show="suggestions.length > 1"> - <strong - i18n-id="timeline.expressionSuggestions.arg.listTitle" - i18n-default-message="Arguments:" - ></strong> - <span ng-repeat="arg in suggestion.args" ng-hide="$index < 1 && suggestion.chainable"> - <strong>{{arg.name}}</strong>=(<em>{{arg.types.join(' | ')}}</em>) - <em ng-show="!$last">,</em> - </span> - </div> - - <div class="timSuggestions__details" ng-show="suggestions.length === 1"> - <table class="table table-striped table-condensed table-bordered"> - <thead> - <th - scope="col" - i18n-id="timeline.expressionSuggestions.arg.nameTitle" - i18n-default-message="Argument Name" - ></th> - <th - scope="col" - i18n-id="timeline.expressionSuggestions.arg.typesTitle" - i18n-default-message="Accepted Types" - ></th> - <th - scope="col" - i18n-id="timeline.expressionSuggestions.arg.infoTitle" - i18n-default-message="Information" - ></th> - </thead> - <tr ng-repeat="arg in suggestion.args" ng-hide="$index < 1 && suggestion.chainable"> - <td>{{arg.name}}</td> - <td><em>{{arg.types.join(', ')}}</em></td> - <td>{{arg.help}}</td> - </tr> - </table> - </div> - </div> - </div> - - <div ng-switch-when="arguments"> - <h4> - <strong>{{suggestion.name}}=</strong> - <small id="timelineSuggestionDescription{{$index}}"> - {{suggestion.help}} - </small> - </h4> - <div> - <strong>Accepts:</strong> - <em>{{suggestion.types.join(', ')}}</em> - </div> - </div> - - <div ng-switch-when="argument_value"> - <h4> - <strong>{{suggestion.name}}</strong> - <small id="timelineSuggestionDescription{{$index}}"> - {{suggestion.help}} - </small> - </h4> - </div> - - </div> - </div> -</div> diff --git a/src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.js b/src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.js deleted file mode 100644 index a2c24e57f756..000000000000 --- a/src/plugins/timeline/public/directives/timeline_expression_suggestions/timeline_expression_suggestions.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 './timeline_expression_suggestions.html'; - -export function TimelineExpressionSuggestions() { - return { - restrict: 'E', - scope: { - suggestions: '=', - suggestionsType: '=', - selectedIndex: '=', - onClickSuggestion: '&', - shouldPopover: '=', - }, - replace: true, - template, - link: function (scope) { - // This will prevent the expression input from losing focus. - scope.onMouseDown = (e) => e.preventDefault(); - }, - }; -} diff --git a/src/plugins/timeline/public/directives/timeline_grid.js b/src/plugins/timeline/public/directives/timeline_grid.js deleted file mode 100644 index 48db69aa4a0e..000000000000 --- a/src/plugins/timeline/public/directives/timeline_grid.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 $ from 'jquery'; - -export function initTimelineGridDirective(app) { - app.directive('timelineGrid', function () { - return { - restrict: 'A', - scope: { - timelineGridRows: '=', - timelineGridColumns: '=', - }, - link: function ($scope, $elem) { - function init() { - setDimensions(); - } - - $scope.$on('$destroy', function () { - $(window).off('resize'); //remove the handler added earlier - }); - - $(window).resize(function () { - setDimensions(); - }); - - $scope.$watchMulti(['timelineGridColumns', 'timelineGridRows'], function () { - setDimensions(); - }); - - function setDimensions() { - const borderSize = 2; - const headerSize = 45 + 35 + 28 + 20 * 2; // chrome + subnav + buttons + (container padding) - const verticalPadding = 10; - - if ($scope.timelineGridColumns != null) { - $elem.width($elem.parent().width() / $scope.timelineGridColumns - borderSize * 2); - } - - if ($scope.timelineGridRows != null) { - $elem.height( - ($(window).height() - headerSize) / $scope.timelineGridRows - - (verticalPadding + borderSize * 2) - ); - } - } - - init(); - }, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/timeline_help/_index.scss b/src/plugins/timeline/public/directives/timeline_help/_index.scss deleted file mode 100644 index 763746e95a10..000000000000 --- a/src/plugins/timeline/public/directives/timeline_help/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./timeline_help"; diff --git a/src/plugins/timeline/public/directives/timeline_help/_timeline_help.scss b/src/plugins/timeline/public/directives/timeline_help/_timeline_help.scss deleted file mode 100644 index c114f831adcb..000000000000 --- a/src/plugins/timeline/public/directives/timeline_help/_timeline_help.scss +++ /dev/null @@ -1,24 +0,0 @@ -.timHelp { - // EUITODO: Make .euiText > code background transparent - code { - background-color: transparentize($euiTextColor, 0.9); - } -} - -.timHelp__buttons { - display: flex; - justify-content: space-between; -} - -.timHelp__functions { - height: $euiSizeXL * 10; - overflow-y: auto; -} - -/** - * 1. Override bootstrap .table styles. - */ -.timHelp__functionsTableRow:hover, -.timHelp__functionDetailsTable { - background-color: $euiColorLightestShade !important; /* 1 */ -} diff --git a/src/plugins/timeline/public/directives/timeline_help/timeline_help.html b/src/plugins/timeline/public/directives/timeline_help/timeline_help.html deleted file mode 100644 index a241405b3c44..000000000000 --- a/src/plugins/timeline/public/directives/timeline_help/timeline_help.html +++ /dev/null @@ -1,507 +0,0 @@ -<div class="euiText timHelp"> - <div ng-show="page === 1"> - <div> - <h1 i18n-id="timeline.help.welcomeTitle" i18n-default-message="Welcome to {strongTimelineLabel}!" - i18n-values="{ html_strongTimelineLabel: '<strong>Timeline</strong>' }"></h1> - <p i18n-id="timeline.help.welcome.content.paragraph1" i18n-default-message="Timeline is the clawing, gnashing, zebra killing, pluggable time - series interface for {emphasizedEverything}. If your datastore can - produce a time series, then you have all of the awesome power of - Timeline at your disposal. Timeline lets you compare, combine, and - combobulate datasets across multiple datasources with one - easy-to-master expression syntax. This tutorial focuses on - OpenSearch, but you'll quickly discover that what you learn here - applies to any datasource Timeline supports." - i18n-values="{ html_emphasizedEverything: '<em>' + translations.emphasizedEverythingText + '</em>' }"></p> - <p> - <span i18n-id="timeline.help.welcome.content.paragraph2" - i18n-default-message="Ready to get started? Click {strongNext}. Want to skip the tutorial and view the docs?" - i18n-values="{ - html_strongNext: '<strong>' + translations.strongNextText + '</strong>', - }"></span> - <a ng-click="setPage(0)" i18n-id="timeline.help.welcome.content.functionReferenceLinkText" - i18n-default-message="Jump to the function reference"></a>. - </p> - </div> - <div class="timHelp__buttons"> - <button ng-click="opts.dontShowHelp()" class="kuiButton kuiButton--hollow"> - {{translations.dontShowHelpButtonLabel}} - </button> - - <button ng-click="setPage(page+1)" class="kuiButton kuiButton--primary"> - {{translations.nextButtonLabel}} - </button> - </div> - </div> - <div ng-show="page === 2"> - <div ng-show="!opensearch.valid"> - <div> - <h2 i18n-id="timeline.help.configuration.notValidTitle" i18n-default-message="First time configuration"></h2> - <p i18n-id="timeline.help.configuration.notValid.paragraph1" i18n-default-message="If you're using Logstash, you don't need to configure anything to - start exploring your log data with Timeline. To search other - indices, go to {advancedSettingsPath} and configure the {opensearchDefaultIndex} - and {opensearchTimefield} settings to match your indices." - i18n-values="{ - html_advancedSettingsPath: '<strong>' + translations.notValidAdvancedSettingsPath + '</strong>', - html_opensearchDefaultIndex: '<code>timeline:es.default_index</code>', - html_opensearchTimefield: '<code>timeline:es.timefield</code>', - }"></p> - <p i18n-id="timeline.help.configuration.notValid.paragraph2" i18n-default-message="You'll also see some other Timeline settings. For now, you don't need - to worry about them. Later, you'll see that you can set most of - them on the fly if you need to." - ></p> - </div> - <div class="timHelp__buttons"> - <button ng-click="setPage(page-1)" class="kuiButton kuiButton--primary"> - {{translations.previousButtonLabel}} - </button> - - <span ng-show="opensearch.invalidCount > 0 && !opensearch.valid" - i18n-id="timeline.help.configuration.notValid.notValidSettingsErrorMessage" i18n-default-message="Could not validate OpenSearch settings: {reason}. - Check your Advanced Settings and try again. ({count})" i18n-values="{ - html_reason: '<strong>' + opensearch.invalidReason + '</strong>', - count: opensearch.invalidCount, - }" - ></span> - - <button ng-click="recheckOpenSearch()" class="kuiButton kuiButton--primary" - i18n-id="timeline.help.configuration.notValid.validateButtonLabel" - i18n-default-message="Validate Config"></button> - - </div> - </div> - <div ng-show="opensearch.valid"> - <div> - <h2 i18n-id="timeline.help.configuration.validTitle" - i18n-default-message="Good news, OpenSearch is configured correctly!"></h2> - <p> - <span i18n-id="timeline.help.configuration.valid.paragraph1Part1" i18n-default-message="We validated your default index and your timefield and everything - looks ok. We found data from {statsMin} to {statsMax}. - You're probably all set. If this doesn't look right, see" - i18n-values="{ - html_statsMin: '<strong>' + opensearch.stats.min + '</strong>', - html_statsMax: '<strong>' + opensearch.stats.max + '</strong>', - }" i18n-description="Part of composite text timeline.help.configuration.valid.paragraph1Part1 + - timeline.help.configuration.firstTimeConfigurationLinkText + - timeline.help.configuration.valid.paragraph1Part2"></span> - <a ng-click="opensearch.valid = false" i18n-id="timeline.help.configuration.firstTimeConfigurationLinkText" - i18n-default-message="First time configuration" i18n-description="Part of composite text timeline.help.configuration.valid.paragraph1Part1 + - timeline.help.configuration.firstTimeConfigurationLinkText + - timeline.help.configuration.valid.paragraph1Part2"></a> - <span i18n-id="timeline.help.configuration.valid.paragraph1Part2" - i18n-default-message="for information about configuring the OpenSearch datasource." i18n-description="Part of composite text timeline.help.configuration.valid.paragraph1Part1 + - timeline.help.configuration.firstTimeConfigurationLinkText + - timeline.help.configuration.valid.paragraph1Part2"></span> - </p> - <p i18n-id="timeline.help.configuration.valid.paragraph2" i18n-default-message="You should already see one chart, but you might need to make a - couple adjustments before you see any interesting data:"></p> - <ul> - <li> - <strong i18n-id="timeline.help.configuration.valid.intervalsTitle" - i18n-default-message="Intervals"></strong> - <p> - <span i18n-id="timeline.help.configuration.valid.intervalsTextPart1" i18n-default-message="The interval selector at the right of the input bar lets you - control the sampling frequency. It's currently set to {interval}." - i18n-values="{ html_interval: '<code>' + state.interval + '</code>' }" i18n-description="Part of composite text - timeline.help.configuration.valid.intervalsTextPart1 + - (timeline.help.configuration.valid.intervalIsAutoText || - timeline.help.configuration.valid.intervals.content.intervalIsNotAutoText) + - timeline.help.configuration.valid.intervalsTextPart2"></span> - <span ng-show="state.interval == 'auto'"> - <strong i18n-id="timeline.help.configuration.valid.intervalIsAutoText" - i18n-default-message="You're all set!" i18n-description="Part of composite text - timeline.help.configuration.valid.intervalsTextPart1 + - (timeline.help.configuration.valid.intervalIsAutoText || - timeline.help.configuration.valid.intervals.content.intervalIsNotAutoText) + - timeline.help.configuration.valid.intervalsTextPart2"></strong> - </span> - <span ng-show="state.interval != 'auto'" - i18n-id="timeline.help.configuration.valid.intervals.content.intervalIsNotAutoText" - i18n-default-message="Set it to {auto} to let Timeline choose an appropriate interval." - i18n-description="Part of composite text - timeline.help.configuration.valid.intervalsTextPart1 + - (timeline.help.configuration.valid.intervalIsAutoText || - timeline.help.configuration.valid.intervals.content.intervalIsNotAutoText) + - timeline.help.configuration.valid.intervalsTextPart2" - i18n-values="{ html_auto: '<code>auto</code>' }"></span> - <span i18n-id="timeline.help.configuration.valid.intervalsTextPart2" i18n-default-message="If Timeline thinks your combination of time range and interval - will produce too many data points, it throws an error. - You can adjust that limit by configuring {maxBuckets} in {advancedSettingsPath}." - i18n-values="{ - html_maxBuckets: '<code>timeline:max_buckets</code>', - html_advancedSettingsPath: '<strong>' + translations.validAdvancedSettingsPath + '</strong>', - }" - ></span> - </p> - </li> - <li> - <strong i18n-id="timeline.help.configuration.valid.timeRangeTitle" - i18n-default-message="Time range"></strong> - <p i18n-id="timeline.help.configuration.valid.timeRangeText" i18n-default-message="Use the timepicker in the OpenSearch Dashboards toolbar to select the time period - that contains the data you want to visualize. Make sure you select - a time period that includes all or part of the time range shown above." - ></p> - </li> - </ul> - <p i18n-id="timeline.help.configuration.valid.paragraph3" - i18n-default-message="Now, you should see a line chart that displays a count of your data points over time."> - </p> - </div> - <div class="timHelp__buttons"> - <button ng-click="setPage(page-1)" class="kuiButton kuiButton--primary"> - {{translations.previousButtonLabel}} - </button> - - <button ng-click="setPage(page+1)" class="kuiButton kuiButton--primary"> - {{translations.nextButtonLabel}} - </button> - </div> - </div> - </div> - <div ng-show="page === 3"> - <div> - <h2 i18n-id="timeline.help.queryingTitle" i18n-default-message="Querying the OpenSearch datasource"></h2> - <p i18n-id="timeline.help.querying.paragraph1" i18n-default-message="Now that we've validated that you have a working OpenSearch - datasource, you can start submitting queries. For starters, - enter {opensearchPattern} in the input bar and hit enter." - i18n-values="{ - html_opensearchPattern: '<code>.opensearch(*)</code>', - }" - ></p> - <p> - <span i18n-id="timeline.help.querying.paragraph2Part1" i18n-default-message="This says {opensearchAsteriskQueryDescription}. If you want to find a subset, you could enter something - like {htmlQuery} to count events that match {html}, or {bobQuery} - to find events that contain {bob} in the {user} field and have a {bytes} - field that is greater than 100. Note that this query is enclosed in single - quotes—that's because it contains spaces. You can enter any" - i18n-values="{ - html_opensearchAsteriskQueryDescription: '<em>' + translations.opensearchAsteriskQueryDescription + '</em>', - html_html: '<em>html</em>', - html_htmlQuery: '<code>.opensearch(html)</code>', - html_bobQuery: '<code>.opensearch(\'user:bob AND bytes:>100\')</code>', - html_bob: '<em>bob</em>', - html_user: '<code>user</code>', - html_bytes: '<code>bytes</code>', - }" i18n-description="Part of composite text - timeline.help.querying.paragraph2Part1 + - timeline.help.querying.luceneQueryLinkText + - timeline.help.querying.paragraph2Part2"></span> - <a href="https://opensearch.org/docs/latest/opensearch/query-dsl/full-text/#query-string" - target="_blank" rel="noopener noreferrer" i18n-id="timeline.help.querying.luceneQueryLinkText" - i18n-default-message="Lucene query string" i18n-description="Part of composite text - timeline.help.querying.paragraph2Part1 + - timeline.help.querying.luceneQueryLinkText + - timeline.help.querying.paragraph2Part2"></a> - <span i18n-id="timeline.help.querying.paragraph2Part2" - i18n-default-message="as the first argument to the {opensearchQuery} function." i18n-values="{ - html_opensearchQuery: '<code>.opensearch()</code>', - }" i18n-description="Part of composite text - timeline.help.querying.paragraph2Part1 + - timeline.help.querying.luceneQueryLinkText + - timeline.help.querying.paragraph2Part2"></span> - </p> - <h4 i18n-id="timeline.help.querying.passingArgumentsTitle" i18n-default-message="Passing arguments"></h4> - <p i18n-id="timeline.help.querying.passingArgumentsText" i18n-default-message="Timeline has a number of shortcuts that make it easy to do common things. - One is that for simple arguments that don't contain spaces or special - characters, you don't need to use quotes. Many functions also have defaults. - For example, {opensearchEmptyQuery} and {opensearchStarQuery} do the same thing. - Arguments also have names, so you don't have to specify them in a specific order. - For example, you can enter {opensearchLogstashQuery} to tell the OpenSearch datasource - {opensearchIndexQueryDescription}." - i18n-values="{ - html_opensearchEmptyQuery: '<code>.opensearch()</code>', - html_opensearchStarQuery: '<code>.opensearch(*)</code>', - html_opensearchLogstashQuery: '<code>.opensearch(index=\'logstash-*\', q=\'*\')</code>', - html_opensearchIndexQueryDescription: '<em>' + translations.opensearchIndexQueryDescription + '</em>', - }"></p> - <h4 i18n-id="timeline.help.querying.countTitle" i18n-default-message="Beyond count"></h4> - <p> - <span i18n-id="timeline.help.querying.countTextPart1" - i18n-default-message="Counting events is all well and good, but the OpenSearch datasource also supports any" - i18n-description="Part of composite text - timeline.help.querying.countTextPart1 + - timeline.help.querying.countMetricAggregationLinkText + - timeline.help.querying.countTextPart2"></span> - <a href="https://opensearch.org/docs/latest/opensearch/metric-agg/" - target="_blank" rel="noopener noreferrer" i18n-id="timeline.help.querying.countMetricAggregationLinkText" - i18n-default-message="OpenSearch metric aggregation" i18n-description="Part of composite text - timeline.help.querying.countTextPart1 + - timeline.help.querying.countMetricAggregationLinkText + - timeline.help.querying.countTextPart2"></a> - <span i18n-id="timeline.help.querying.countTextPart2" i18n-default-message="that returns a single value. Some of the most useful are - {min}, {max}, {avg}, {sum}, and {cardinality}. - Let's say you want a unique count of the {srcIp} field. - Simply use the {cardinality} metric: {opensearchCardinalityQuery}. To get the - average of the {bytes} field, you can use the {avg} metric: {opensearchAvgQuery}." - i18n-values="{ - html_min: '<code>min</code>', - html_max: '<code>max</code>', - html_avg: '<code>avg</code>', - html_sum: '<code>sum</code>', - html_cardinality: '<code>cardinality</code>', - html_bytes: '<code>bytes</code>', - html_srcIp: '<code>src_ip</code>', - html_opensearchCardinalityQuery: '<code>.opensearch(*, metric=\'cardinality:src_ip\')</code>', - html_opensearchAvgQuery: '<code>.opensearch(metric=\'avg:bytes\')</code>', - }" i18n-description="Part of composite text - timeline.help.querying.countTextPart1 + - timeline.help.querying.countMetricAggregationLinkText + - timeline.help.querying.countTextPart2"></span> - </p> - </div> - <div class="timHelp__buttons"> - <button ng-click="setPage(page-1)" class="kuiButton kuiButton--primary"> - {{translations.previousButtonLabel}} - </button> - - <button ng-click="setPage(page+1)" class="kuiButton kuiButton--primary"> - {{translations.nextButtonLabel}} - </button> - </div> - </div> - - <div ng-show="page === 4"> - <div> - <h2 i18n-id="timeline.help.expressionsTitle" i18n-default-message="Expressing yourself with expressions"></h2> - <p i18n-id="timeline.help.expressions.paragraph1" i18n-default-message="Every expression starts with a datasource function. From there, you - can append new functions to the datasource to transform and augment it."></p> - <p i18n-id="timeline.help.expressions.paragraph2" i18n-default-message="By the way, from here on out you probably know more about your data - than we do. Feel free to replace the sample queries with something - more meaningful!"></p> - <p i18n-id="timeline.help.expressions.paragraph3" i18n-default-message="We're going to experiment, so click {strongAdd} in the OpenSearch Dashboards toolbar - to add another chart or three. Then, select a chart, - copy one of the following expressions, paste it into the input bar, - and hit enter. Rinse, repeat to try out the other expressions." - i18n-values="{ html_strongAdd: '<strong>' + translations.strongAddText + '</strong>' }" - ></p> - <table class="table table-condensed table-striped"> - <tr> - <td><code>.opensearch(*), .opensearch(US)</code></td> - <td i18n-id="timeline.help.expressions.examples.twoExpressionsDescription" - i18n-default-message="{descriptionTitle} Two expressions on the same chart." - i18n-values="{ - html_descriptionTitle: '<strong>' + translations.twoExpressionsDescriptionTitle + '</strong>', - }" - ></td> - </tr> - <tr> - <td><code>.opensearch(*).color(#f66), .opensearch(US).bars(1)</code></td> - <td - i18n-id="timeline.help.expressions.examples.customStylingDescription" - i18n-default-message="{descriptionTitle} Colorizes the first series red and - uses 1 pixel wide bars for the second series." - i18n-values="{ - html_descriptionTitle: '<strong>' + translations.customStylingDescriptionTitle + '</strong>', - }" - ></td> - </tr> - <tr> - <td> - <code - >.opensearch(*).color(#f66).lines(fill=3), .opensearch(US).bars(1).points(radius=3, - weight=1)</code - > - </td> - <td i18n-id="timeline.help.expressions.examples.namedArgumentsDescription" i18n-default-message="{descriptionTitle} Forget trying to remember what order you need - to specify arguments in, use named arguments to make - the expressions easier to read and write." - i18n-values="{ - html_descriptionTitle: '<strong>' + translations.namedArgumentsDescriptionTitle + '</strong>', - }" - ></td> - </tr> - <tr> - <td><code>(.opensearch(*), .opensearch(GB)).points()</code></td> - <td i18n-id="timeline.help.expressions.examples.groupedExpressionsDescription" i18n-default-message="{descriptionTitle} You can also chain groups of expressions to - functions. Here, both series are shown as points instead of lines." i18n-values="{ - html_descriptionTitle: '<strong>' + translations.groupedExpressionsDescriptionTitle + '</strong>', - }" - ></td> - </tr> - </table> - <p> - <span i18n-id="timeline.help.expressions.paragraph4" i18n-default-message="Timeline provides additional view transformation functions you can use - to customize the appearance of your charts. For the complete list, see the" - i18n-description="Part of composite text - timeline.help.expressions.paragraph4 + - timeline.help.expressions.functionReferenceLinkText"></span> - <a ng-click="setPage(0)" i18n-id="timeline.help.expressions.functionReferenceLinkText" - i18n-default-message="Function reference" i18n-description="Part of composite text - timeline.help.expressions.paragraph4 + - timeline.help.expressions.functionReferenceLinkText"></a>. - </p> - </div> - <div class="timHelp__buttons"> - <button ng-click="setPage(page-1)" class="kuiButton kuiButton--primary"> - {{translations.previousButtonLabel}} - </button> - - <button ng-click="setPage(page+1)" class="kuiButton kuiButton--primary"> - {{translations.nextButtonLabel}} - </button> - </div> - </div> - <div ng-show="page === 5"> - <div> - <h2 i18n-id="timeline.help.dataTransformingTitle" - i18n-default-message="Transforming your data: the real fun begins!"></h2> - <p i18n-id="timeline.help.dataTransforming.paragraph1" i18n-default-message="Now that you've mastered the basics, it's time to unleash the power of - Timeline. Let's figure out what percentage some subset of our data - represents of the whole, over time. For example, what percentage of - our web traffic comes from the US?"></p> - <p i18n-id="timeline.help.dataTransforming.paragraph2" - i18n-default-message="First, we need to find all events that contain US: {opensearchUsQuery}." - i18n-values="{ html_opensearchUsQuery: '<code>.opensearch(\'US\')</code>' }"></p> - <p i18n-id="timeline.help.dataTransforming.paragraph3" i18n-default-message="Next, we want to calculate the ratio of US events to the whole. - To divide {us} by everything, we can use the {divide} function: - {divideDataQuery}." - i18n-values="{ - html_us: '<code>\'US\'</code>', - html_divide: '<code>divide</code>', - html_divideDataQuery: '<code>.opensearch(\'US\').divide(.opensearch())</code>', - }"></p> - <p i18n-id="timeline.help.dataTransforming.paragraph4" i18n-default-message="Not bad, but this gives us a number between 0 and 1. To convert it - to a percentage, simply multiply by 100: {multiplyDataQuery}." - i18n-values="{ html_multiplyDataQuery: '<code>.opensearch(\'US\').divide(.opensearch()).multiply(100)</code>' }"> - </p> - <p i18n-id="timeline.help.dataTransforming.paragraph5" i18n-default-message="Now we know what percentage of our traffic comes from the US, and - can see how it has changed over time! Timeline has a number of - built-in arithmetic functions, such as {sum}, {subtract}, {multiply}, - and {divide}. Many of these can take a series or a number. There are - also other useful data transformation functions, such as - {movingaverage}, {abs}, and {derivative}." - i18n-values="{ - html_sum: '<code>sum</code>', - html_subtract: '<code>subtract</code>', - html_multiply: '<code>multiply</code>', - html_divide: '<code>divide</code>', - html_movingaverage: '<code>movingaverage</code>', - html_abs: '<code>abs</code>', - html_derivative: '<code>derivative</code>', - }" - ></p> - <p> - <span i18n-id="timeline.help.dataTransforming.paragraph6Part1" - i18n-default-message="Now that you're familiar with the syntax, refer to the" i18n-description="Part of composite text - timeline.help.dataTransforming.paragraph6Part1 + - timeline.help.dataTransforming.functionReferenceLinkText + - timeline.help.dataTransforming.paragraph6Part2"></span> - <a ng-click="setPage(0)" i18n-id="timeline.help.dataTransforming.functionReferenceLinkText" - i18n-default-message="Function reference" i18n-description="Part of composite text - timeline.help.dataTransforming.paragraph6Part1 + - timeline.help.dataTransforming.functionReferenceLinkText + - timeline.help.dataTransforming.paragraph6Part2"></a> - <span i18n-id="timeline.help.dataTransforming.paragraph6Part2" i18n-default-message="to see how to use all of the available Timeline functions. - You can view the reference at any time by clicking \{Docs\} - in the OpenSearch Dashboards toolbar. To get back to this tutorial, click the - \{Tutorial\} link at the top of the reference." i18n-description="Part of composite text - timeline.help.dataTransforming.paragraph6Part1 + - timeline.help.dataTransforming.functionReferenceLinkText + - timeline.help.dataTransforming.paragraph6Part2"></span> - </p> - </div> - <div class="timHelp__buttons"> - <button ng-click="setPage(page-1)" class="kuiButton kuiButton--primary"> - {{translations.previousButtonLabel}} - </button> - - <button ng-click="opts.dontShowHelp()" class="kuiButton kuiButton--hollow"> - {{translations.dontShowHelpButtonLabel}} - </button> - </div> - </div> -</div> -<div ng-show="page === 0"> - <h2 class="kuiLocalDropdownTitle" i18n-id="timeline.help.mainPageTitle" i18n-default-message="Help"></h2> - - <timeline-help-tabs activate-tab="activateTab" active-tab="activeTab"> - </timeline-help-tabs> - - <div ng-show="activeTab == 'funcref'" class="list-group-item list-group-item--noBorder"> - <div class="kuiLocalDropdownHelpText"> - <span i18n-id="timeline.help.mainPage.functionReference.gettingStartedText" - i18n-default-message="Click any function for more information. Just getting started?"></span> - <a i18n-id="timeline.help.mainPage.functionReference.welcomePageLinkText" - i18n-default-message="Check out the tutorial" class="kuiLink" ng-click="setPage(1)" osd-accessible-click></a>. - </div> - - <div class="timHelp__functions"> - <table class="table table-condensed table-bordered"> - <tr - class="timHelp__functionsTableRow" - ng-repeat-start="function in functions.list" - ng-class="{active: functions.details === function.name}" - ng-click="functions.details = - (functions.details === function.name ? - null : function.name)" - osd-accessible-click - > - <td><strong>.{{function.name}}()</strong></td> - <td>{{function.help}}</td> - </tr> - <tr ng-if="functions.details === function.name" ng-repeat-end> - <td colspan="2"> - <div class="suggestion-details"> - <table - class="table table-condensed table-bordered timHelp__functionDetailsTable" - ng-show="function.args.length > (function.chainable ? 1: 0)" - > - <thead> - <th scope="col" - i18n-id="timeline.help.mainPage.functionReference.detailsTable.argumentNameColumnLabel" - i18n-default-message="Argument Name"></th> - <th scope="col" - i18n-id="timeline.help.mainPage.functionReference.detailsTable.acceptedTypesColumnLabel" - i18n-default-message="Accepted Types"></th> - <th scope="col" i18n-id="timeline.help.mainPage.functionReference.detailsTable.informationColumnLabel" - i18n-default-message="Information"></th> - </thead> - <tr ng-repeat="arg in function.args" ng-hide="$index < 1 && function.chainable"> - <td>{{arg.name}}</td> - <td><em>{{arg.types.join(', ')}}</em></td> - <td>{{arg.help}}</td> - </tr> - </table> - <div ng-hide="function.args.length > (function.chainable ? 1: 0)"> - <em i18n-id="timeline.help.mainPage.functionReference.noArgumentsFunctionErrorMessage" - i18n-default-message="This function does not accept any arguments. Well that's simple, isn't it?"></em> - </div> - </div> - </td> - </tr> - </table> - </div> - </div> - - <div ng-show="activeTab == 'keyboardtips'" class="list-group-item list-group-item--noBorder"> - <!-- General editing tips --> - <dl class="dl-horizontal"> - <dd> - <strong i18n-id="timeline.help.mainPage.keyboardTips.generalEditingTitle" - i18n-default-message="General editing"></strong> - </dd> - <dt></dt> - <dt>Ctrl/Cmd + Enter</dt> - <dd i18n-id="timeline.help.mainPage.keyboardTips.generalEditing.submitRequestText" - i18n-default-message="Submit request"></dd> - </dl> - - <!-- Auto complete tips --> - <dl class="dl-horizontal"> - <dt></dt> - <dd> - <strong i18n-id="timeline.help.mainPage.keyboardTips.autoCompleteTitle" - i18n-default-message="When auto-complete is visible"></strong> - </dd> - <dt i18n-id="timeline.help.mainPage.keyboardTips.autoComplete.downArrowLabel" i18n-default-message="Down arrow"> - </dt> - <dd i18n-id="timeline.help.mainPage.keyboardTips.autoComplete.downArrowDescription" - i18n-default-message="Switch focus to auto-complete menu. Use arrows to further select a term"></dd> - <dt>Enter/Tab</dt> - <dd i18n-id="timeline.help.mainPage.keyboardTips.autoComplete.enterTabDescription" - i18n-default-message="Select the currently selected or the top most term in auto-complete menu"></dd> - <dt>Esc</dt> - <dd i18n-id="timeline.help.mainPage.keyboardTips.autoComplete.escDescription" - i18n-default-message="Close auto-complete menu"></dd> - </dl> - </div> -</div> diff --git a/src/plugins/timeline/public/directives/timeline_help/timeline_help.js b/src/plugins/timeline/public/directives/timeline_help/timeline_help.js deleted file mode 100644 index 86ba02c6c87b..000000000000 --- a/src/plugins/timeline/public/directives/timeline_help/timeline_help.js +++ /dev/null @@ -1,177 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 './timeline_help.html'; -import { i18n } from '@osd/i18n'; -import _ from 'lodash'; -import moment from 'moment'; - -export function initTimelineHelpDirective(app) { - app.directive('timelineHelp', function ($http) { - return { - restrict: 'E', - template, - controller: function ($scope) { - $scope.functions = { - list: [], - details: null, - }; - - $scope.activeTab = 'funcref'; - $scope.activateTab = function (tabName) { - $scope.activeTab = tabName; - }; - - function init() { - $scope.opensearch = { - invalidCount: 0, - }; - - $scope.translations = { - nextButtonLabel: i18n.translate('timeline.help.nextPageButtonLabel', { - defaultMessage: 'Next', - }), - previousButtonLabel: i18n.translate('timeline.help.previousPageButtonLabel', { - defaultMessage: 'Previous', - }), - dontShowHelpButtonLabel: i18n.translate('timeline.help.dontShowHelpButtonLabel', { - defaultMessage: `Don't show this again`, - }), - strongNextText: i18n.translate('timeline.help.welcome.content.strongNextText', { - defaultMessage: 'Next', - }), - emphasizedEverythingText: i18n.translate( - 'timeline.help.welcome.content.emphasizedEverythingText', - { - defaultMessage: 'everything', - } - ), - notValidAdvancedSettingsPath: i18n.translate( - 'timeline.help.configuration.notValid.advancedSettingsPathText', - { - defaultMessage: 'Management / OpenSearch Dashboards / Advanced Settings', - } - ), - validAdvancedSettingsPath: i18n.translate( - 'timeline.help.configuration.valid.advancedSettingsPathText', - { - defaultMessage: 'Management/OpenSearch Dashboards/Advanced Settings', - } - ), - opensearchAsteriskQueryDescription: i18n.translate( - 'timeline.help.querying.opensearchAsteriskQueryDescriptionText', - { - defaultMessage: 'hey OpenSearch, find everything in my default index', - } - ), - opensearchIndexQueryDescription: i18n.translate( - 'timeline.help.querying.opensearchIndexQueryDescriptionText', - { - defaultMessage: 'use * as the q (query) for the logstash-* index', - } - ), - strongAddText: i18n.translate('timeline.help.expressions.strongAddText', { - defaultMessage: 'Add', - }), - twoExpressionsDescriptionTitle: i18n.translate( - 'timeline.help.expressions.examples.twoExpressionsDescriptionTitle', - { - defaultMessage: 'Double the fun.', - } - ), - customStylingDescriptionTitle: i18n.translate( - 'timeline.help.expressions.examples.customStylingDescriptionTitle', - { - defaultMessage: 'Custom styling.', - } - ), - namedArgumentsDescriptionTitle: i18n.translate( - 'timeline.help.expressions.examples.namedArgumentsDescriptionTitle', - { - defaultMessage: 'Named arguments.', - } - ), - groupedExpressionsDescriptionTitle: i18n.translate( - 'timeline.help.expressions.examples.groupedExpressionsDescriptionTitle', - { - defaultMessage: 'Grouped expressions.', - } - ), - }; - - getFunctions(); - checkOpenSearch(); - } - - function getFunctions() { - return $http.get('../api/timeline/functions').then(function (resp) { - $scope.functions.list = resp.data; - }); - } - $scope.recheckOpenSearch = function () { - $scope.opensearch.valid = null; - checkOpenSearch().then(function (valid) { - if (!valid) $scope.opensearch.invalidCount++; - }); - }; - - function checkOpenSearch() { - return $http.get('../api/timeline/validate/opensearch').then(function (resp) { - if (resp.data.ok) { - $scope.opensearch.valid = true; - $scope.opensearch.stats = { - min: moment(resp.data.min).format('LLL'), - max: moment(resp.data.max).format('LLL'), - field: resp.data.field, - }; - } else { - $scope.opensearch.valid = false; - $scope.opensearch.invalidReason = (function () { - try { - const opensearchResp = JSON.parse(resp.data.resp.response); - return _.get(opensearchResp, 'error.root_cause[0].reason'); - } catch (e) { - if (_.get(resp, 'data.resp.message')) return _.get(resp, 'data.resp.message'); - if (_.get(resp, 'data.resp.output.payload.message')) - return _.get(resp, 'data.resp.output.payload.message'); - return i18n.translate('timeline.help.unknownErrorMessage', { - defaultMessage: 'Unknown error', - }); - } - })(); - } - return $scope.opensearch.valid; - }); - } - init(); - }, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/timeline_interval/_index.scss b/src/plugins/timeline/public/directives/timeline_interval/_index.scss deleted file mode 100644 index fb597657a0a7..000000000000 --- a/src/plugins/timeline/public/directives/timeline_interval/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./timeline_interval"; diff --git a/src/plugins/timeline/public/directives/timeline_interval/_timeline_interval.scss b/src/plugins/timeline/public/directives/timeline_interval/_timeline_interval.scss deleted file mode 100644 index 6117bbff13eb..000000000000 --- a/src/plugins/timeline/public/directives/timeline_interval/_timeline_interval.scss +++ /dev/null @@ -1,24 +0,0 @@ -timeline-interval { - display: flex; -} - -.timInterval__input { - width: $euiSizeXL * 2; -} - -.timInterval__input--compact { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.timInterval__presets { - width: $euiSizeXL * 3; -} - -.timInterval__presets--compact { - width: $euiSizeXL * 1; - padding-left: 0; - border-left: none; - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} diff --git a/src/plugins/timeline/public/directives/timeline_interval/timeline_interval.html b/src/plugins/timeline/public/directives/timeline_interval/timeline_interval.html deleted file mode 100644 index 7eb1b72e46fd..000000000000 --- a/src/plugins/timeline/public/directives/timeline_interval/timeline_interval.html +++ /dev/null @@ -1,22 +0,0 @@ -<input - input-focus - aria-label="{{ ::'timeline.intervals.customIntervalAriaLabel' | i18n: { defaultMessage: 'Custom interval' } }}" - class="kuiTextInput timInterval__input" - ng-show="interval === 'other'" - ng-class="{ 'timInterval__input--compact': interval === 'other' }" - ng-model="otherInterval" -><select - id="timelineInterval" - aria-label="{{ ::'timeline.intervals.selectIntervalAriaLabel' | i18n: { defaultMessage: 'Select interval' } }}" - class="form-control timInterval__presets" - ng-class="{ 'timInterval__presets--compact': interval === 'other'}" - ng-model="interval" -> - <option - ng-repeat="intervalOption in intervalOptions" - aria-label="{{::intervalLabels[intervalOption]}}" - value="{{::intervalOption}}" - > - {{::intervalOption}} - </option> -</select> diff --git a/src/plugins/timeline/public/directives/timeline_interval/timeline_interval.js b/src/plugins/timeline/public/directives/timeline_interval/timeline_interval.js deleted file mode 100644 index 66869155916a..000000000000 --- a/src/plugins/timeline/public/directives/timeline_interval/timeline_interval.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; -import $ from 'jquery'; -import template from './timeline_interval.html'; - -export function TimelineInterval($timeout) { - return { - restrict: 'E', - scope: { - // The interval model - model: '=', - changeInterval: '=', - }, - template, - link: function ($scope, $elem) { - $scope.intervalOptions = ['auto', '1s', '1m', '1h', '1d', '1w', '1M', '1y', 'other']; - $scope.intervalLabels = { - auto: 'auto', - '1s': '1 second', - '1m': '1 minute', - '1h': '1 hour', - '1d': '1 day', - '1w': '1 week', - '1M': '1 month', - '1y': '1 year', - other: 'other', - }; - - $scope.$watch('model', function (newVal, oldVal) { - // Only run this on initialization - if (newVal !== oldVal || oldVal == null) return; - - if (_.includes($scope.intervalOptions, newVal)) { - $scope.interval = newVal; - } else { - $scope.interval = 'other'; - } - - if (newVal !== 'other') { - $scope.otherInterval = newVal; - } - }); - - $scope.$watch('interval', function (newVal, oldVal) { - if (newVal === oldVal || $scope.model === newVal) return; - - if (newVal === 'other') { - $scope.otherInterval = oldVal; - $scope.changeInterval($scope.otherInterval); - $timeout(function () { - $('input', $elem).select(); - }, 0); - } else { - $scope.otherInterval = $scope.interval; - $scope.changeInterval($scope.interval); - } - }); - - $scope.$watch('otherInterval', function (newVal, oldVal) { - if (newVal === oldVal || $scope.model === newVal) return; - $scope.changeInterval(newVal); - }); - }, - }; -} diff --git a/src/plugins/timeline/public/directives/timeline_load_sheet.js b/src/plugins/timeline/public/directives/timeline_load_sheet.js deleted file mode 100644 index 718d090133e7..000000000000 --- a/src/plugins/timeline/public/directives/timeline_load_sheet.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '../partials/load_sheet.html'; - -export function initTimelineLoadSheetDirective(app) { - app.directive('timelineLoad', function () { - return { - replace: true, - restrict: 'E', - template, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/timeline_options_sheet.js b/src/plugins/timeline/public/directives/timeline_options_sheet.js deleted file mode 100644 index 485869f5fcf8..000000000000 --- a/src/plugins/timeline/public/directives/timeline_options_sheet.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '../partials/sheet_options.html'; - -export function initTimelineOptionsSheetDirective(app) { - app.directive('timelineOptions', function () { - return { - replace: true, - restrict: 'E', - template, - }; - }); -} diff --git a/src/plugins/timeline/public/directives/timeline_save_sheet.js b/src/plugins/timeline/public/directives/timeline_save_sheet.js deleted file mode 100644 index 80ffe3c4e294..000000000000 --- a/src/plugins/timeline/public/directives/timeline_save_sheet.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 saveTemplate from '../partials/save_sheet.html'; - -export function initTimelineSaveSheetDirective(app) { - app.directive('timelineSave', function () { - return { - replace: true, - restrict: 'E', - template: saveTemplate, - }; - }); -} diff --git a/src/plugins/timeline/public/index.html b/src/plugins/timeline/public/index.html deleted file mode 100644 index 1b116e54479b..000000000000 --- a/src/plugins/timeline/public/index.html +++ /dev/null @@ -1,95 +0,0 @@ -<timeline-app class="timApp app-container"> - <span class="kuiLocalTitle"> - <span class="timApp__stats" ng-show="stats"> - <span - i18n-id="timeline.topNavMenu.statsDescription" - i18n-default-message="Query Time {queryTime}ms / Processing Time {processingTime}ms" - i18n-values="{ - queryTime: stats.queryTime - stats.invokeTime, - processingTime: stats.sheetTime - stats.queryTime, - }"></span> - </span> - </span> - <!-- Local nav. --> - <osd-top-nav - app-name="'timeline'" - config="topNavMenu" - show-search-bar="true" - show-search-bar-inline="true" - show-filter-bar="false" - show-query-input="false" - date-range-from="model.timeRange.from" - date-range-to="model.timeRange.to" - is-refresh-paused="model.refreshInterval.pause" - refresh-interval="model.refreshInterval.value" - on-refresh-change="onRefreshChange" - on-query-submit="onTimeUpdate"> - </osd-top-nav> - - - <div class="timApp__menus"> - <timeline-deprecation></timeline-deprecation> - <timeline-help ng-show="menus.showHelp"></timeline-help> - <timeline-save ng-show="menus.showSave"></timeline-save> - <timeline-load ng-show="menus.showLoad"></timeline-load> - <timeline-options ng-show="menus.showOptions"></timeline-options> - </div> - - <div class="timApp__container"> - <div> - <!-- Search. --> - <form - role="form" - ng-submit="updateChart()" - class="kuiFieldGroup kuiFieldGroup--alignTop kuiVerticalRhythm" - > - <div class="kuiFieldGroupSection kuiFieldGroupSection--wide"> - <timeline-expression-input - sheet="expression" - rows="1" - update-chart="updateChart()" - should-popover-suggestions="true" - ></timeline-expression-input> - </div> - - <div class="kuiFieldGroupSection"> - <timeline-interval - class="kuiVerticalRhythmSmall" - model="state.interval" - change-interval="changeInterval" - ></timeline-interval> - - <button - type="submit" - aria-label="{{ ::'timeline.search.submitAriaLabel' | i18n: { defaultMessage: 'Search' } }}" - class="kuiButton kuiButton--primary fullWidth kuiVerticalRhythmSmall" - > - <span aria-hidden="true" class="kuiButton__icon kuiIcon fa-play"></span> - </button> - </div> - </form> - - <div class="kuiVerticalRhythm"> - <timeline-fullscreen - ng-show="transient.fullscreen" - transient="transient" - state="state" - series="sheet[state.selected]" - expression="state.sheet[state.selected]" - on-search="search" - ></timeline-fullscreen> - - <timeline-cells - ng-show="!transient.fullscreen" - transient="transient" - state="state" - sheet="sheet" - on-search="search" - on-select="setActiveCell" - on-remove-sheet="removeSheet" - ></timeline-cells> - </div> - </div> - </div> - </div> -</div> diff --git a/src/plugins/timeline/public/index.scss b/src/plugins/timeline/public/index.scss deleted file mode 100644 index 8c2a25c383a6..000000000000 --- a/src/plugins/timeline/public/index.scss +++ /dev/null @@ -1,18 +0,0 @@ -/* Timeline plugin styles */ - -// Prefix all styles with "tim" to avoid conflicts. -// Examples -// timChart -// timChart__legend -// timChart__legend--small -// timChart__legend-isLoading - -@import "./app"; -@import "./base"; -@import "./directives/index"; - -// these styles is needed to be loaded here explicitly if the timeline visualization was not opened in browser -// styles for timeline visualization are lazy loaded only while a vis is opened -// this will duplicate styles only if both Timeline app and timeline visualization are loaded -// could be left here as it is since the Timeline app is deprecated -@import "../../vis_type_timeline/public/components/timeline_vis"; diff --git a/src/plugins/timeline/public/index.ts b/src/plugins/timeline/public/index.ts deleted file mode 100644 index f4b35580b7d1..000000000000 --- a/src/plugins/timeline/public/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { PluginInitializerContext } from 'opensearch-dashboards/public'; -import { TimelinePlugin as Plugin } from './plugin'; - -export function plugin(initializerContext: PluginInitializerContext) { - return new Plugin(initializerContext); -} diff --git a/src/plugins/timeline/public/lib/observe_resize.js b/src/plugins/timeline/public/lib/observe_resize.js deleted file mode 100644 index 32a582b19be8..000000000000 --- a/src/plugins/timeline/public/lib/observe_resize.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -export default function ($elem, fn, frequency) { - frequency = frequency || 500; - let currentHeight = $elem.height(); - let currentWidth = $elem.width(); - - let timeout; - - function checkLoop() { - timeout = setTimeout(function () { - if (currentHeight !== $elem.height() || currentWidth !== $elem.width()) { - currentHeight = $elem.height(); - currentWidth = $elem.width(); - - if (currentWidth > 0 && currentWidth > 0) fn(); - } - checkLoop(); - }, frequency); - } - - checkLoop(); - - return function () { - clearTimeout(timeout); - }; -} diff --git a/src/plugins/timeline/public/panels/panel.ts b/src/plugins/timeline/public/panels/panel.ts deleted file mode 100644 index bff7b49ab9f9..000000000000 --- a/src/plugins/timeline/public/panels/panel.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -interface PanelConfig { - help?: string; - render?: Function; -} - -export class Panel { - name: string; - help: string; - render: Function | undefined; - - constructor(name: string, config: PanelConfig) { - this.name = name; - this.help = config.help || ''; - this.render = config.render; - - if (!config.render) { - throw new Error( - i18n.translate('timeline.panels.noRenderFunctionErrorMessage', { - defaultMessage: 'Panel must have a rendering function', - }) - ); - } - } -} diff --git a/src/plugins/timeline/public/panels/timechart/schema.ts b/src/plugins/timeline/public/panels/timechart/schema.ts deleted file mode 100644 index d18c5337440d..000000000000 --- a/src/plugins/timeline/public/panels/timechart/schema.ts +++ /dev/null @@ -1,414 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 _ from 'lodash'; -import $ from 'jquery'; -import moment from 'moment-timezone'; -// @ts-ignore -import observeResize from '../../lib/observe_resize'; -import { - calculateInterval, - DEFAULT_TIME_FORMAT, - tickFormatters, - xaxisFormatterProvider, - generateTicksProvider, -} from '../../../../vis_type_timeline/public'; -import { TimelineVisualizationDependencies } from '../../application'; - -const DEBOUNCE_DELAY = 50; - -export function timechartFn(dependencies: TimelineVisualizationDependencies) { - const { - $rootScope, - $compile, - uiSettings, - data: { - query: { timefilter }, - }, - } = dependencies; - - return function () { - return { - help: 'Draw a timeseries chart', - render($scope: any, $elem: any) { - const template = '<div class="chart-top-title"></div><div class="chart-canvas"></div>'; - const formatters = tickFormatters() as any; - const getxAxisFormatter = xaxisFormatterProvider(uiSettings); - const generateTicks = generateTicksProvider(); - - // TODO: I wonder if we should supply our own moment that sets this every time? - // could just use angular's injection to provide a moment service? - moment.tz.setDefault(uiSettings.get('dateFormat:tz')); - - const render = $scope.seriesList.render || {}; - - $scope.chart = $scope.seriesList.list; - $scope.interval = $scope.interval; - $scope.search = $scope.search || _.noop; - - let legendValueNumbers: any; - let legendCaption: any; - const debouncedSetLegendNumbers = _.debounce(setLegendNumbers, DEBOUNCE_DELAY, { - maxWait: DEBOUNCE_DELAY, - leading: true, - trailing: false, - }); - // ensure legend is the same height with or without a caption so legend items do not move around - const emptyCaption = '<br>'; - - const defaultOptions = { - xaxis: { - mode: 'time', - tickLength: 5, - timezone: 'browser', - }, - selection: { - mode: 'x', - color: '#ccc', - }, - crosshair: { - mode: 'x', - color: '#C66', - lineWidth: 2, - }, - grid: { - show: render.grid, - borderWidth: 0, - borderColor: null, - margin: 10, - hoverable: true, - autoHighlight: false, - }, - legend: { - backgroundColor: 'rgb(255,255,255,0)', - position: 'nw', - labelBoxBorderColor: 'rgb(255,255,255,0)', - labelFormatter(label: any, series: any) { - const wrapperSpan = document.createElement('span'); - const labelSpan = document.createElement('span'); - const numberSpan = document.createElement('span'); - - wrapperSpan.setAttribute('class', 'ngLegendValue'); - wrapperSpan.setAttribute('osd-accessible-click', ''); - wrapperSpan.setAttribute('ng-click', `toggleSeries(${series._id})`); - wrapperSpan.setAttribute('ng-focus', `focusSeries(${series._id})`); - wrapperSpan.setAttribute('ng-mouseover', `highlightSeries(${series._id})`); - - labelSpan.setAttribute('ng-non-bindable', ''); - labelSpan.appendChild(document.createTextNode(label)); - numberSpan.setAttribute('class', 'ngLegendValueNumber'); - - wrapperSpan.appendChild(labelSpan); - wrapperSpan.appendChild(numberSpan); - - return wrapperSpan.outerHTML; - }, - }, - colors: [ - '#01A4A4', - '#C66', - '#D0D102', - '#616161', - '#00A1CB', - '#32742C', - '#F18D05', - '#113F8C', - '#61AE24', - '#D70060', - ], - }; - - const originalColorMap = new Map(); - $scope.chart.forEach((series: any, seriesIndex: any) => { - if (!series.color) { - const colorIndex = seriesIndex % defaultOptions.colors.length; - series.color = defaultOptions.colors[colorIndex]; - } - originalColorMap.set(series, series.color); - }); - - let highlightedSeries: any; - let focusedSeries: any; - function unhighlightSeries() { - if (highlightedSeries === null) { - return; - } - - highlightedSeries = null; - focusedSeries = null; - $scope.chart.forEach((series: any) => { - series.color = originalColorMap.get(series); // reset the colors - }); - drawPlot($scope.chart); - } - $scope.highlightSeries = _.debounce(function (id: any) { - if (highlightedSeries === id) { - return; - } - - highlightedSeries = id; - $scope.chart.forEach((series: any, seriesIndex: any) => { - if (seriesIndex !== id) { - series.color = 'rgba(128,128,128,0.1)'; // mark as grey - } else { - series.color = originalColorMap.get(series); // color it like it was - } - }); - drawPlot($scope.chart); - }, DEBOUNCE_DELAY); - $scope.focusSeries = function (id: any) { - focusedSeries = id; - $scope.highlightSeries(id); - }; - - $scope.toggleSeries = function (id: any) { - const series = $scope.chart[id]; - series._hide = !series._hide; - drawPlot($scope.chart); - }; - - const cancelResize = observeResize($elem, function () { - drawPlot($scope.chart); - }); - - $scope.$on('$destroy', function () { - cancelResize(); - $elem.off('plothover'); - $elem.off('plotselected'); - $elem.off('mouseleave'); - }); - - $elem.on('plothover', function (event: any, pos: any, item: any) { - $rootScope.$broadcast('timelinePlotHover', event, pos, item); - }); - - $elem.on('plotselected', function (event: any, ranges: any) { - timefilter.timefilter.setTime({ - from: moment(ranges.xaxis.from), - to: moment(ranges.xaxis.to), - }); - }); - - $elem.on('mouseleave', function () { - $rootScope.$broadcast('timelinePlotLeave'); - }); - - $scope.$on('timelinePlotHover', function (angularEvent: any, flotEvent: any, pos: any) { - if (!$scope.plot) return; - $scope.plot.setCrosshair(pos); - debouncedSetLegendNumbers(pos); - }); - - $scope.$on('timelinePlotLeave', function () { - if (!$scope.plot) return; - $scope.plot.clearCrosshair(); - clearLegendNumbers(); - }); - - // Shamelessly borrowed from the flotCrosshairs example - function setLegendNumbers(pos: any) { - unhighlightSeries(); - - const plot = $scope.plot; - - const axes = plot.getAxes(); - if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max) { - return; - } - - let i; - const dataset = plot.getData(); - if (legendCaption) { - legendCaption.text( - moment(pos.x).format( - _.get(dataset, '[0]._global.legend.timeFormat', DEFAULT_TIME_FORMAT) - ) - ); - } - for (i = 0; i < dataset.length; ++i) { - const series = dataset[i]; - const useNearestPoint = series.lines.show && !series.lines.steps; - const precision = _.get(series, '_meta.precision', 2); - - if (series._hide) continue; - - const currentPoint = series.data.find((point: any, index: number) => { - if (index + 1 === series.data.length) { - return true; - } - if (useNearestPoint) { - return pos.x - point[0] < series.data[index + 1][0] - pos.x; - } else { - return pos.x < series.data[index + 1][0]; - } - }); - - const y = currentPoint[1]; - - if (y != null) { - let label = y.toFixed(precision); - if (series.yaxis.tickFormatter) { - label = series.yaxis.tickFormatter(label, series.yaxis); - } - legendValueNumbers.eq(i).text(`(${label})`); - } else { - legendValueNumbers.eq(i).empty(); - } - } - } - - function clearLegendNumbers() { - if (legendCaption) { - legendCaption.html(emptyCaption); - } - _.each(legendValueNumbers, function (num) { - $(num).empty(); - }); - } - - let legendScope = $scope.$new(); - function drawPlot(plotConfig: any) { - if (!$('.chart-canvas', $elem).length) $elem.html(template); - const canvasElem = $('.chart-canvas', $elem); - - // we can't use `$.plot` to draw the chart when the height or width is 0 - // so, we'll need another event to trigger drawPlot to actually draw it - if (canvasElem.height() === 0 || canvasElem.width() === 0) { - return; - } - - const title = _(plotConfig).map('_title').compact().last() as any; - $('.chart-top-title', $elem).text(title == null ? '' : title); - - const options = _.cloneDeep(defaultOptions) as any; - - // Get the X-axis tick format - const time = timefilter.timefilter.getBounds() as any; - const interval = calculateInterval( - time.min.valueOf(), - time.max.valueOf(), - uiSettings.get('timeline:target_buckets') || 200, - $scope.interval, - uiSettings.get('timeline:min_interval') || '1ms' - ); - const format = getxAxisFormatter(interval); - - // Use moment to format ticks so we get timezone correction - options.xaxis.tickFormatter = function (val: any) { - return moment(val).format(format); - }; - - // Calculate how many ticks can fit on the axis - const tickLetterWidth = 7; - const tickPadding = 45; - options.xaxis.ticks = Math.floor( - $elem.width() / (format.length * tickLetterWidth + tickPadding) - ); - - const series = _.map(plotConfig, function (serie: any, index) { - serie = _.cloneDeep( - _.defaults(serie, { - shadowSize: 0, - lines: { - lineWidth: 3, - }, - }) - ); - serie._id = index; - - if (serie.color) { - const span = document.createElement('span'); - span.style.color = serie.color; - serie.color = span.style.color; - } - - if (serie._hide) { - serie.data = []; - serie.stack = false; - // serie.color = "#ddd"; - serie.label = '(hidden) ' + serie.label; - } - - if (serie._global) { - _.mergeWith(options, serie._global, function (objVal, srcVal) { - // This is kind of gross, it means that you can't replace a global value with a null - // best you can do is an empty string. Deal with it. - if (objVal == null) return srcVal; - if (srcVal == null) return objVal; - }); - } - - return serie; - }); - - if (options.yaxes) { - options.yaxes.forEach((yaxis: any) => { - if (yaxis && yaxis.units) { - yaxis.tickFormatter = formatters[yaxis.units.type]; - const byteModes = ['bytes', 'bytes/s']; - if (byteModes.includes(yaxis.units.type)) { - yaxis.tickGenerator = generateTicks; - } - } - }); - } - - // @ts-ignore - $scope.plot = $.plot(canvasElem, _.compact(series), options); - - if ($scope.plot) { - $scope.$emit('timelineChartRendered'); - } - - legendScope.$destroy(); - legendScope = $scope.$new(); - // Used to toggle the series, and for displaying values on hover - legendValueNumbers = canvasElem.find('.ngLegendValueNumber'); - _.each(canvasElem.find('.ngLegendValue'), function (elem) { - $compile(elem)(legendScope); - }); - - if (_.get($scope.plot.getData(), '[0]._global.legend.showTime', true)) { - legendCaption = $('<caption class="timChart__legendCaption"></caption>'); - legendCaption.html(emptyCaption); - canvasElem.find('div.legend table').append(legendCaption); - - // legend has been re-created. Apply focus on legend element when previously set - if (focusedSeries || focusedSeries === 0) { - const $legendLabels = canvasElem.find('div.legend table .legendLabel>span'); - $legendLabels.get(focusedSeries).focus(); - } - } - } - $scope.$watch('chart', drawPlot); - }, - }; - }; -} diff --git a/src/plugins/timeline/public/panels/timechart/timechart.ts b/src/plugins/timeline/public/panels/timechart/timechart.ts deleted file mode 100644 index 176d22e1016e..000000000000 --- a/src/plugins/timeline/public/panels/timechart/timechart.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { timechartFn } from './schema'; -import { Panel } from '../panel'; -import { TimelineVisualizationDependencies } from '../../application'; - -export function getTimeChart(dependencies: TimelineVisualizationDependencies) { - // Schema is broken out so that it may be extended for use in other plugins - // Its also easier to test. - return new Panel('timechart', timechartFn(dependencies)()); -} diff --git a/src/plugins/timeline/public/partials/load_sheet.html b/src/plugins/timeline/public/partials/load_sheet.html deleted file mode 100644 index 5e2c91e7627f..000000000000 --- a/src/plugins/timeline/public/partials/load_sheet.html +++ /dev/null @@ -1,12 +0,0 @@ -<form role="form" ng-submit="fetch()"> - <h2 - class="kuiLocalDropdownTitle" - i18n-id="timeline.topNavMenu.openSheetTitle" - i18n-default-message="Open Sheet" - ></h2> - - <saved-object-finder - type="timelion-sheet" - use-local-management="true" - ></saved-object-finder> -</form> diff --git a/src/plugins/timeline/public/partials/save_sheet.html b/src/plugins/timeline/public/partials/save_sheet.html deleted file mode 100644 index f2a5035a9c53..000000000000 --- a/src/plugins/timeline/public/partials/save_sheet.html +++ /dev/null @@ -1,106 +0,0 @@ -<div class="list-group"> - <button class="list-group-item" ng-click="section = 'sheet'" type="button" data-test-subj="timelineSaveAsSheetButton"> - <h4 - class="list-group-item-heading" - i18n-id="timeline.topNavMenu.save.saveEntireSheetTitle" - i18n-default-message="Save entire Timeline sheet" - ></h4> - <p - class="list-group-item-text" - i18n-id="timeline.topNavMenu.save.saveEntireSheetDescription" - i18n-default-message="You want this option if you mostly use Timeline expressions from within - the Timeline app and don't need to add Timeline charts to OpenSearch Dashboards - dashboards. You may also want this if you make use of references to - other panels." - ></p> - </button> - - <div class="list-group-item" ng-show="section == 'sheet'"> - <form role="form" class="container-fluid" ng-submit="opts.saveSheet()"> - <label - for="savedSheet" - class="kuiLabel kuiVerticalRhythmSmall" - i18n-id="timeline.topNavMenu.save.saveEntireSheetLabel" - i18n-default-message="Save sheet as" - ></label> - - <input - id="savedSheet" - ng-model="opts.savedSheet.title" - input-focus="select" - class="form-control kuiVerticalRhythmSmall" - placeholder="{{ ::'timeline.topNavMenu.save.saveEntireSheet.inputPlaceholder' | i18n: { defaultMessage: 'Name this sheet...' } }}" - aria-label="{{ ::'timeline.topNavMenu.save.saveEntireSheet.inputAriaLabel' | i18n: { defaultMessage: 'Name' } }}" - > - - <saved-object-save-as-check-box - class="kuiVerticalRhythmSmall" - saved-object="opts.savedSheet" - ></saved-object-save-as-check-box> - - <button - ng-disabled="!opts.savedSheet.title" - type="submit" - class="kuiButton kuiButton--primary kuiVerticalRhythmSmall" - i18n-id="timeline.topNavMenu.save.saveEntireSheet.submitButtonLabel" - i18n-default-message="Save" - data-test-subj="timelineFinishSaveButton" - ></button> - </form> - </div> - - <button class="list-group-item" ng-click="section = 'expression'" type="button"> - <h4 - class="list-group-item-heading" - i18n-id="timeline.topNavMenu.save.saveAsDashboardPanelTitle" - i18n-default-message="Save current expression as OpenSearch Dashboards dashboard panel" - ></h4> - <p - class="list-group-item-text" - i18n-id="timeline.topNavMenu.save.saveAsDashboardPanelDescription" - i18n-default-message="Need to add a chart to a OpenSearch Dashboards dashboard? We can do that! This option - will save your currently selected expression as a panel that can be - added to OpenSearch Dashboards dashboards as you would add anything else. Note, if you - use references to other panels you will need to remove the refences by - copying the referenced expression directly into the expression you are - saving. Click a chart to select a different expression to save." - ></p> - </button> - - <div class="list-group-item" ng-show="section == 'expression'"> - <form role="form" class="container-fluid" ng-submit="opts.saveExpression(panelTitle)"> - <div class="form-group"> - <label - class="control-label" - i18n-id="timeline.topNavMenu.save.saveAsDashboardPanel.selectedExpressionLabel" - i18n-default-message="Currently selected expression" - ></label> - <code>{{opts.state.sheet[opts.state.selected]}}</code> - </div> - <div class="form-group"> - <label - for="savedExpression" - class="control-label" - i18n-id="timeline.topNavMenu.save.saveAsDashboardPanelLabel" - i18n-default-message="Save expression as" - ></label> - <input - id="savedExpression" - ng-model="panelTitle" - input-focus="select" - class="form-control" - placeholder="{{ ::'timeline.topNavMenu.save.saveAsDashboardPanel.inputPlaceholder' | i18n: { defaultMessage: 'Name this panel' } }}" - > - </div> - <div class="form-group"> - <button - ng-disabled="!panelTitle" - type="submit" - class="kuiButton kuiButton--primary" - i18n-id="timeline.topNavMenu.save.saveAsDashboardPanel.submitButtonLabel" - i18n-default-message="Save" - ></button> - </div> - </form> - </div> -</div> diff --git a/src/plugins/timeline/public/partials/sheet_options.html b/src/plugins/timeline/public/partials/sheet_options.html deleted file mode 100644 index 83f0a122a987..000000000000 --- a/src/plugins/timeline/public/partials/sheet_options.html +++ /dev/null @@ -1,36 +0,0 @@ -<form role="form"> - <h2 - class="kuiLocalDropdownTitle" - i18n-id="timeline.topNavMenu.sheetOptionsTitle" - i18n-default-message="Sheet options" - ></h2> - - <div class="clearfix"> - <div class="form-group col-md-6"> - <label - for="timelineColCount" - i18n-id="timeline.topNavMenu.options.columnsCountLabel" - i18n-default-message="Columns (Column count must divide evenly into 12)" - ></label> - <select class="form-control" - id="timelineColCount" - ng-change="opts.search()" - ng-options="column for column in [1, 2, 3, 4, 6, 12]" - ng-model="opts.state.columns"> - </select> - </div> - <div class="form-group col-md-6"> - <label - for="timelineRowCount" - i18n-id="timeline.topNavMenu.options.rowsCountLabel" - i18n-default-message="Rows (This is a target based on the current window height)" - ></label> - <select class="form-control" - id="timelineRowCount" - ng-change="opts.search()" - ng-options="row for row in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" - ng-model="opts.state.rows"> - </select> - </div> - </div> -</form> diff --git a/src/plugins/timeline/public/plugin.ts b/src/plugins/timeline/public/plugin.ts deleted file mode 100644 index b6141d2c23e2..000000000000 --- a/src/plugins/timeline/public/plugin.ts +++ /dev/null @@ -1,162 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { BehaviorSubject } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { - CoreSetup, - CoreStart, - Plugin, - PluginInitializerContext, - DEFAULT_APP_CATEGORIES, - AppMountParameters, - AppUpdater, - ScopedHistory, - AppNavLinkStatus, -} from '../../../core/public'; -import { Panel } from './panels/panel'; -import { - initAngularBootstrap, - OpenSearchDashboardsLegacyStart, -} from '../../opensearch_dashboards_legacy/public'; -import { createOsdUrlTracker } from '../../opensearch_dashboards_utils/public'; -import { DataPublicPluginStart, opensearchFilters, DataPublicPluginSetup } from '../../data/public'; -import { NavigationPublicPluginStart } from '../../navigation/public'; -import { VisualizationsStart } from '../../visualizations/public'; -import { - VisTypeTimelinePluginStart, - VisTypeTimelinePluginSetup, -} from '../../vis_type_timeline/public'; - -export interface TimelinePluginDependencies { - data: DataPublicPluginStart; - navigation: NavigationPublicPluginStart; - visualizations: VisualizationsStart; - visTypeTimeline: VisTypeTimelinePluginStart; -} - -/** @internal */ -export class TimelinePlugin implements Plugin<void, void> { - initializerContext: PluginInitializerContext; - private appStateUpdater = new BehaviorSubject<AppUpdater>(() => ({})); - private stopUrlTracking: (() => void) | undefined = undefined; - private currentHistory: ScopedHistory | undefined = undefined; - - constructor(initializerContext: PluginInitializerContext) { - this.initializerContext = initializerContext; - } - - public setup( - core: CoreSetup, - { - data, - visTypeTimeline, - }: { data: DataPublicPluginSetup; visTypeTimeline: VisTypeTimelinePluginSetup } - ) { - const timelinePanels: Map<string, Panel> = new Map(); - - const { appMounted, appUnMounted, stop: stopUrlTracker } = createOsdUrlTracker({ - baseUrl: core.http.basePath.prepend('/app/timeline'), - defaultSubUrl: '#/', - storageKey: `lastUrl:${core.http.basePath.get()}:timeline`, - navLinkUpdater$: this.appStateUpdater, - toastNotifications: core.notifications.toasts, - stateParams: [ - { - osdUrlKey: '_g', - stateUpdate$: data.query.state$.pipe( - filter( - ({ changes }) => !!(changes.globalFilters || changes.time || changes.refreshInterval) - ), - map(({ state }) => ({ - ...state, - filters: state.filters?.filter(opensearchFilters.isFilterPinned), - })) - ), - }, - ], - getHistory: () => this.currentHistory!, - }); - - this.stopUrlTracking = () => { - stopUrlTracker(); - }; - - initAngularBootstrap(); - core.application.register({ - id: 'timelion', - title: 'Timeline', - order: 8000, - defaultPath: '#/', - euiIconType: 'inputOutput', - category: DEFAULT_APP_CATEGORIES.opensearchDashboards, - navLinkStatus: - visTypeTimeline.isUiEnabled === false ? AppNavLinkStatus.hidden : AppNavLinkStatus.default, - mount: async (params: AppMountParameters) => { - const [coreStart, pluginsStart] = await core.getStartServices(); - this.currentHistory = params.history; - - appMounted(); - - const unlistenParentHistory = params.history.listen(() => { - window.dispatchEvent(new HashChangeEvent('hashchange')); - }); - - const { renderApp } = await import('./application'); - params.element.classList.add('timelineAppContainer'); - const unmount = renderApp({ - mountParams: params, - pluginInitializerContext: this.initializerContext, - timelinePanels, - core: coreStart, - plugins: pluginsStart as TimelinePluginDependencies, - }); - return () => { - unlistenParentHistory(); - unmount(); - appUnMounted(); - }; - }, - }); - } - - public start( - core: CoreStart, - { opensearchDashboardsLegacy }: { opensearchDashboardsLegacy: OpenSearchDashboardsLegacyStart } - ) { - opensearchDashboardsLegacy.loadFontAwesome(); - } - - public stop(): void { - if (this.stopUrlTracking) { - this.stopUrlTracking(); - } - } -} diff --git a/src/plugins/timeline/public/services/_saved_sheet.ts b/src/plugins/timeline/public/services/_saved_sheet.ts deleted file mode 100644 index 06353b63388d..000000000000 --- a/src/plugins/timeline/public/services/_saved_sheet.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { IUiSettingsClient } from 'opensearch-dashboards/public'; -import { - createSavedObjectClass, - SavedObjectOpenSearchDashboardsServices, -} from '../../../saved_objects/public'; - -// Used only by the savedSheets service, usually no reason to change this -export function createSavedSheetClass( - services: SavedObjectOpenSearchDashboardsServices, - config: IUiSettingsClient -) { - const SavedObjectClass = createSavedObjectClass(services); - - class SavedSheet extends SavedObjectClass { - static type = 'timelion-sheet'; - - // if type:sheet has no mapping, we push this mapping into OpenSearch - static mapping = { - title: 'text', - hits: 'integer', - description: 'text', - timelion_sheet: 'text', - timelion_interval: 'keyword', - timelion_other_interval: 'keyword', - timelion_chart_height: 'integer', - timelion_columns: 'integer', - timelion_rows: 'integer', - version: 'integer', - }; - - // Order these fields to the top, the rest are alphabetical - static fieldOrder = ['title', 'description']; - // SavedSheet constructor. Usually you'd interact with an instance of this. - // ID is option, without it one will be generated on save. - constructor(id: string) { - super({ - type: SavedSheet.type, - mapping: SavedSheet.mapping, - - // if this is null/undefined then the SavedObject will be assigned the defaults - id, - - // default values that will get assigned if the doc is new - defaults: { - title: 'New Timeline Sheet', - hits: 0, - description: '', - timelion_sheet: ['.opensearch(*)'], - timelion_interval: 'auto', - timelion_chart_height: 275, - timelion_columns: config.get('timeline:default_columns') || 2, - timelion_rows: config.get('timeline:default_rows') || 2, - version: 1, - }, - }); - this.showInRecentlyAccessed = true; - this.getFullPath = () => `/app/timeline#/${this.id}`; - } - } - - return SavedSheet; -} diff --git a/src/plugins/timeline/public/services/saved_sheets.ts b/src/plugins/timeline/public/services/saved_sheets.ts deleted file mode 100644 index 4da4d7b9ed94..000000000000 --- a/src/plugins/timeline/public/services/saved_sheets.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { SavedObjectLoader } from '../../../saved_objects/public'; -import { createSavedSheetClass } from './_saved_sheet'; -import { RenderDeps } from '../application'; - -export function initSavedSheetService(app: angular.IModule, deps: RenderDeps) { - const savedObjectsClient = deps.core.savedObjects.client; - const services = { - savedObjectsClient, - indexPatterns: deps.plugins.data.indexPatterns, - search: deps.plugins.data.search, - chrome: deps.core.chrome, - overlays: deps.core.overlays, - }; - - const SavedSheet = createSavedSheetClass(services, deps.core.uiSettings); - - const savedSheetLoader = new SavedObjectLoader(SavedSheet, savedObjectsClient); - savedSheetLoader.urlFor = (id) => `#/${encodeURIComponent(id)}`; - // Customize loader properties since adding an 's' on type doesn't work for type 'timelion-sheet'. - savedSheetLoader.loaderProperties = { - name: 'timelion-sheet', - noun: 'Saved Sheets', - nouns: 'saved sheets', - }; - // This is the only thing that gets injected into controllers - app.service('savedSheets', function () { - return savedSheetLoader; - }); - - return savedSheetLoader; -} diff --git a/src/plugins/timeline/public/timeline_app_state.ts b/src/plugins/timeline/public/timeline_app_state.ts deleted file mode 100644 index 62487c2eba03..000000000000 --- a/src/plugins/timeline/public/timeline_app_state.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { - createStateContainer, - syncState, - IOsdUrlStateStorage, -} from '../../opensearch_dashboards_utils/public'; - -import { TimelineAppState, TimelineAppStateTransitions } from './types'; - -const STATE_STORAGE_KEY = '_a'; - -interface Arguments { - osdUrlStateStorage: IOsdUrlStateStorage; - stateDefaults: TimelineAppState; -} - -export function initTimelineAppState({ stateDefaults, osdUrlStateStorage }: Arguments) { - const urlState = osdUrlStateStorage.get<TimelineAppState>(STATE_STORAGE_KEY); - const initialState = { - ...stateDefaults, - ...urlState, - }; - - /* - make sure url ('_a') matches initial state - Initializing appState does two things - first it translates the defaults into AppState, - second it updates appState based on the url (the url trumps the defaults). This means if - we update the state format at all and want to handle BWC, we must not only migrate the - data stored with saved vis, but also any old state in the url. - */ - osdUrlStateStorage.set(STATE_STORAGE_KEY, initialState, { replace: true }); - - const stateContainer = createStateContainer<TimelineAppState, TimelineAppStateTransitions>( - initialState, - { - set: (state) => (prop, value) => ({ ...state, [prop]: value }), - updateState: (state) => (newValues) => ({ ...state, ...newValues }), - } - ); - - const { start: startStateSync, stop: stopStateSync } = syncState({ - storageKey: STATE_STORAGE_KEY, - stateContainer: { - ...stateContainer, - set: (state) => { - if (state) { - // syncState utils requires to handle incoming "null" value - stateContainer.set(state); - } - }, - }, - stateStorage: osdUrlStateStorage, - }); - - // start syncing the appState with the ('_a') url - startStateSync(); - - return { stateContainer, stopStateSync }; -} diff --git a/src/plugins/timeline/public/types.ts b/src/plugins/timeline/public/types.ts deleted file mode 100644 index c94fef3cff51..000000000000 --- a/src/plugins/timeline/public/types.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -export interface TimelineAppState { - sheet: string[]; - selected: number; - columns: number; - rows: number; - interval: string; -} - -export interface TimelineAppStateTransitions { - set: ( - state: TimelineAppState - ) => <T extends keyof TimelineAppState>(prop: T, value: TimelineAppState[T]) => TimelineAppState; - updateState: ( - state: TimelineAppState - ) => <T extends keyof TimelineAppState>(newValues: Partial<TimelineAppState>) => TimelineAppState; -} diff --git a/src/plugins/timeline/server/config.ts b/src/plugins/timeline/server/config.ts deleted file mode 100644 index b98585aaa695..000000000000 --- a/src/plugins/timeline/server/config.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { schema, TypeOf } from '@osd/config-schema'; - -export const configSchema = { - schema: schema.object({ - graphiteUrls: schema.maybe(schema.arrayOf(schema.string())), - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - }), - }), -}; - -export type TimelineConfigType = TypeOf<typeof configSchema.schema>; diff --git a/src/plugins/timeline/server/index.ts b/src/plugins/timeline/server/index.ts deleted file mode 100644 index 9b26a79d3a11..000000000000 --- a/src/plugins/timeline/server/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; -import { TimelinePlugin } from './plugin'; -import { configSchema, TimelineConfigType } from './config'; - -export const config: PluginConfigDescriptor<TimelineConfigType> = { - schema: configSchema.schema, -}; - -export const plugin = (context: PluginInitializerContext<TimelineConfigType>) => - new TimelinePlugin(context); diff --git a/src/plugins/timeline/server/plugin.ts b/src/plugins/timeline/server/plugin.ts deleted file mode 100644 index 2aa14e7dab57..000000000000 --- a/src/plugins/timeline/server/plugin.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { CoreSetup, CoreStart, Plugin, PluginInitializerContext, Logger } from 'src/core/server'; -import { i18n } from '@osd/i18n'; -import { schema } from '@osd/config-schema'; -import { TimelineConfigType } from './config'; -import { timelineSheetSavedObjectType } from './saved_objects'; - -/** - * Deprecated since 7.0, the Timeline app will be removed in 8.0. - * To continue using your Timeline worksheets, migrate them to a dashboard. - **/ -const showWarningMessageIfTimelineSheetWasFound = (core: CoreStart, logger: Logger) => { - const { savedObjects } = core; - const savedObjectsClient = savedObjects.createInternalRepository(); - - savedObjectsClient - .find({ - type: 'timelion-sheet', - perPage: 1, - }) - .then( - ({ total }) => - total && - logger.warn( - 'Deprecated since 7.0, the Timeline app will be removed in 8.0. To continue using your Timeline worksheets, migrate them to a dashboard.' - ) - ); -}; - -export class TimelinePlugin implements Plugin { - private logger: Logger; - - constructor(context: PluginInitializerContext<TimelineConfigType>) { - this.logger = context.logger.get(); - } - - public setup(core: CoreSetup) { - core.capabilities.registerProvider(() => ({ - timelion: { - save: true, - }, - })); - core.savedObjects.registerType(timelineSheetSavedObjectType); - - core.uiSettings.register({ - 'timeline:showTutorial': { - name: i18n.translate('timeline.uiSettings.showTutorialLabel', { - defaultMessage: 'Show tutorial', - }), - value: false, - description: i18n.translate('timeline.uiSettings.showTutorialDescription', { - defaultMessage: 'Should I show the tutorial by default when entering the timeline app?', - }), - category: ['timeline'], - schema: schema.boolean(), - }, - 'timeline:default_columns': { - name: i18n.translate('timeline.uiSettings.defaultColumnsLabel', { - defaultMessage: 'Default columns', - }), - value: 2, - description: i18n.translate('timeline.uiSettings.defaultColumnsDescription', { - defaultMessage: 'Number of columns on a timeline sheet by default', - }), - category: ['timeline'], - schema: schema.number(), - }, - 'timeline:default_rows': { - name: i18n.translate('timeline.uiSettings.defaultRowsLabel', { - defaultMessage: 'Default rows', - }), - value: 2, - description: i18n.translate('timeline.uiSettings.defaultRowsDescription', { - defaultMessage: 'Number of rows on a timeline sheet by default', - }), - category: ['timeline'], - schema: schema.number(), - }, - }); - } - start(core: CoreStart) { - showWarningMessageIfTimelineSheetWasFound(core, this.logger); - } - stop() {} -} diff --git a/src/plugins/timeline/server/saved_objects/index.ts b/src/plugins/timeline/server/saved_objects/index.ts deleted file mode 100644 index f0546d981aff..000000000000 --- a/src/plugins/timeline/server/saved_objects/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -export { timelineSheetSavedObjectType } from './timeline_sheet'; diff --git a/src/plugins/timeline/server/saved_objects/timeline_sheet.ts b/src/plugins/timeline/server/saved_objects/timeline_sheet.ts deleted file mode 100644 index 878a4d542db5..000000000000 --- a/src/plugins/timeline/server/saved_objects/timeline_sheet.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { SavedObjectsType } from 'opensearch-dashboards/server'; - -export const timelineSheetSavedObjectType: SavedObjectsType = { - name: 'timelion-sheet', - hidden: false, - namespaceType: 'single', - mappings: { - properties: { - description: { type: 'text' }, - hits: { type: 'integer' }, - kibanaSavedObjectMeta: { - properties: { - searchSourceJSON: { type: 'text' }, - }, - }, - timelion_chart_height: { type: 'integer' }, - timelion_columns: { type: 'integer' }, - timelion_interval: { type: 'keyword' }, - timelion_other_interval: { type: 'keyword' }, - timelion_rows: { type: 'integer' }, - timelion_sheet: { type: 'text' }, - title: { type: 'text' }, - version: { type: 'integer' }, - }, - }, -}; diff --git a/src/plugins/ui_actions/public/actions/action.ts b/src/plugins/ui_actions/public/actions/action.ts index b38e2bd4e40f..76fc2ddb3bed 100644 --- a/src/plugins/ui_actions/public/actions/action.ts +++ b/src/plugins/ui_actions/public/actions/action.ts @@ -92,7 +92,7 @@ export interface Action<Context extends BaseContext = {}, T = ActionType> * Returns a title to be displayed to the user. * @param context */ - getDisplayName(context: ActionExecutionContext<Context>): string; + getDisplayName(context: ActionExecutionContext<Context>): JSX.Element | string; /** * `UiComponent` to render when displaying this action as a context menu item. diff --git a/src/plugins/ui_actions/public/actions/action_internal.ts b/src/plugins/ui_actions/public/actions/action_internal.ts index fb39a01ed698..5610051dc3f7 100644 --- a/src/plugins/ui_actions/public/actions/action_internal.ts +++ b/src/plugins/ui_actions/public/actions/action_internal.ts @@ -59,7 +59,7 @@ export class ActionInternal<A extends ActionDefinition = ActionDefinition> return this.definition.getIconType(context); } - public getDisplayName(context: Context<A>): string { + public getDisplayName(context: Context<A>): JSX.Element | string { if (!this.definition.getDisplayName) return `Action: ${this.id}`; return this.definition.getDisplayName(context); } diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts index 949595d60939..b9afca9fb99c 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts @@ -36,20 +36,28 @@ const createTestAction = ({ type, dispayName, order, + grouping, }: { type?: string; dispayName: string; order?: number; + grouping?: any[]; }) => createAction({ type: type as any, // mapping doesn't matter for this test getDisplayName: () => dispayName, order, execute: async () => {}, + grouping, }); const resultMapper = (panel: EuiContextMenuPanelDescriptor) => ({ - items: panel.items ? panel.items.map((item) => ({ name: item.name })) : [], + items: panel.items + ? panel.items.map((item) => ({ + ...(item.name ? { name: item.name } : {}), + ...(item.isSeparator ? { isSeparator: true } : {}), + })) + : [], }); test('sorts items in DESC order by "order" field first, then by display name', async () => { @@ -248,3 +256,195 @@ test('hides items behind in "More" submenu if there are more than 4 actions', as ] `); }); + +test('flattening of group with only one action', async () => { + const grouping1 = [ + { + id: 'test-group', + getDisplayName: () => 'Test group', + getIconType: () => 'bell', + }, + ]; + const actions = [ + createTestAction({ + dispayName: 'Foo 1', + }), + createTestAction({ + dispayName: 'Bar 1', + grouping: grouping1, + }), + ]; + const menu = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: 'TEST' as any })), + }); + + expect(menu.map(resultMapper)).toMatchInlineSnapshot(` + Array [ + Object { + "items": Array [ + Object { + "name": "Foo 1", + }, + Object { + "isSeparator": true, + }, + Object { + "name": "Bar 1", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Bar 1", + }, + ], + }, + ] + `); +}); + +test('grouping with only two actions', async () => { + const grouping1 = [ + { + id: 'test-group', + getDisplayName: () => 'Test group', + getIconType: () => 'bell', + }, + ]; + const actions = [ + createTestAction({ + dispayName: 'Foo 1', + }), + createTestAction({ + dispayName: 'Bar 1', + grouping: grouping1, + }), + createTestAction({ + dispayName: 'Bar 2', + grouping: grouping1, + }), + ]; + const menu = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: 'TEST' as any })), + }); + + expect(menu.map(resultMapper)).toMatchInlineSnapshot(` + Array [ + Object { + "items": Array [ + Object { + "name": "Foo 1", + }, + Object { + "isSeparator": true, + }, + Object { + "name": "Test group", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Bar 1", + }, + Object { + "name": "Bar 2", + }, + ], + }, + ] + `); +}); + +test('groups with deep nesting', async () => { + const grouping1 = [ + { + id: 'test-group', + getDisplayName: () => 'Test group', + getIconType: () => 'bell', + }, + ]; + const grouping2 = [ + { + id: 'test-group-2', + getDisplayName: () => 'Test group 2', + getIconType: () => 'bell', + }, + { + id: 'test-group-3', + getDisplayName: () => 'Test group 3', + getIconType: () => 'bell', + }, + ]; + + const actions = [ + createTestAction({ + dispayName: 'Foo 1', + }), + createTestAction({ + dispayName: 'Bar 1', + grouping: grouping1, + }), + createTestAction({ + dispayName: 'Bar 2', + grouping: grouping1, + }), + createTestAction({ + dispayName: 'Qux 1', + grouping: grouping2, + }), + ]; + const menu = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: 'TEST' as any })), + }); + + expect(menu.map(resultMapper)).toMatchInlineSnapshot(` + Array [ + Object { + "items": Array [ + Object { + "name": "Foo 1", + }, + Object { + "isSeparator": true, + }, + Object { + "name": "Test group", + }, + Object { + "isSeparator": true, + }, + Object { + "name": "Test group 3", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Bar 1", + }, + Object { + "name": "Bar 2", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Test group 3", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Qux 1", + }, + ], + }, + ] + `); +}); diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx index 054900f52b77..6d69be1f3faa 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx @@ -146,6 +146,7 @@ export async function buildContextMenuForActions({ closeMenu = () => {}, }: BuildContextMenuParams): Promise<EuiContextMenuPanelDescriptor[]> { const panels: Record<string, PanelDescriptor> = { + // This is the first panel which links out to all others via items property mainMenu: { id: 'mainMenu', title, @@ -157,35 +158,51 @@ export async function buildContextMenuForActions({ const context: ActionExecutionContext<object> = { ...item.context, trigger: item.trigger }; const isCompatible = await item.action.isCompatible(context); if (!isCompatible) return; - let parentPanel = ''; - let currentPanel = ''; + + // Reference to the last/parent/upper group. + // Groups are provided in order of parent to children. + let parentGroupId = ''; + if (action.grouping) { for (let i = 0; i < action.grouping.length; i++) { const group = action.grouping[i]; - currentPanel = group.id; - if (!panels[currentPanel]) { + const groupId = group.id; + + // If a panel does not exist for the current group, then create it + if (!panels[groupId]) { const name = group.getDisplayName ? group.getDisplayName(context) : group.id; - panels[currentPanel] = { - id: currentPanel, + + // Create panel for group + panels[groupId] = { + id: groupId, title: name, items: [], _level: i, _icon: group.getIconType ? group.getIconType(context) : 'empty', }; - if (parentPanel) { - panels[parentPanel].items!.push({ + + // If there are multiple groups and this is not the first group, + // then add an item to the parent group relating to this group + if (parentGroupId) { + panels[parentGroupId].items!.push({ name, - panel: currentPanel, + panel: groupId, icon: group.getIconType ? group.getIconType(context) : 'empty', _order: group.order || 0, _title: group.getDisplayName ? group.getDisplayName(context) : '', }); } } - parentPanel = currentPanel; + + // Save the current group, because it will be used as the parent group + // for adding items to it for any additional groups in the array + parentGroupId = groupId; } } - panels[parentPanel || 'mainMenu'].items!.push({ + + // Add a context menu item for this action so it shows up on a context menu panel. + // We add this within the parent group or default to the mainMenu panel. + panels[parentGroupId || 'mainMenu'].items!.push({ name: action.MenuItem ? React.createElement(uiToReactComponent(action.MenuItem), { context }) : action.getDisplayName(context), @@ -197,8 +214,10 @@ export async function buildContextMenuForActions({ _title: action.getDisplayName(context), }); }); + await Promise.all(promises); + // For each panel, sort items by order and title for (const panel of Object.values(panels)) { const items = panel.items.filter(Boolean) as ItemDescriptor[]; panel.items = _.sortBy( @@ -208,13 +227,23 @@ export async function buildContextMenuForActions({ ); } + // On the mainMenu, before adding in items for other groups, the first 4 items are shown. + // Any additional items are hidden behind a "more" item wrapMainPanelItemsIntoSubmenu(panels, 'mainMenu'); for (const panel of Object.values(panels)) { + // If the panel is a root-level panel, such as the parent of a group, + // then create mainMenu item for this panel if (panel._level === 0) { - // TODO: Add separator line here once it is available in EUI. - // See https://github.com/elastic/eui/pull/4018 - if (panel.items.length > 3) { + // Add separator with unique key if needed + if (panels.mainMenu.items.length) { + panels.mainMenu.items.push({ isSeparator: true, key: `${panel.id}separator` }); + } + + // If a panel has more than one child, then allow items to be grouped + // and link to it in the mainMenu. Otherwise, flatten the group. + // Note: this only happens on the root level panels, not for inner groups. + if (panel.items.length > 1) { panels.mainMenu.items.push({ name: panel.title || panel.id, icon: panel._icon || 'empty', diff --git a/src/plugins/vis_type_timeline/config.ts b/src/plugins/vis_type_timeline/config.ts index 2d566ed390bd..07c0df4953dc 100644 --- a/src/plugins/vis_type_timeline/config.ts +++ b/src/plugins/vis_type_timeline/config.ts @@ -30,15 +30,10 @@ import { schema, TypeOf } from '@osd/config-schema'; -export const configSchema = schema.object( - { - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ enabled: schema.boolean({ defaultValue: false }) }), - graphiteAllowedUrls: schema.maybe(schema.arrayOf(schema.string())), - graphiteBlockedIPs: schema.maybe(schema.arrayOf(schema.string())), - }, - // This option should be removed as soon as we entirely migrate config from legacy Timeline plugin. - { unknowns: 'allow' } -); +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + graphiteAllowedUrls: schema.maybe(schema.arrayOf(schema.string())), + graphiteBlockedIPs: schema.maybe(schema.arrayOf(schema.string())), +}); export type ConfigSchema = TypeOf<typeof configSchema>; diff --git a/src/plugins/vis_type_timeline/public/plugin.ts b/src/plugins/vis_type_timeline/public/plugin.ts index a27c9bc783ea..92d466da56a5 100644 --- a/src/plugins/vis_type_timeline/public/plugin.ts +++ b/src/plugins/vis_type_timeline/public/plugin.ts @@ -77,16 +77,11 @@ export interface VisTypeTimelinePluginStart { getArgValueSuggestions: typeof getArgValueSuggestions; } -/** @public */ -export interface VisTypeTimelinePluginSetup { - isUiEnabled: boolean; -} - /** @internal */ export class TimelineVisPlugin implements Plugin< - VisTypeTimelinePluginSetup, + void, VisTypeTimelinePluginStart, TimelineVisSetupDependencies, TimelineVisStartDependencies @@ -106,10 +101,6 @@ export class TimelineVisPlugin expressions.registerFunction(() => getTimelineVisualizationConfig(dependencies)); expressions.registerRenderer(getTimelineVisRenderer(dependencies)); visualizations.createBaseVisualization(getTimelineVisDefinition(dependencies)); - - return { - isUiEnabled: this.initializerContext.config.get().ui.enabled, - }; } public start(core: CoreStart, plugins: TimelineVisStartDependencies) { diff --git a/src/plugins/vis_type_timeline/server/index.ts b/src/plugins/vis_type_timeline/server/index.ts index 9be2c2083414..3e41475bb8bb 100644 --- a/src/plugins/vis_type_timeline/server/index.ts +++ b/src/plugins/vis_type_timeline/server/index.ts @@ -32,13 +32,8 @@ import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/ import { configSchema, ConfigSchema } from '../config'; import { Plugin } from './plugin'; -export { PluginSetupContract } from './plugin'; - export const config: PluginConfigDescriptor<ConfigSchema> = { schema: configSchema, - exposeToBrowser: { - ui: true, - }, deprecations: ({ renameFromRoot, renameFromRootWithoutMap }) => [ // timelion.enabled and timelion_vis.enabled deprecation renameFromRoot('timelion.enabled', 'vis_type_timeline.enabled'), diff --git a/src/plugins/vis_type_timeline/server/plugin.ts b/src/plugins/vis_type_timeline/server/plugin.ts index 88b33536bd99..d2c7097ac419 100644 --- a/src/plugins/vis_type_timeline/server/plugin.ts +++ b/src/plugins/vis_type_timeline/server/plugin.ts @@ -47,13 +47,6 @@ const experimentalLabel = i18n.translate('timeline.uiSettings.experimentalLabel' defaultMessage: 'experimental', }); -/** - * Describes public Timeline plugin contract returned at the `setup` stage. - */ -export interface PluginSetupContract { - uiEnabled: boolean; -} - export interface TimelinePluginStartDeps { data: PluginStart; } @@ -64,7 +57,7 @@ export interface TimelinePluginStartDeps { export class Plugin { constructor(private readonly initializerContext: PluginInitializerContext) {} - public async setup(core: CoreSetup): Promise<RecursiveReadonly<PluginSetupContract>> { + public async setup(core: CoreSetup): void { const config = await this.initializerContext.config .create<TypeOf<typeof configSchema>>() .pipe(first()) @@ -193,8 +186,6 @@ export class Plugin { schema: schema.string(), }, }); - - return deepFreeze({ uiEnabled: config.ui.enabled }); } public start() { diff --git a/src/plugins/vis_type_timeline/server/series_functions/label.js b/src/plugins/vis_type_timeline/server/series_functions/label.js index c935d537081f..4649ee6cf53f 100644 --- a/src/plugins/vis_type_timeline/server/series_functions/label.js +++ b/src/plugins/vis_type_timeline/server/series_functions/label.js @@ -62,10 +62,8 @@ export default new Chainable('label', { const config = args.byName; return alter(args, function (eachSeries) { if (config.regex) { - // not using a standard `import` so that if there's an issue with the re2 native module - // that it doesn't prevent OpenSearch Dashboards from starting up and we only have an issue using Timeline labels - const RE2 = require('re2'); - eachSeries.label = eachSeries.label.replace(new RE2(config.regex), config.label); + const regex = new RegExp(config.regex); + eachSeries.label = eachSeries.label.replace(regex, config.label); } else { eachSeries.label = config.label; } diff --git a/src/plugins/vis_type_timeline/server/series_functions/label.test.js b/src/plugins/vis_type_timeline/server/series_functions/label.test.js index c6dc832914a3..69268b385b07 100644 --- a/src/plugins/vis_type_timeline/server/series_functions/label.test.js +++ b/src/plugins/vis_type_timeline/server/series_functions/label.test.js @@ -51,4 +51,24 @@ describe('label.js', () => { expect(r.output.list[0].label).to.equal('beerative'); }); }); + + it('can use a regex to capture groups to modify series label', () => { + return invoke(fn, [seriesList, 'beer$2', '(N)(egative)']).then((r) => { + expect(r.output.list[0].label).to.equal('beeregative'); + }); + }); + + it('can handle different regex patterns', () => { + const seriesListCopy1 = JSON.parse(JSON.stringify(seriesList)); + const seriesListCopy2 = JSON.parse(JSON.stringify(seriesList)); + + return Promise.all([ + invoke(fn, [seriesListCopy1, 'beer$1 - $2', '(N)(egative)']).then((r) => { + expect(r.output.list[0].label).to.equal('beerN - egative'); + }), + invoke(fn, [seriesListCopy2, 'beer$1_$2', '(N)(eg.*)']).then((r) => { + expect(r.output.list[0].label).to.equal('beerN_egative'); + }), + ]); + }); }); diff --git a/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap b/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap index 973d539e4751..08a3b99be5ea 100644 --- a/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap +++ b/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`VegaVisualizations VegaVisualization - basics should show vega blank rectangle on top of a map (vegamap) 1`] = `"<div class=\\"vgaVis__view leaflet-container leaflet-grab leaflet-touch-drag\\" style=\\"height: 100%; position: relative;\\" tabindex=\\"0\\"><div class=\\"leaflet-pane leaflet-map-pane\\" style=\\"left: 0px; top: 0px;\\"><div class=\\"leaflet-pane leaflet-tile-pane\\"></div><div class=\\"leaflet-pane leaflet-shadow-pane\\"></div><div class=\\"leaflet-pane leaflet-overlay-pane\\"><div class=\\"leaflet-vega-container\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\" style=\\"left: 0px; top: 0px; cursor: default;\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"0\\" height=\\"0\\" viewBox=\\"0 0 0 0\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(0,0)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h0v0h0Z\\"></path><g><g class=\\"mark-rect role-mark\\" role=\\"graphics-symbol\\" aria-roledescription=\\"rect mark container\\"><path d=\\"M0,0h0v0h0Z\\" fill=\\"#0f0\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div></div><div class=\\"leaflet-pane leaflet-marker-pane\\"></div><div class=\\"leaflet-pane leaflet-tooltip-pane\\"></div><div class=\\"leaflet-pane leaflet-popup-pane\\"></div></div><div class=\\"leaflet-control-container\\"><div class=\\"leaflet-top leaflet-left\\"><div class=\\"leaflet-control-zoom leaflet-bar leaflet-control\\"><a class=\\"leaflet-control-zoom-in\\" href=\\"#\\" title=\\"Zoom in\\" role=\\"button\\" aria-label=\\"Zoom in\\">+</a><a class=\\"leaflet-control-zoom-out\\" href=\\"#\\" title=\\"Zoom out\\" role=\\"button\\" aria-label=\\"Zoom out\\">−</a></div></div><div class=\\"leaflet-top leaflet-right\\"></div><div class=\\"leaflet-bottom leaflet-left\\"></div><div class=\\"leaflet-bottom leaflet-right\\"><div class=\\"leaflet-control-attribution leaflet-control\\"></div></div></div></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vega blank rectangle on top of a map (vegamap) 1`] = `<div class="vgaVis__view leaflet-container leaflet-grab leaflet-touch-drag" style="height: 100%; position: relative;" tabindex="0"><div class="leaflet-pane leaflet-map-pane" style="left: 0px; top: 0px;"><div class="leaflet-pane leaflet-tile-pane"></div><div class="leaflet-pane leaflet-shadow-pane"></div><div class="leaflet-pane leaflet-overlay-pane"><div class="leaflet-vega-container" role="graphics-document" aria-roledescription="visualization" aria-label="Vega visualization" style="left: 0px; top: 0px; cursor: default;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="marks" width="0" height="0" viewBox="0 0 0 0" style="background-color: transparent;"><g fill="none" stroke-miterlimit="10" transform="translate(0,0)"><g class="mark-group role-frame root" role="graphics-object" aria-roledescription="group mark container"><g transform="translate(0,0)"><path class="background" aria-hidden="true" d="M0,0h0v0h0Z"></path><g><g class="mark-rect role-mark" role="graphics-symbol" aria-roledescription="rect mark container"><path d="M0,0h0v0h0Z" fill="#0f0"></path></g></g><path class="foreground" aria-hidden="true" d="" display="none"></path></g></g></g></svg></div></div><div class="leaflet-pane leaflet-marker-pane"></div><div class="leaflet-pane leaflet-tooltip-pane"></div><div class="leaflet-pane leaflet-popup-pane"></div></div><div class="leaflet-control-container"><div class="leaflet-top leaflet-left"><div class="leaflet-control-zoom leaflet-bar leaflet-control"><a class="leaflet-control-zoom-in" href="#" title="Zoom in" role="button" aria-label="Zoom in">+</a><a class="leaflet-control-zoom-out" href="#" title="Zoom out" role="button" aria-label="Zoom out">−</a></div></div><div class="leaflet-top leaflet-right"></div><div class="leaflet-bottom leaflet-left"></div><div class="leaflet-bottom leaflet-right"><div class="leaflet-control-attribution leaflet-control"></div></div></div></div><div class="vgaVis__controls vgaVis__controls--column"></div>`; -exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--err\\"><pre class=\\"vgaVis__messageCode\\">Cannot read property 'get' of undefined</pre></li></ul>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `<ul class="vgaVis__messages"><li class="vgaVis__message vgaVis__message--err"><pre class="vgaVis__messageCode">Cannot read properties of undefined (reading 'get')</pre></li></ul>`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--err\\"><pre class=\\"vgaVis__messageCode\\">Cannot read property 'get' of undefined</pre></li></ul>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `<ul class="vgaVis__messages"><li class="vgaVis__message vgaVis__message--err"><pre class="vgaVis__messageCode">Cannot read properties of undefined (reading 'get')</pre></li></ul>`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--err\\"><pre class=\\"vgaVis__messageCode\\">Cannot read property 'get' of undefined</pre></li></ul>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `<ul class="vgaVis__messages"><li class="vgaVis__message vgaVis__message--err"><pre class="vgaVis__messageCode">Cannot read properties of undefined (reading 'get')</pre></li></ul>`; diff --git a/src/plugins/vis_type_vega/public/vega_visualization.test.js b/src/plugins/vis_type_vega/public/vega_visualization.test.js index 2d4d648828b0..59ee90108502 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.test.js +++ b/src/plugins/vis_type_vega/public/vega_visualization.test.js @@ -56,6 +56,12 @@ jest.mock('./lib/vega', () => ({ vegaLite: jest.requireActual('vega-lite/src'), })); +// This is to remove the time-zone dependent `aria-label` key of "Dec 10/11, 2017" from the snapshot +expect.addSnapshotSerializer({ + serialize: (value) => value.replace(/key:\sDec 1[01], 2017;/g, 'key: <Date>;'), + test: (value) => typeof value === 'string', +}); + // FLAKY: https://github.com/elastic/kibana/issues/71713 describe('VegaVisualizations', () => { let domNode; diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts index a981ce578c09..570d61ec3b75 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts @@ -30,9 +30,20 @@ import { visualizationSavedObjectTypeMigrations } from './visualization_migrations'; import { SavedObjectMigrationContext, SavedObjectMigrationFn } from 'opensearch-dashboards/server'; +import { createReplaceSerializer } from '@osd/dev-utils'; const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; +// ToDo: Remove this logic when Node 14 support is removed +if (process.version.startsWith('v14.')) { + expect.addSnapshotSerializer( + createReplaceSerializer( + /Cannot read property '([^ ]+?)' of undefined/g, + "Cannot read properties of undefined (reading '$1')" + ) + ); +} + const testMigrateMatchAllQuery = (migrate: Function) => { it('should migrate obsolete match_all query', () => { const migratedDoc = migrate({ @@ -1003,7 +1014,7 @@ describe('migration visualization', () => { `); expect(logMsgArr).toMatchInlineSnapshot(` Array [ - "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", + "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read properties of undefined (reading 'gauge')", "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", ] `); diff --git a/tasks/function_test_groups.js b/tasks/function_test_groups.js index 91f017a437c3..d6df1573a661 100644 --- a/tasks/function_test_groups.js +++ b/tasks/function_test_groups.js @@ -33,10 +33,10 @@ import { resolve } from 'path'; import execa from 'execa'; import grunt from 'grunt'; -import { safeLoad } from 'js-yaml'; +import { load } from 'js-yaml'; const JOBS_YAML = readFileSync(resolve(__dirname, '../.ci/jobs.yml'), 'utf8'); -const TEST_TAGS = safeLoad(JOBS_YAML) +const TEST_TAGS = load(JOBS_YAML) .JOB.filter((id) => id.startsWith('opensearch-dashboards-ciGroup')) .map((id) => id.replace(/^opensearch-dashboards-/, '')); diff --git a/test/api_integration/fixtures/opensearch_archiver/index_patterns/basic_opensearch_dashboards/mappings.json b/test/api_integration/fixtures/opensearch_archiver/index_patterns/basic_opensearch_dashboards/mappings.json index c357464ef7ad..df28b3f143eb 100644 --- a/test/api_integration/fixtures/opensearch_archiver/index_patterns/basic_opensearch_dashboards/mappings.json +++ b/test/api_integration/fixtures/opensearch_archiver/index_patterns/basic_opensearch_dashboards/mappings.json @@ -146,47 +146,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "namespace": { "type": "keyword" }, diff --git a/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/relationships/mappings.json b/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/relationships/mappings.json index f12e6a07b7e1..0f850ee15352 100644 --- a/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/relationships/mappings.json +++ b/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/relationships/mappings.json @@ -179,47 +179,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -280,4 +239,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/search/mappings.json b/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/search/mappings.json index f12e6a07b7e1..0f850ee15352 100644 --- a/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/search/mappings.json +++ b/test/api_integration/fixtures/opensearch_archiver/management/saved_objects/search/mappings.json @@ -179,47 +179,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -280,4 +239,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/api_integration/fixtures/opensearch_archiver/saved_objects/10k/mappings.json b/test/api_integration/fixtures/opensearch_archiver/saved_objects/10k/mappings.json index c357464ef7ad..df28b3f143eb 100644 --- a/test/api_integration/fixtures/opensearch_archiver/saved_objects/10k/mappings.json +++ b/test/api_integration/fixtures/opensearch_archiver/saved_objects/10k/mappings.json @@ -146,47 +146,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "namespace": { "type": "keyword" }, diff --git a/test/api_integration/fixtures/opensearch_archiver/saved_objects/basic/mappings.json b/test/api_integration/fixtures/opensearch_archiver/saved_objects/basic/mappings.json index 5777475bbfc4..df28b3f143eb 100644 --- a/test/api_integration/fixtures/opensearch_archiver/saved_objects/basic/mappings.json +++ b/test/api_integration/fixtures/opensearch_archiver/saved_objects/basic/mappings.json @@ -146,47 +146,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "namespace": { "type": "keyword" }, @@ -250,4 +209,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/api_integration/fixtures/opensearch_archiver/saved_objects/find_edgecases/mappings.json b/test/api_integration/fixtures/opensearch_archiver/saved_objects/find_edgecases/mappings.json index 5bfb286b5ea6..7ff2743fc4a6 100644 --- a/test/api_integration/fixtures/opensearch_archiver/saved_objects/find_edgecases/mappings.json +++ b/test/api_integration/fixtures/opensearch_archiver/saved_objects/find_edgecases/mappings.json @@ -146,47 +146,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "namespace": { "type": "keyword" }, diff --git a/test/api_integration/fixtures/opensearch_archiver/search/count/mappings.json b/test/api_integration/fixtures/opensearch_archiver/search/count/mappings.json index da1e59e86d90..d9638ae749a4 100644 --- a/test/api_integration/fixtures/opensearch_archiver/search/count/mappings.json +++ b/test/api_integration/fixtures/opensearch_archiver/search/count/mappings.json @@ -36,48 +36,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "visualization": { "dynamic": "strict", "properties": { @@ -293,4 +251,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/apps/dashboard/dashboard_filtering.js b/test/functional/apps/dashboard/dashboard_filtering.js index deb2ae399240..b3ff62c8b9da 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.js +++ b/test/functional/apps/dashboard/dashboard_filtering.js @@ -126,11 +126,6 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.savedSearchRowCount(0); }); - // TODO: Uncomment once https://github.com/elastic/kibana/issues/22561 is fixed - // it('timeline is filtered', async () => { - // await dashboardExpect.timelineLegendCount(0); - // }); - it('vega is filtered', async () => { await dashboardExpect.vegaTextsDoNotExist(['5,000']); }); @@ -179,11 +174,6 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.savedSearchRowCount(0); }); - // TODO: Uncomment once https://github.com/elastic/kibana/issues/22561 is fixed - // it('timeline is filtered', async () => { - // await dashboardExpect.timelineLegendCount(0); - // }); - it('vega is filtered', async () => { await dashboardExpect.vegaTextsDoNotExist(['5,000']); }); diff --git a/test/functional/apps/timeline/_expression_typeahead.js b/test/functional/apps/timeline/_expression_typeahead.js deleted file mode 100644 index 5e395ec28a71..000000000000 --- a/test/functional/apps/timeline/_expression_typeahead.js +++ /dev/null @@ -1,119 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 '@osd/expect'; - -export default function ({ getPageObjects }) { - const PageObjects = getPageObjects(['common', 'timeline', 'settings', 'timePicker']); - - describe('expression typeahead', () => { - before(async () => { - await PageObjects.timeline.initTests(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - }); - - it('should display function suggestions filtered by function name', async () => { - await PageObjects.timeline.setExpression('.opensearch'); - const suggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(suggestions.length).to.eql(1); - expect(suggestions[0].includes('.opensearch()')).to.eql(true); - }); - - it('should show argument suggestions when function suggestion is selected', async () => { - await PageObjects.timeline.setExpression('.opensearch'); - await PageObjects.timeline.clickSuggestion(); - const suggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(suggestions.length).to.eql(10); - expect(suggestions[0].includes('fit=')).to.eql(true); - }); - - it('should show argument value suggestions when argument is selected', async () => { - await PageObjects.timeline.setExpression('.legend'); - await PageObjects.timeline.clickSuggestion(); - const argumentSuggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(argumentSuggestions.length).to.eql(4); - expect(argumentSuggestions[1].includes('position=')).to.eql(true); - await PageObjects.timeline.clickSuggestion(1); - const valueSuggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(valueSuggestions.length).to.eql(5); - expect(valueSuggestions[0].includes('disable legend')).to.eql(true); - expect(valueSuggestions[1].includes('place legend in north east corner')).to.eql(true); - }); - - // TODO: [RENAMEME] the index is not being loaded with the default data. - // While navigating creating an index pattern this would works. - // Need to fix why it's not loading prior to text run. - xdescribe('dynamic suggestions for argument values', () => { - describe('.opensearch()', () => { - before(async () => { - await PageObjects.timeline.setExpression('.opensearch'); - await PageObjects.timeline.clickSuggestion(); - }); - - it('should show index pattern suggestions for index argument', async () => { - await PageObjects.timeline.updateExpression('index'); - await PageObjects.timeline.clickSuggestion(); - const suggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(suggestions.length).to.eql(1); - expect(suggestions[0].includes('logstash-*')).to.eql(true); - await PageObjects.timeline.clickSuggestion(); - }); - - it('should show field suggestions for timefield argument when index pattern set', async () => { - await PageObjects.timeline.updateExpression(',timefield'); - await PageObjects.timeline.clickSuggestion(); - const suggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(suggestions.length).to.eql(4); - expect(suggestions[0].includes('@timestamp')).to.eql(true); - await PageObjects.timeline.clickSuggestion(); - }); - - it('should show field suggestions for split argument when index pattern set', async () => { - await PageObjects.timeline.updateExpression(',split'); - await PageObjects.timeline.clickSuggestion(); - const suggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(suggestions.length).to.eql(52); - expect(suggestions[0].includes('@message.raw')).to.eql(true); - await PageObjects.timeline.clickSuggestion(10); - }); - - it('should show field suggestions for metric argument when index pattern set', async () => { - await PageObjects.timeline.updateExpression(',metric'); - await PageObjects.timeline.clickSuggestion(); - await PageObjects.timeline.updateExpression('avg:'); - await PageObjects.timeline.clickSuggestion(); - const suggestions = await PageObjects.timeline.getSuggestionItemsText(); - expect(suggestions.length).to.eql(2); - expect(suggestions[0].includes('avg:bytes')).to.eql(true); - }); - }); - }); - }); -} diff --git a/test/functional/apps/timeline/index.js b/test/functional/apps/timeline/index.js deleted file mode 100644 index d89ffce2eda7..000000000000 --- a/test/functional/apps/timeline/index.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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. - */ - -export default function ({ getService, loadTestFile }) { - const browser = getService('browser'); - const log = getService('log'); - const opensearchArchiver = getService('opensearchArchiver'); - const opensearchDashboardsServer = getService('opensearchDashboardsServer'); - - describe('timeline app', function () { - this.tags('ciGroup1'); - - before(async function () { - log.debug('Starting timeline before method'); - await browser.setWindowSize(1280, 800); - await opensearchArchiver.loadIfNeeded('logstash_functional'); - await opensearchDashboardsServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); - }); - - loadTestFile(require.resolve('./_expression_typeahead')); - }); -} diff --git a/test/functional/config.js b/test/functional/config.js index 9485225dc8ca..d927aea2966f 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -46,7 +46,6 @@ export default async function ({ readConfigFile }) { require.resolve('./apps/management'), require.resolve('./apps/saved_objects_management'), require.resolve('./apps/status_page'), - require.resolve('./apps/timeline'), require.resolve('./apps/visualize'), require.resolve('./apps/vis_builder'), ], @@ -107,9 +106,6 @@ export default async function ({ readConfigFile }) { settings: { pathname: '/app/management', }, - timeline: { - pathname: '/app/timeline', - }, console: { pathname: '/app/dev_tools', hash: '/console', diff --git a/test/functional/fixtures/opensearch_archiver/dashboard/current/opensearch_dashboards/mappings.json b/test/functional/fixtures/opensearch_archiver/dashboard/current/opensearch_dashboards/mappings.json index 8ba03cf35876..c7fb3a03d629 100644 --- a/test/functional/fixtures/opensearch_archiver/dashboard/current/opensearch_dashboards/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/dashboard/current/opensearch_dashboards/mappings.json @@ -23,7 +23,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -366,47 +365,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { @@ -487,4 +445,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/dashboard/legacy/mappings.json b/test/functional/fixtures/opensearch_archiver/dashboard/legacy/mappings.json index 9ca92fc2b55d..afeb0422eb97 100644 --- a/test/functional/fixtures/opensearch_archiver/dashboard/legacy/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/dashboard/legacy/mappings.json @@ -134,48 +134,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -241,4 +199,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/discover/mappings.json b/test/functional/fixtures/opensearch_archiver/discover/mappings.json index 2f24917b04fc..df2768093b7e 100644 --- a/test/functional/fixtures/opensearch_archiver/discover/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/discover/mappings.json @@ -134,48 +134,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/opensearch_archiver/empty_opensearch_dashboards/mappings.json b/test/functional/fixtures/opensearch_archiver/empty_opensearch_dashboards/mappings.json index a3a005fe8075..ad23c9028344 100644 --- a/test/functional/fixtures/opensearch_archiver/empty_opensearch_dashboards/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/empty_opensearch_dashboards/mappings.json @@ -143,48 +143,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/opensearch_archiver/hamlet/mappings.json b/test/functional/fixtures/opensearch_archiver/hamlet/mappings.json index 396bbadc30ba..d50b6c58bf99 100644 --- a/test/functional/fixtures/opensearch_archiver/hamlet/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/hamlet/mappings.json @@ -19,7 +19,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "server": "ec97f1c5da1a19609a60874e5af1100c", - "timeline-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -230,47 +229,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -373,4 +331,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/invalid_scripted_field/mappings.json b/test/functional/fixtures/opensearch_archiver/invalid_scripted_field/mappings.json index 592b4cde4751..19c0d074f96d 100644 --- a/test/functional/fixtures/opensearch_archiver/invalid_scripted_field/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/invalid_scripted_field/mappings.json @@ -140,47 +140,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -247,4 +206,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/management/mappings.json b/test/functional/fixtures/opensearch_archiver/management/mappings.json index 9ca92fc2b55d..afeb0422eb97 100644 --- a/test/functional/fixtures/opensearch_archiver/management/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/management/mappings.json @@ -134,48 +134,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -241,4 +199,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/mgmt/mappings.json b/test/functional/fixtures/opensearch_archiver/mgmt/mappings.json index c7c440ac42e8..35948f59d0e8 100644 --- a/test/functional/fixtures/opensearch_archiver/mgmt/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/mgmt/mappings.json @@ -171,47 +171,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -279,4 +238,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/opensearch_dashboards_sample_data_flights_index_pattern/mappings.json b/test/functional/fixtures/opensearch_archiver/opensearch_dashboards_sample_data_flights_index_pattern/mappings.json index e404d68391c8..2f7eaf05472c 100644 --- a/test/functional/fixtures/opensearch_archiver/opensearch_dashboards_sample_data_flights_index_pattern/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/opensearch_dashboards_sample_data_flights_index_pattern/mappings.json @@ -19,7 +19,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "server": "ec97f1c5da1a19609a60874e5af1100c", - "timeline-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -245,47 +244,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -461,4 +419,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/saved_objects_imports/mappings.json b/test/functional/fixtures/opensearch_archiver/saved_objects_imports/mappings.json index 9ca92fc2b55d..afeb0422eb97 100644 --- a/test/functional/fixtures/opensearch_archiver/saved_objects_imports/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/saved_objects_imports/mappings.json @@ -134,48 +134,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -241,4 +199,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/saved_objects_management/edit_saved_object/mappings.json b/test/functional/fixtures/opensearch_archiver/saved_objects_management/edit_saved_object/mappings.json index 3c2fcb67bd57..b2a66e47c403 100644 --- a/test/functional/fixtures/opensearch_archiver/saved_objects_management/edit_saved_object/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/saved_objects_management/edit_saved_object/mappings.json @@ -355,47 +355,6 @@ } } }, - "timeline-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -456,4 +415,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/timeline/mappings.json b/test/functional/fixtures/opensearch_archiver/timeline/mappings.json index 561ed8b85848..afeb0422eb97 100644 --- a/test/functional/fixtures/opensearch_archiver/timeline/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/timeline/mappings.json @@ -134,48 +134,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -241,4 +199,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/visualize/mappings.json b/test/functional/fixtures/opensearch_archiver/visualize/mappings.json index f93bab96c263..4046784ca4be 100644 --- a/test/functional/fixtures/opensearch_archiver/visualize/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/visualize/mappings.json @@ -134,48 +134,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/opensearch_archiver/visualize_embedding/mappings.json b/test/functional/fixtures/opensearch_archiver/visualize_embedding/mappings.json index 9ca92fc2b55d..afeb0422eb97 100644 --- a/test/functional/fixtures/opensearch_archiver/visualize_embedding/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/visualize_embedding/mappings.json @@ -134,48 +134,6 @@ } } }, - "timeline-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timeline_chart_height": { - "type": "integer" - }, - "timeline_columns": { - "type": "integer" - }, - "timeline_interval": { - "type": "keyword" - }, - "timeline_other_interval": { - "type": "keyword" - }, - "timeline_rows": { - "type": "integer" - }, - "timeline_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -241,4 +199,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/visualize_source-filters/mappings.json b/test/functional/fixtures/opensearch_archiver/visualize_source-filters/mappings.json index a89fe1dfacfc..afeb0422eb97 100644 --- a/test/functional/fixtures/opensearch_archiver/visualize_source-filters/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/visualize_source-filters/mappings.json @@ -134,48 +134,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -241,4 +199,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/opensearch_archiver/visualize_source_filters/mappings.json b/test/functional/fixtures/opensearch_archiver/visualize_source_filters/mappings.json index 0f17621dbf52..b85e54477f8e 100644 --- a/test/functional/fixtures/opensearch_archiver/visualize_source_filters/mappings.json +++ b/test/functional/fixtures/opensearch_archiver/visualize_source_filters/mappings.json @@ -152,48 +152,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, @@ -259,4 +217,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/page_objects/index.ts b/test/functional/page_objects/index.ts index 63cd990c445b..2820f33a226f 100644 --- a/test/functional/page_objects/index.ts +++ b/test/functional/page_objects/index.ts @@ -41,7 +41,6 @@ import { SettingsPageProvider } from './settings_page'; import { SharePageProvider } from './share_page'; import { LoginPageProvider } from './login_page'; import { TimePickerProvider } from './time_picker'; -import { TimelinePageProvider } from './timeline_page'; import { VisualBuilderPageProvider } from './visual_builder_page'; import { VisualizePageProvider } from './visualize_page'; import { VisBuilderPageProvider } from './vis_builder_page'; @@ -65,7 +64,6 @@ export const pageObjects = { settings: SettingsPageProvider, share: SharePageProvider, login: LoginPageProvider, - timeline: TimelinePageProvider, timePicker: TimePickerProvider, visualBuilder: VisualBuilderPageProvider, visualize: VisualizePageProvider, diff --git a/test/functional/page_objects/timeline_page.ts b/test/functional/page_objects/timeline_page.ts deleted file mode 100644 index ee89665e4c01..000000000000 --- a/test/functional/page_objects/timeline_page.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * 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 { FtrProviderContext } from '../ftr_provider_context'; - -export function TimelinePageProvider({ getService, getPageObjects }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - const log = getService('log'); - const PageObjects = getPageObjects(['common', 'header']); - const opensearchArchiver = getService('opensearchArchiver'); - const opensearchDashboardsServer = getService('opensearchDashboardsServer'); - - class TimelinePage { - public async initTests() { - await opensearchDashboardsServer.uiSettings.replace({ - defaultIndex: 'logstash-*', - }); - - log.debug('load opensearch-dashboards index'); - await opensearchArchiver.load('timeline'); - - await PageObjects.common.navigateToApp('timelion'); - } - - public async setExpression(expression: string) { - const input = await testSubjects.find('timelineExpressionTextArea'); - await input.clearValue(); - await input.type(expression); - } - - public async updateExpression(updates: string) { - const input = await testSubjects.find('timelineExpressionTextArea'); - await input.type(updates); - await PageObjects.common.sleep(1000); - } - - public async getExpression() { - const input = await testSubjects.find('timelineExpressionTextArea'); - return input.getVisibleText(); - } - - public async getSuggestionItemsText() { - const elements = await testSubjects.findAll('timelineSuggestionListItem'); - return await Promise.all(elements.map(async (element) => await element.getVisibleText())); - } - - public async clickSuggestion(suggestionIndex = 0, waitTime = 1000) { - const elements = await testSubjects.findAll('timelineSuggestionListItem'); - if (suggestionIndex > elements.length) { - throw new Error( - `Unable to select suggestion ${suggestionIndex}, only ${elements.length} suggestions available.` - ); - } - await elements[suggestionIndex].click(); - // Wait for timeline expression to be updated after clicking suggestions - await PageObjects.common.sleep(waitTime); - } - - public async saveTimelineSheet() { - await testSubjects.click('timelineSaveButton'); - await testSubjects.click('timelineSaveAsSheetButton'); - await testSubjects.click('timelineFinishSaveButton'); - await testSubjects.existOrFail('timelineSaveSuccessToast'); - await testSubjects.waitForDeleted('timelineSaveSuccessToast'); - } - - public async expectWriteControls() { - await testSubjects.existOrFail('timelineSaveButton'); - await testSubjects.existOrFail('timelineDeleteButton'); - } - - public async expectMissingWriteControls() { - await testSubjects.missingOrFail('timelineSaveButton'); - await testSubjects.missingOrFail('timelineDeleteButton'); - } - } - - return new TimelinePage(); -} diff --git a/test/new_visualize_flow/fixtures/opensearch_archiver/opensearch_dashboards/mappings.json b/test/new_visualize_flow/fixtures/opensearch_archiver/opensearch_dashboards/mappings.json index 8ba03cf35876..c7fb3a03d629 100644 --- a/test/new_visualize_flow/fixtures/opensearch_archiver/opensearch_dashboards/mappings.json +++ b/test/new_visualize_flow/fixtures/opensearch_archiver/opensearch_dashboards/mappings.json @@ -23,7 +23,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -366,47 +365,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { @@ -487,4 +445,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/plugin_functional/config.ts b/test/plugin_functional/config.ts index e733a4e36368..ce027815a57f 100644 --- a/test/plugin_functional/config.ts +++ b/test/plugin_functional/config.ts @@ -52,6 +52,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('./test_suites/doc_views_links'), require.resolve('./test_suites/application_links'), require.resolve('./test_suites/data_plugin'), + require.resolve('./test_suites/dashboard_listing_plugin'), ], services: { ...functionalConfig.get('services'), diff --git a/test/plugin_functional/plugins/dashboard_listing_test_plugin/opensearch_dashboards.json b/test/plugin_functional/plugins/dashboard_listing_test_plugin/opensearch_dashboards.json new file mode 100644 index 000000000000..454d9ea58471 --- /dev/null +++ b/test/plugin_functional/plugins/dashboard_listing_test_plugin/opensearch_dashboards.json @@ -0,0 +1,9 @@ +{ + "id": "dashboard_listing_test_plugin", + "version": "0.0.1", + "opensearchDashboardsVersion": "opensearchDashboards", + "configPath": ["dashboard_listing_test_plugin"], + "server": false, + "ui": true, + "requiredPlugins": ["dashboard"] +} diff --git a/test/plugin_functional/plugins/dashboard_listing_test_plugin/package.json b/test/plugin_functional/plugins/dashboard_listing_test_plugin/package.json new file mode 100644 index 000000000000..0b593604a2ad --- /dev/null +++ b/test/plugin_functional/plugins/dashboard_listing_test_plugin/package.json @@ -0,0 +1,17 @@ +{ + "name": "dashboard_listing_test_plugin", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/dashboard_listing_test_plugin", + "opensearchDashboards": { + "version": "opensearchDashboards", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "osd": "../../../../scripts/use_node ../../../../scripts/osd.js", + "build": "../../../../scripts/use_node ../../../../scripts/remove.js './target' && tsc" + }, + "devDependencies": { + "typescript": "4.0.2" + } +} diff --git a/test/plugin_functional/plugins/dashboard_listing_test_plugin/public/index.ts b/test/plugin_functional/plugins/dashboard_listing_test_plugin/public/index.ts new file mode 100644 index 000000000000..80ddbf8a3382 --- /dev/null +++ b/test/plugin_functional/plugins/dashboard_listing_test_plugin/public/index.ts @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import { PluginInitializer } from 'opensearch-dashboards/public'; +import { + DashboardListingTestPlugin, + DashboardListingTestPluginSetup, + DashboardListingTestPluginStart, +} from './plugin'; + +export const plugin: PluginInitializer< + DashboardListingTestPluginSetup, + DashboardListingTestPluginStart +> = () => new DashboardListingTestPlugin(); diff --git a/test/plugin_functional/plugins/dashboard_listing_test_plugin/public/plugin.tsx b/test/plugin_functional/plugins/dashboard_listing_test_plugin/public/plugin.tsx new file mode 100644 index 000000000000..76a407f7c0d2 --- /dev/null +++ b/test/plugin_functional/plugins/dashboard_listing_test_plugin/public/plugin.tsx @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import * as React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { Router, Switch, Route, Link } from 'react-router-dom'; +import { CoreSetup, Plugin } from 'opensearch-dashboards/public'; + +export class DashboardListingTestPlugin + implements Plugin<DashboardListingTestPluginSetup, DashboardListingTestPluginStart> { + public setup(core: CoreSetup, setupDeps: SetupDependencies) { + const ID = 'dashboard_listing_test_plugin'; + const BASE_URL = core.http.basePath.prepend(`/app/${ID}#`); + setupDeps.dashboard.registerDashboardProvider({ + appId: ID, + savedObjectsType: 'dashboardTest', + savedObjectsName: 'Dashboard Test', + editUrlPathFn: (obj: SavedObject) => `${BASE_URL}/${obj.id}/edit`, + viewUrlPathFn: (obj: SavedObject) => `${BASE_URL}/${obj.id}`, + createLinkText: 'Test Dashboard', + createSortText: 'Test Dashboard', + createUrl: `${BASE_URL}/create`, + }); + + core.application.register({ + id: ID, + title: 'Dashboard Listing Test Plugin', + appRoute: `app/${ID}`, + async mount(context, { element }) { + render( + <h1 data-test-subj="dashboardListingTestHeader">Dashboard Listing Test Header</h1>, + element + ); + + return () => unmountComponentAtNode(element); + }, + }); + } + + public start() {} + public stop() {} +} + +export type DashboardListingTestPluginSetup = ReturnType<DashboardListingTestPlugin['setup']>; +export type DashboardListingTestPluginStart = ReturnType<DashboardListingTestPlugin['start']>; diff --git a/test/plugin_functional/plugins/dashboard_listing_test_plugin/tsconfig.json b/test/plugin_functional/plugins/dashboard_listing_test_plugin/tsconfig.json new file mode 100644 index 000000000000..f77a5eaffc30 --- /dev/null +++ b/test/plugin_functional/plugins/dashboard_listing_test_plugin/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "../../../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../../../src/core/tsconfig.json" } + ] +} diff --git a/test/plugin_functional/test_suites/dashboard_listing_plugin/dashboard_listing_plugin.ts b/test/plugin_functional/test_suites/dashboard_listing_plugin/dashboard_listing_plugin.ts new file mode 100644 index 000000000000..354cfac4fa87 --- /dev/null +++ b/test/plugin_functional/test_suites/dashboard_listing_plugin/dashboard_listing_plugin.ts @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import url from 'url'; +import expect from '@osd/expect'; + +const getPathWithHash = (absoluteUrl: string) => { + const parsed = url.parse(absoluteUrl); + return `${parsed.path}${parsed.hash ?? ''}`; +}; + +export default function ({ getService, getPageObjects }) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'dashboard', 'header']); + const browser = getService('browser'); + const listingTable = getService('listingTable'); + const find = getService('find'); + + describe('dashboard listing plugin', function describeIndexTests() { + const dashboardName = 'Dashboard Test'; + + before(async () => { + await PageObjects.dashboard.initTests({ + opensearchDashboardsIndex: '../functional/fixtures/opensearch_archiver/dashboard/legacy', + }); + await PageObjects.dashboard.clickCreateDashboardPrompt(); + await PageObjects.dashboard.saveDashboard('default'); + await PageObjects.dashboard.gotoDashboardLandingPage(); + }); + + it('should be able to navigate to create a dashboard', async () => { + await testSubjects.click('createMenuDropdown'); + await testSubjects.click('contextMenuItem-dashboard'); + await PageObjects.dashboard.saveDashboard(dashboardName); + + await PageObjects.dashboard.gotoDashboardLandingPage(); + await listingTable.searchAndExpectItemsCount('dashboard', dashboardName, 1); + }); + + it('should be able to navigate to view dashboard', async () => { + await listingTable.clickItemLink('dashboard', dashboardName); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await PageObjects.dashboard.getIsInViewMode(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + }); + + it('should be able to navigate to edit dashboard', async () => { + await listingTable.searchForItemWithName(dashboardName); + const editBttn = await find.allByCssSelector('.euiToolTipAnchor'); + await editBttn[0].click(); + await PageObjects.dashboard.clickCancelOutOfEditMode(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + }); + + it('should be able to navigate to create a test dashboard', async () => { + await testSubjects.click('createMenuDropdown'); + await testSubjects.click('contextMenuItem-dashboard_listing_test_plugin'); + expect(getPathWithHash(await browser.getCurrentUrl())).to.eql( + '/app/dashboard_listing_test_plugin#/create' + ); + }); + }); +} diff --git a/test/plugin_functional/test_suites/dashboard_listing_plugin/index.ts b/test/plugin_functional/test_suites/dashboard_listing_plugin/index.ts new file mode 100644 index 000000000000..a84790824f64 --- /dev/null +++ b/test/plugin_functional/test_suites/dashboard_listing_plugin/index.ts @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +export default function ({ getService, loadTestFile }) { + const browser = getService('browser'); + const opensearchArchiver = getService('opensearchArchiver'); + + async function loadLogstash() { + await browser.setWindowSize(1200, 900); + await opensearchArchiver.loadIfNeeded( + '../functional/fixtures/opensearch_archiver/logstash_functional' + ); + } + + async function unloadLogstash() { + await opensearchArchiver.unload( + '../functional/fixtures/opensearch_archiver/logstash_functional' + ); + } + + describe('dashboard listing plugin', () => { + before(loadLogstash); + after(unloadLogstash); + + loadTestFile(require.resolve('./dashboard_listing_plugin')); + }); +} diff --git a/yarn.lock b/yarn.lock index 33ca05a2f2c1..e1985b3cd645 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1385,7 +1385,7 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@gar/promisify@^1.0.1": +"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== @@ -2221,6 +2221,36 @@ resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== +"@lmdb/lmdb-darwin-arm64@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.8.0.tgz#20c8c2b81cde5c35fcbe81714448493db93d3e62" + integrity sha512-SadxUdQvGDa+gfZr6uX8kiXbyVUzulhhvieco12K/c5oaNY00MQAm5C00eP2ht959zVus6cQyD09LmoP0OBIqg== + +"@lmdb/lmdb-darwin-x64@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.8.0.tgz#b78c20328725daefce846bf7480a8d3d5b789e19" + integrity sha512-woNqmHXTX7cLu9Mbyj/c1wHQFSoCC/n1CwL2xw2tLVQTXFFJrX0RJQd5XBtvr5kC6Rqf51dk3ScLHU/Bl6v7wg== + +"@lmdb/lmdb-linux-arm64@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.8.0.tgz#1b343dfc91c13c3633b9237dce4c454c4a7d576f" + integrity sha512-XQtJs4xOQ9uNKuuxSkJfsUdQJ1a/gyKwbxBGQw5VUo/RhzwxOdVM8deIBv8UfdjfBId1JIDn/8FXuPmDVfnKQg== + +"@lmdb/lmdb-linux-arm@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.8.0.tgz#26e079de966e9d68b640810593fdb14f1b5958f2" + integrity sha512-uog9KOYrK0uIafyIg7wddB3h0s6qA9qoDZVfACEW3LvYCmw5iLOAsKuh/c+7CUeCYyAJVaWGK2YSDFXRlHdcUw== + +"@lmdb/lmdb-linux-x64@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.8.0.tgz#ab84bb9591f73e194ee5d8c7aeecddbe953112ea" + integrity sha512-I5OuHnZYoGtIlq6Wmga3qHb0yefNgMrvsb+XbXfoDuQNa/KVBUb9UZgNJ65707egwHNGCFaMwXcKoGNTaJof6A== + +"@lmdb/lmdb-win32-x64@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.8.0.tgz#5fa3c9453820d474ffcf7a5c1d71d7b3775621fe" + integrity sha512-RWUSEdHh+MbqmshbsyAHA1kInR7F0MEphWh4HzWlP+3bjUVy36Fn6L1xVbLPfndcAwlbv/x5XuyKaiKK2hEzrw== + "@mapbox/hast-util-table-cell-style@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@mapbox/hast-util-table-cell-style/-/hast-util-table-cell-style-0.2.0.tgz#1003f59d54fae6f638cb5646f52110fb3da95b4d" @@ -2291,11 +2321,125 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" + integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" + integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" + integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" + integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== + +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" + integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== + +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" + integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== + "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== +"@node-rs/xxhash-android-arm-eabi@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-android-arm-eabi/-/xxhash-android-arm-eabi-1.4.0.tgz#55ace4d3882686d1e379aaf613e1338d78f13fc8" + integrity sha512-JuZNqt5/znWkIGteikQdS+HT9S0JsMYi06S4yzU/sMKLCIPvD0MnCTXlYtuDcgRIKScCaepAsSQVomnAyLFNNA== + +"@node-rs/xxhash-android-arm64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-android-arm64/-/xxhash-android-arm64-1.4.0.tgz#2290c53ceabda804afb4c45679613d833a6385a0" + integrity sha512-BZzQO5jlgsIr9HhiqTwZjYqlfVeZiu+7PaoAdNEOq+i/SjyAqv1jGSkyek4rBSAiodyNkXcbE0eQtomeN6a55w== + +"@node-rs/xxhash-darwin-arm64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-darwin-arm64/-/xxhash-darwin-arm64-1.4.0.tgz#96df4f48b13deb6899e84ed0882bdbd0a4856f13" + integrity sha512-JlEAzTsQaqJaWVse/JP//6QKBIhzqzTlvNY4uEbi8TaZMfvDDhW//ClXM6CkSV799GJxAYPu1LXa4+OeBQpa7Q== + +"@node-rs/xxhash-darwin-x64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-darwin-x64/-/xxhash-darwin-x64-1.4.0.tgz#9df3ca3a87354dd5386aadfa20ad032a299c2b8f" + integrity sha512-9ycVJfzLvw1wc6Tgq0giLkMn5nGOBawTeOA17t27dQFdY/scZPz583DO7w+eznMnlzUXwoLiloanUebRhy+piQ== + +"@node-rs/xxhash-freebsd-x64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-freebsd-x64/-/xxhash-freebsd-x64-1.4.0.tgz#24b0c0bfd33429303688b4af78f9d323daa0fb5b" + integrity sha512-vFRDr6qA0gHWQDjuSxXcdzM4Ppk+5VebEhc76zkWrRVc6RG60fxLo5B4j6QwMwXGTYaG8HMv/nQhAgbnOCWWxQ== + +"@node-rs/xxhash-linux-arm-gnueabihf@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm-gnueabihf/-/xxhash-linux-arm-gnueabihf-1.4.0.tgz#4c09f70cd39429fb1a52f3567085e949603d4817" + integrity sha512-0KS6y1caqbtPanos9XNMekWpozCHA6QSlQzaZyn9Hn+Z+mYpR5+NoWixefhp06jt59qF9+LkkF3C9fSEHYmq/w== + +"@node-rs/xxhash-linux-arm64-gnu@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm64-gnu/-/xxhash-linux-arm64-gnu-1.4.0.tgz#e92d7026614506fb4db309977127fd8589fabd7c" + integrity sha512-QI97JK2qiQhVgRtUBMgA1ZjPLpwnz11SE2Mw1jryejmyH9EXKKiCyt2FweO6MVP7bEuMxcdajBho4pEL7s/QsA== + +"@node-rs/xxhash-linux-arm64-musl@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm64-musl/-/xxhash-linux-arm64-musl-1.4.0.tgz#a8b16233a86c116e6af32a69278248d17b2d09e7" + integrity sha512-dtMid4OMkNBYGJkjoT1jdkENpV8m8MGp3lliDN8C+2znZUQM8KFRTXRkfaq4lgzu3Y2XeYzsLOoBsBd3Hgf7gA== + +"@node-rs/xxhash-linux-x64-gnu@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-x64-gnu/-/xxhash-linux-x64-gnu-1.4.0.tgz#385ec91396ebaa2b73abf419be3971ec893dcbd1" + integrity sha512-OeOQL10cG62wL1IVoeC74xESmefHU7r3xiZMTP2hK5Dh3FdF2sa3x/Db9BcGXlaokg/lMGDxuTuzOLC2Rv/wlQ== + +"@node-rs/xxhash-linux-x64-musl@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-x64-musl/-/xxhash-linux-x64-musl-1.4.0.tgz#715bb962502b0ec69e1fc19db22ac035c63d30c7" + integrity sha512-kZ8wNi5bH9b+ZpuPlSbFd6JXk8CKbfCvCPZ0Vk0IqLkzB6PihQflnZPM9r0QZ2jtFgyfWmpbFK4YxwX9YcyLog== + +"@node-rs/xxhash-win32-arm64-msvc@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-arm64-msvc/-/xxhash-win32-arm64-msvc-1.4.0.tgz#4a3a4ebcb50c73e4309e429b28eb44dbf8f7f71f" + integrity sha512-Ggv66jlhQvj4XgQqNgl2JKQ7My/97PvPZi5jKbcS7t65wJC36J6XERQwRPdupO8UH63XfPqb7HJqrgmiz8tmlA== + +"@node-rs/xxhash-win32-ia32-msvc@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-ia32-msvc/-/xxhash-win32-ia32-msvc-1.4.0.tgz#fdfdb43e41113a8baf15779ca53bb637d2e1bc8f" + integrity sha512-mYpF1+7unqKKGsPn7Y8X6SqP2Bc5BU5dsHBKhAGAuvrMg9W63zM+YWM8/fpNGfFlOrjiKRvXHZ96nrZyzoxeBw== + +"@node-rs/xxhash-win32-x64-msvc@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-x64-msvc/-/xxhash-win32-x64-msvc-1.4.0.tgz#aee714a4ae0121f3947f94139adf13f5b6d93d12" + integrity sha512-rKuqWHuQNlrfjIOkQW3oCBta/GUlyVoUkKB13aVr8uixOs/eneuDaYJx2h02FAAWlWCKADFnMxgDl0LVFBy53w== + +"@node-rs/xxhash@^1.3.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@node-rs/xxhash/-/xxhash-1.4.0.tgz#1e75850e0e530c9224e8e5ba4056d52e8868291b" + integrity sha512-UpSOParhMqbQ7hsYovN2e+uqvWqHJiCDvFl8gDzMcXgBY/PkI2zo2zhdRAZdz48c6/dke+0WjCKy90wDVQxS6g== + optionalDependencies: + "@node-rs/xxhash-android-arm-eabi" "1.4.0" + "@node-rs/xxhash-android-arm64" "1.4.0" + "@node-rs/xxhash-darwin-arm64" "1.4.0" + "@node-rs/xxhash-darwin-x64" "1.4.0" + "@node-rs/xxhash-freebsd-x64" "1.4.0" + "@node-rs/xxhash-linux-arm-gnueabihf" "1.4.0" + "@node-rs/xxhash-linux-arm64-gnu" "1.4.0" + "@node-rs/xxhash-linux-arm64-musl" "1.4.0" + "@node-rs/xxhash-linux-x64-gnu" "1.4.0" + "@node-rs/xxhash-linux-x64-musl" "1.4.0" + "@node-rs/xxhash-win32-arm64-msvc" "1.4.0" + "@node-rs/xxhash-win32-ia32-msvc" "1.4.0" + "@node-rs/xxhash-win32-x64-msvc" "1.4.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2330,6 +2474,14 @@ "@gar/promisify" "^1.0.1" semver "^7.3.5" +"@npmcli/fs@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" + integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== + dependencies: + "@gar/promisify" "^1.1.3" + semver "^7.3.5" + "@npmcli/move-file@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" @@ -2338,17 +2490,30 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@opensearch-project/opensearch@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-2.1.0.tgz#d79ab4ae643493512099673e117faffe40b4fe56" - integrity sha512-iM2u63j2IlUOuMSbcw1TZFpRqjK6qMwVhb3jLLa/x4aATxdKOiO1i17mgzfkeepqj85efNzXBZzN+jkq1/EXhQ== +"@npmcli/move-file@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" + integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@opensearch-project/opensearch@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-2.2.1.tgz#a400203afa6512ef73945663163a404763a10f5a" + integrity sha512-8zfQX1acL9eWG+ohIc9nJVT9LSqXCdbVEJs0rCPRtji3XF6ahzsiKmGNTeWLxCPDxWCjAIWq9t95xP3Y5Egi6Q== dependencies: aws4 "^1.11.0" debug "^4.3.1" - hpagent "^0.1.1" + hpagent "^1.2.0" ms "^2.1.3" secure-json-parse "^2.4.0" +"@opentelemetry/api@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.0.tgz#2c91791a9ba6ca0a0f4aaac5e45d58df13639ac8" + integrity sha512-IgMK9i3sFGNUqPMbjABm0G26g0QCKCUBfglhQ7rQq6WcxbKfEHRcmwsoER4hZcuYqJgkYn2OeuoJIv7Jsftp7g== + "@percy/cli-app@1.10.4": version "1.10.4" resolved "https://registry.yarnpkg.com/@percy/cli-app/-/cli-app-1.10.4.tgz#3c27b71269d41ca3bd5af6d69ec5493cf5a16d1a" @@ -2657,6 +2822,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@tsd/typescript@~4.7.3": version "4.7.4" resolved "https://registry.yarnpkg.com/@tsd/typescript/-/typescript-4.7.4.tgz#f1e4e6c3099a174a0cb7aa51cf53f34f6494e528" @@ -2785,10 +2955,10 @@ dependencies: "@types/color-convert" "*" -"@types/compression-webpack-plugin@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/compression-webpack-plugin/-/compression-webpack-plugin-2.0.5.tgz#2cb870888da56db7c9f096e6b2bb31419fb51c2d" - integrity sha512-yv0M9wZ/9GSJewznsJERr4BhFhnjlrwUoe0fpQw2AkLgpbOKabzsN6EexgytKGnkGiKWYR00w1YwqeGfo4/nUw== +"@types/compression-webpack-plugin@^6.0.6": + version "6.0.6" + resolved "https://registry.yarnpkg.com/@types/compression-webpack-plugin/-/compression-webpack-plugin-6.0.6.tgz#0af843e445b5b6531432a8f521cc2c34c4a82276" + integrity sha512-UuSUiWmgg4zfzTaGwyo4XCY4MBfgPuvvwAHujQZyu1beGDiJKXU3YMg0Pl6zcLMYV6tDqZJWyYG1QkT5yt28EA== dependencies: "@types/webpack" "^4" @@ -3126,10 +3296,10 @@ resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e" integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg== -"@types/js-yaml@^3.11.1": - version "3.12.7" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.7.tgz#330c5d97a3500e9c903210d6e49f02964af04a0e" - integrity sha512-S6+8JAYTE1qdsc9HMVsfY7+SgSuUU/Tp6TYTmITW0PZxiyIMvol3Gy//y69Wkhs0ti4py5qgR3uZH6uz/DNzJQ== +"@types/js-yaml@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" + integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== "@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" @@ -3285,10 +3455,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@12.20.24", "@types/node@16.9.1", "@types/node@^14.17.32": - version "14.18.12" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.12.tgz#0d4557fd3b94497d793efd4e7d92df2f83b4ef24" - integrity sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A== +"@types/node@*", "@types/node@12.20.24", "@types/node@16.9.1", "@types/node@~18.7.0": + version "18.7.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.23.tgz#75c580983846181ebe5f4abc40fe9dfb2d65665f" + integrity sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg== "@types/normalize-package-data@*", "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -4155,7 +4325,16 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: +agentkeepalive@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" + integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== + dependencies: + debug "^4.1.0" + depd "^2.0.0" + humanize-ms "^1.2.1" + +agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== @@ -4249,11 +4428,6 @@ angular-sanitize@^1.8.0: resolved "https://registry.yarnpkg.com/angular-sanitize/-/angular-sanitize-1.8.2.tgz#ae78040f00c5e2ce1c63780bcc47fa14a1698296" integrity sha512-OB6Goa+QN3byf5asQ7XRl7DKZejm/F/ZOqa9z1skqYVOWA2hoBxoCmt9E7+i7T/TbxZP5zYzKxNZVVJNu860Hg== -angular-sortable-view@^0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/angular-sortable-view/-/angular-sortable-view-0.0.17.tgz#99e2679951a86b6ee6ff27b099022943c683fb4f" - integrity sha512-2WkhM0Lt/wyMyrX/+7ve9ejSegBd7A4eRBNHEIJz8XMBIOjt+3oM1WpcAm+qNThkmNmmQaDeaYv0TQZw/WDMBw== - angular@>=1.0.6, angular@^1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.2.tgz#5983bbb5a9fa63e213cb7749199e0d352de3a2f1" @@ -4644,6 +4818,11 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + integrity sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA== + async-value-promise@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/async-value-promise/-/async-value-promise-1.1.1.tgz#68957819e3eace804f3b4b69477e2bd276c15378" @@ -5308,6 +5487,30 @@ cacache@^15.0.5, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" +cacache@^16.1.0: + version "16.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" + integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== + dependencies: + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + unique-filename "^2.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -5440,7 +5643,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@~4.1.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2, chalk@~4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -5540,7 +5743,7 @@ cheerio@^1.0.0-rc.3: parse5-htmlparser2-tree-adapter "^6.0.1" tslib "^2.2.0" -chokidar@3.5.3, "chokidar@>=2.0.0 <4.0.0", chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.2: +chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.2: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -5942,11 +6145,12 @@ compress-commons@^4.1.0: normalize-path "^3.0.0" readable-stream "^3.6.0" -compression-webpack-plugin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-4.0.1.tgz#33eda97f1170dd38c5556771de10f34245aa0274" - integrity sha512-0mg6PgwTsUe5LEcUrOu3ob32vraDx2VdbMGAT1PARcOV+UJWDYZFdkSo6RbHoGQ061mmmkC7XpRKOlvwm/gzJQ== +"compression-webpack-plugin@npm:@amoo-miki/compression-webpack-plugin@4.0.1-rc.1": + version "4.0.1-rc.1" + resolved "https://registry.yarnpkg.com/@amoo-miki/compression-webpack-plugin/-/compression-webpack-plugin-4.0.1-rc.1.tgz#a9736c353bca6b8f0f35c0cccdedb7070b3459db" + integrity sha512-cwh4PPeqhN1AZUB2t5LX7599rcN+/mbLneAz7EFe6oZIeip0t91jr0dQS7J7NouF43olmIfHTTcnXOskd6N9sQ== dependencies: + "@node-rs/xxhash" "^1.3.0" cacache "^15.0.5" find-cache-dir "^3.3.1" schema-utils "^2.7.0" @@ -5988,11 +6192,6 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -container-info@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/container-info/-/container-info-1.1.0.tgz#6fcb94e93eacd397c6316ca2834491ede44e55ee" - integrity sha512-eD2zLAmxGS2kmL4f1jY8BdOqnmpL6X70kvzTBW/9FIQnxoxiBJ4htMsTmtPLPWRs7NHYFvqKQ1VtppV08mdsQA== - content-disposition@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -6007,10 +6206,10 @@ convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.4.0, co dependencies: safe-buffer "~5.1.1" -cookie@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== cookiejar@^2.1.4: version "2.1.4" @@ -6889,7 +7088,7 @@ delayed-stream@~1.0.0: delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== delete-empty@^2.0.0: version "2.0.0" @@ -6905,6 +7104,11 @@ depd@^1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + dependency-check@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/dependency-check/-/dependency-check-4.1.0.tgz#d45405cabb50298f8674fe28ab594c8a5530edff" @@ -6938,10 +7142,10 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== +detect-libc@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== detect-newline@2.X: version "2.1.0" @@ -7017,14 +7221,6 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dir-glob@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" - integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== - dependencies: - arrify "^1.0.1" - path-type "^3.0.0" - dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -7213,14 +7409,13 @@ ejs@^3.1.7: dependencies: jake "^10.8.5" -elastic-apm-http-client@11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.0.1.tgz#15dbe99d56d62b3f732d1bd2b51bef6094b78801" - integrity sha512-5AOWlhs2WlZpI+DfgGqY/8Rk7KF8WeevaO8R961eBylavU6GWhLRNiJncohn5jsvrqhmeT19azBvy/oYRN7bJw== +elastic-apm-http-client@11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.2.0.tgz#4da8b975ca326c1e5beb59746ab1124c4feddad3" + integrity sha512-XHXK+gQmd34eRN/ffrml7AN4h1VwujB79WEO2C/J59ufvEk+mT1OGBhl6pntHPUWn4Um52C5m84O6jIXzaQwfw== dependencies: agentkeepalive "^4.2.1" breadth-filter "^2.0.0" - container-info "^1.0.1" end-of-stream "^1.4.4" fast-safe-stringify "^2.0.7" fast-stream-to-buffer "^1.0.0" @@ -7229,19 +7424,21 @@ elastic-apm-http-client@11.0.1: semver "^6.3.0" stream-chopper "^3.0.1" -elastic-apm-node@^3.7.0: - version "3.31.0" - resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.31.0.tgz#6e0bf622d922c95ff0127a263babcdeaeea71457" - integrity sha512-0OulazfhkXYbOaGkHncqjwOfxtcvzsDyzUKr6Y1k95HwKrjf1Vi+xPutZv4p/WfDdO+JadphI0U2Uu5ncGB2iA== +elastic-apm-node@^3.43.0: + version "3.43.0" + resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.43.0.tgz#3d644cdb403c8230f5db7b6a3796d19e38faa2b7" + integrity sha512-iFuP8pXkfQst1LOFq6F86EjHhkvUL+pigpmbw2KL8rs+9i7Fn5pkxf8RP1G3oqwqydwUSdvw5+DPhO0lDO+LtA== dependencies: "@elastic/ecs-pino-format" "^1.2.0" + "@opentelemetry/api" "^1.1.0" after-all-results "^2.0.0" async-cache "^1.1.0" async-value-promise "^1.1.1" basic-auth "^2.0.1" - cookie "^0.4.0" + cookie "^0.5.0" core-util-is "^1.0.2" - elastic-apm-http-client "11.0.1" + debug "^4.1.1" + elastic-apm-http-client "11.2.0" end-of-stream "^1.4.4" error-callsites "^2.0.4" error-stack-parser "^2.0.6" @@ -7251,20 +7448,19 @@ elastic-apm-node@^3.7.0: is-native "^1.0.1" lru-cache "^6.0.0" measured-reporting "^1.51.1" + module-details-from-path "^1.0.3" monitor-event-loop-delay "^1.0.0" object-filter-sequence "^1.0.0" object-identity-map "^1.0.2" original-url "^1.2.3" pino "^6.11.2" - read-pkg-up "^7.0.1" relative-microtime "^2.0.0" - require-in-the-middle "^5.0.3" + resolve "^1.22.1" semver "^6.3.0" set-cookie-serde "^1.0.0" shallow-clone-shim "^2.0.0" source-map "^0.8.0-beta.0" sql-summary "^1.0.1" - traceparent "^1.0.0" traverse "^0.6.6" unicode-byte-truncate "^1.0.0" @@ -7337,7 +7533,7 @@ emoticon@^3.2.0: resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== -encoding@^0.1.12: +encoding@^0.1.12, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -8353,13 +8549,6 @@ fetch-mock@^7.3.9: path-to-regexp "^2.2.1" whatwg-url "^6.5.0" -fibers@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/fibers/-/fibers-5.0.3.tgz#2fd03acb255db66fe693d15beafbf5ae92193fd7" - integrity sha512-/qYTSoZydQkM21qZpGLDLuCq8c+B8KhuCQ1kLPvnRNhxhVbvrpmH9l2+Lblf5neDuEsY4bfT7LeO553TXQDvJw== - dependencies: - detect-libc "^1.0.3" - figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -8722,7 +8911,7 @@ fs-extra@~7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^2.0.0: +fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== @@ -8809,6 +8998,13 @@ gauge@^4.0.3: strip-ansi "^6.0.1" wide-align "^1.1.5" +gaze@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== + dependencies: + globule "^1.0.0" + geckodriver@^3.0.2: version "3.2.0" resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-3.2.0.tgz#6b0a85e2aafbce209bca30e2d53af857707b1034" @@ -8858,6 +9054,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw== + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -8966,7 +9167,7 @@ glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.1.7, glob@~7.1.6: +glob@7.1.7, glob@~7.1.1, glob@~7.1.6: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -8990,6 +9191,17 @@ glob@7.2.0, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glo once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + glob@~5.0.0: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -9094,19 +9306,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^8.0.1: - version "8.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" - integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== - dependencies: - array-union "^1.0.1" - dir-glob "2.0.0" - fast-glob "^2.0.2" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -9126,6 +9325,15 @@ globjoin@^0.1.4: resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM= +globule@^1.0.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.4.tgz#7c11c43056055a75a6e68294453c17f2796170fb" + integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg== + dependencies: + glob "~7.1.1" + lodash "^4.17.21" + minimatch "~3.0.2" + got@11.8.5, got@^11.8.2: version "11.8.5" resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" @@ -9589,11 +9797,6 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoek@5.x.x: - version "5.0.4" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.4.tgz#0f7fa270a1cafeb364a4b2ddfaa33f864e4157da" - integrity sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w== - hoek@6.x.x: version "6.1.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c" @@ -9630,10 +9833,10 @@ hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" -hpagent@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-0.1.2.tgz#cab39c66d4df2d4377dbd212295d878deb9bdaa9" - integrity sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ== +hpagent@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903" + integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA== html-element-map@^1.2.0: version "1.3.1" @@ -9707,10 +9910,10 @@ htmlparser2@^7.0: domutils "^2.8.0" entities "^3.0.1" -http-aws-es@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/http-aws-es/-/http-aws-es-6.0.0.tgz#1528978d2bee718b8732dcdced0856efa747aeff" - integrity sha512-g+qp7J110/m4aHrR3iit4akAlnW0UljZ6oTq/rCcbsI8KP9x+95vqUtx49M2XQ2JMpwJio3B6gDYx+E8WDxqiA== +"http-aws-es@npm:@zhongnansu/http-aws-es@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@zhongnansu/http-aws-es/-/http-aws-es-6.0.1.tgz#1ab929eb7faa78ac5386c84069fb2e885fe1661c" + integrity sha512-uhe3FUtgT+sDyW2VA0hxQ44HUfeI8tf4nUo83mc3jtQPAIO57ZpRFKePmCoU+qaqHWfesD9hIQkTkXOJGlW26w== http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.1" @@ -9741,6 +9944,15 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" @@ -9823,11 +10035,6 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -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== - ignore@^4.0.3, ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -9979,11 +10186,6 @@ inquirer@^7.0.0, inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -install-artifact-from-github@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.1.tgz#eefaad9af35d632e5d912ad1569c1de38c3c2462" - integrity sha512-3l3Bymg2eKDsN5wQuMfgGEj2x6l5MCAv0zPL6rxHESufFVlEAKW/6oY9F1aGgvY/EgWm5+eWGRjINveL4X7Hgg== - internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -10182,10 +10384,10 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== +is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" @@ -10430,6 +10632,11 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-primitive@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-3.0.1.tgz#98c4db1abff185485a657fc2905052b940524d05" + integrity sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w== + is-promise@^2.1.0, is-promise@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" @@ -10735,10 +10942,10 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" -jest-canvas-mock@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz#9535d14bc18ccf1493be36ac37dd349928387826" - integrity sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg== +jest-canvas-mock@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.5.1.tgz#81509af658ef485e9a1bf39c64e06761517bdbcb" + integrity sha512-IVnRiz+v4EYn3ydM/pBo8GW/J+nU/Hg5gHBQQOUQhdRyNfvHnabB8ReqARLO0p+kvQghqr4V0tA92CF3JcUSRg== dependencies: cssfontparser "^1.2.1" moo-color "^1.0.2" @@ -11187,12 +11394,12 @@ jmespath@0.16.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== -joi@^13.5.2: - version "13.7.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-13.7.0.tgz#cfd85ebfe67e8a1900432400b4d03bbd93fb879f" - integrity sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q== +joi@^14.3.1: + version "14.3.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-14.3.1.tgz#164a262ec0b855466e0c35eea2a885ae8b6c703c" + integrity sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ== dependencies: - hoek "5.x.x" + hoek "6.x.x" isemail "3.x.x" topo "3.x.x" @@ -11217,6 +11424,11 @@ jquery@^3.5.0: resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw== +js-base64@^2.4.9: + version "2.6.4" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" + integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ== + js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -11234,14 +11446,14 @@ js-yaml-js-types@1.0.0: dependencies: esprima "^4.0.1" -js-yaml@4.1.0: +js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" -js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@~3.14.0: +js-yaml@^3.13.1, js-yaml@~3.14.0: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -11622,16 +11834,24 @@ listr@^0.14.1: p-map "^2.0.0" rxjs "^6.3.3" -lmdb-store@^1.6.11: - version "1.6.14" - resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-1.6.14.tgz#8aa5f36fb04195f8639a3b01b32f6696867f2bc9" - integrity sha512-4woZfvfgolMEngjoMJrwePjdLotr3QKGJsDWURlJmKBed5JtE00IfAKo7ryPowl4ksGcs21pcdLkwrPnKomIuA== - dependencies: - msgpackr "^1.5.0" - nan "^2.14.2" - node-gyp-build "^4.2.3" - ordered-binary "^1.0.0" - weak-lru-cache "^1.0.0" +lmdb@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.8.0.tgz#0f0aee59e35e6f890d75c02d0860e260bc95955a" + integrity sha512-KMBd90xia9x289LlwDtZkqvyXDFM1cZATxKm965uo3Nq2eaBTy+fmJsHf7+d6xXtFDANxKzWVvrbkThAAI2lfg== + dependencies: + msgpackr "1.9.1" + node-addon-api "^6.1.0" + node-gyp-build-optional-packages "5.1.0" + ordered-binary "^1.4.0" + weak-lru-cache "^1.2.2" + yarn "^1.22.19" + optionalDependencies: + "@lmdb/lmdb-darwin-arm64" "2.8.0" + "@lmdb/lmdb-darwin-x64" "2.8.0" + "@lmdb/lmdb-linux-arm" "2.8.0" + "@lmdb/lmdb-linux-arm64" "2.8.0" + "@lmdb/lmdb-linux-x64" "2.8.0" + "@lmdb/lmdb-win32-x64" "2.8.0" load-bmfont@^1.3.1, load-bmfont@^1.4.0: version "1.4.1" @@ -11966,6 +12186,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + lru-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" @@ -11993,6 +12218,28 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" +make-fetch-happen@^10.0.4: + version "10.2.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" + integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^16.1.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-fetch "^2.0.3" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^9.0.0" + make-fetch-happen@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" @@ -12362,7 +12609,14 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@~3.0.4: +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@~3.0.2, minimatch@~3.0.4: version "3.0.8" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== @@ -12401,6 +12655,17 @@ minipass-fetch@^1.3.2: optionalDependencies: encoding "^0.1.12" +minipass-fetch@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" + integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== + dependencies: + minipass "^3.1.6" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -12429,7 +12694,7 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minipass@^3.1.0, minipass@^3.1.3: +minipass@^3.1.0, minipass@^3.1.3, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -12443,7 +12708,12 @@ minipass@^4.0.0: dependencies: yallist "^4.0.0" -minizlib@^2.0.0, minizlib@^2.1.1: +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -12610,20 +12880,26 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -msgpackr-extract@^1.0.14: - version "1.0.16" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-1.0.16.tgz#701c4f6e6f25c100ae84557092274e8fffeefe45" - integrity sha512-fxdRfQUxPrL/TizyfYfMn09dK58e+d65bRD/fcaVH4052vj30QOzzqxcQIS7B0NsqlypEQ/6Du3QmP2DhWFfCA== +msgpackr-extract@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" + integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== dependencies: - nan "^2.14.2" - node-gyp-build "^4.2.3" - -msgpackr@^1.5.0: - version "1.5.5" - resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.5.5.tgz#c0562abc2951d7e29f75d77a8656b01f103a042c" - integrity sha512-JG0V47xRIQ9pyUnx6Hb4+3TrQoia2nA3UIdmyTldhxaxtKFkekkKpUW/N6fwHwod9o4BGuJGtouxOk+yCP5PEA== + node-gyp-build-optional-packages "5.0.7" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" + +msgpackr@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.9.1.tgz#4375d705232b721bedb44a3993e7aa8a6f959502" + integrity sha512-jJdrNH8tzfCtT0rjPFryBXjRDQE7rqfLkah4/8B4gYa7NNZYFBcGxqWBtfQpGC+oYyBwlkj3fARk4aooKNPHxg== optionalDependencies: - msgpackr-extract "^1.0.14" + msgpackr-extract "^3.0.2" multimatch@^4.0.0: version "4.0.0" @@ -12656,12 +12932,12 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1, nan@^2.14.2: +nan@^2.12.1: version "2.15.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== -nan@^2.15.0: +nan@^2.17.0: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== @@ -12736,7 +13012,7 @@ needle@^2.5.2: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@^0.6.2: +negotiator@^0.6.2, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== @@ -12800,6 +13076,11 @@ nock@12.0.3: lodash "^4.17.13" propagate "^2.0.0" +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + node-emoji@^1.10.0: version "1.11.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" @@ -12819,10 +13100,17 @@ node-forge@^1.2.1, node-forge@^1.3.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build@^4.2.3: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" - integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== +node-gyp-build-optional-packages@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" + integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== + +node-gyp-build-optional-packages@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.0.tgz#25f59b09bee310dd1b14d7ce539aa7c6093109b7" + integrity sha512-Jy0ZKyeFh3QwhJT2augyHuhn0WZ15osYBKNw4U6WAp8nadAgcB60uKJ80Y7HG2OkKnTNOtiUwqNZbSKW/nkvKg== + dependencies: + detect-libc "^2.0.1" node-gyp@^8.4.1: version "8.4.1" @@ -12901,6 +13189,26 @@ node-releases@^2.0.2: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +node-sass@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-8.0.0.tgz#c80d52148db0ce88610bcf1e1d112027393c13e1" + integrity sha512-jPzqCF2/e6JXw6r3VxfIqYc8tKQdkj5Z/BDATYyG6FL6b/LuYBNFGFVhus0mthcWifHm/JzBpKAd+3eXsWeK/A== + dependencies: + async-foreach "^0.1.3" + chalk "^4.1.2" + cross-spawn "^7.0.3" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + lodash "^4.17.15" + make-fetch-happen "^10.0.4" + meow "^9.0.0" + nan "^2.17.0" + node-gyp "^8.4.1" + sass-graph "^4.0.1" + stdout-stream "^1.4.0" + "true-case-path" "^2.2.1" + node-stream-zip@^1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" @@ -13264,10 +13572,10 @@ ora@^4.0.4: strip-ansi "^6.0.0" wcwidth "^1.0.1" -ordered-binary@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.2.4.tgz#51d3a03af078a0bdba6c7bc8f4fedd1f5d45d83e" - integrity sha512-A/csN0d3n+igxBPfUrjbV5GC69LWj2pjZzAAeeHXLukQ4+fytfP4T1Lg0ju7MSPSwq7KtHkGaiwO8URZN5IpLg== +ordered-binary@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389" + integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ== ordered-read-streams@^1.0.0: version "1.0.1" @@ -14191,11 +14499,6 @@ randexp@0.4.6: discontinuous-range "1.0.0" ret "~0.1.10" -random-poly-fill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/random-poly-fill/-/random-poly-fill-1.0.1.tgz#13634dc0255a31ecf85d4a182d92c40f9bbcf5ed" - integrity sha512-bMOL0hLfrNs52+EHtIPIXxn2PxYwXb0qjnKruTjXiM/sKfYqj506aB2plFwWW1HN+ri724bAVVGparh4AtlJKw== - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -14224,15 +14527,6 @@ re-reselect@^3.4.0: resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-3.4.0.tgz#0f2303f3c84394f57f0cd31fea08a1ca4840a7cd" integrity sha512-JsecfN+JlckncVXTWFWjn0Vk6uInl8GSf4eEd9tTk5qXHlgqkPdILpnYpgZcISXNYAzvfvsCZviaDk8AxyS5sg== -re2@1.17.4: - version "1.17.4" - resolved "https://registry.yarnpkg.com/re2/-/re2-1.17.4.tgz#7bf29290bdde963014e77bd2c2e799a6d788386e" - integrity sha512-xyZ4h5PqE8I9tAxTh3G0UttcK5ufrcUxReFjGzfX61vtanNbS1XZHjnwRSyPcLgChI4KLxVgOT/ioZXnUAdoTA== - dependencies: - install-artifact-from-github "^1.3.0" - nan "^2.15.0" - node-gyp "^8.4.1" - react-ace@^7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-7.0.5.tgz#798299fd52ddf3a3dcc92afc5865538463544f01" @@ -14979,7 +15273,7 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-in-the-middle@^5.0.2, require-in-the-middle@^5.0.3: +require-in-the-middle@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz#b768f800377b47526d026bbf5a7f727f16eb412f" integrity sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ== @@ -15087,12 +15381,12 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.5.0, resolve@^1.7.1, resolve@^1.9.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.5.0, resolve@^1.7.1, resolve@^1.9.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: - is-core-module "^2.8.1" + is-core-module "^2.9.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -15283,10 +15577,20 @@ safefs@^6.12.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@^10.2.0: - version "10.2.1" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.2.1.tgz#17e51df313f1a7a203889ce8ff91be362651276e" - integrity sha512-RRvWl+3K2LSMezIsd008ErK4rk6CulIMSwrcc2aZvjymUgKo/vjXGp1rSWmfTUX7bblEOz8tst4wBwWtCGBqKA== +sass-graph@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-4.0.1.tgz#2ff8ca477224d694055bf4093f414cf6cfad1d2e" + integrity sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA== + dependencies: + glob "^7.0.0" + lodash "^4.17.11" + scss-tokenizer "^0.4.3" + yargs "^17.2.1" + +sass-loader@^10.4.1: + version "10.4.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.4.1.tgz#bea4e173ddf512c9d7f53e9ec686186146807cbf" + integrity sha512-aX/iJZTTpNUNx/OSYzo2KsjIUQHqvWsAhhUijFjAPdZTEhstjZI9zTNvkTTwsx+uNUJqUwOw5gacxQMx4hJxGQ== dependencies: klona "^2.0.4" loader-utils "^2.0.0" @@ -15294,13 +15598,6 @@ sass-loader@^10.2.0: schema-utils "^3.0.0" semver "^7.3.2" -sass@~1.26.11: - version "1.26.12" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.12.tgz#79eddaa1773fff32ccf19e00d1ce380fc2afc7d0" - integrity sha512-hmSwtBOWoS9zwe0yAS+QmaseVCUELiGV22gXHDR7+9stEsVuEuxfY1GhC8XmUpC+Ir3Hwq7NxSUNbnmkznnF7g== - dependencies: - chokidar ">=2.0.0 <4.0.0" - sax@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -15358,6 +15655,14 @@ screenfull@^5.0.0: resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== +scss-tokenizer@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz#1058400ee7d814d71049c29923d2b25e61dc026c" + integrity sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw== + dependencies: + js-base64 "^2.4.9" + source-map "^0.7.3" + secure-json-parse@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.4.0.tgz#5aaeaaef85c7a417f76271a4f5b0cc3315ddca85" @@ -15440,15 +15745,13 @@ set-immediate-shim@~1.0.1: resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== +set-value@^2.0.0, set-value@^2.0.1, set-value@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-4.1.0.tgz#aa433662d87081b75ad88a4743bd450f044e7d09" + integrity sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw== dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" + is-plain-object "^2.0.4" + is-primitive "^3.0.1" setimmediate@^1.0.4: version "1.0.5" @@ -15561,11 +15864,6 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg== - slash@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" @@ -15648,6 +15946,15 @@ socks-proxy-agent@^6.0.0: debug "^4.3.3" socks "^2.6.2" +socks-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" + integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + socks@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" @@ -15863,7 +16170,7 @@ split-on-first@^1.0.0: resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== -split-string@^3.0.1, split-string@^3.0.2: +split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== @@ -15907,6 +16214,13 @@ ssri@^8.0.0, ssri@^8.0.1: dependencies: minipass "^3.1.1" +ssri@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" + integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== + dependencies: + minipass "^3.1.1" + stack-generator@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36" @@ -15961,6 +16275,13 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +stdout-stream@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" + integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA== + dependencies: + readable-stream "^2.0.1" + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -16551,7 +16872,7 @@ tar@6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: +tar@^6.0.2, tar@^6.1.11: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== @@ -16563,6 +16884,18 @@ tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^6.1.2: + version "6.1.14" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.14.tgz#e87926bec1cfe7c9e783a77a79f3e81c1cfa3b66" + integrity sha512-piERznXu0U7/pW7cdSn7hjqySIVTYT6F76icmFk7ptU7dDYlXTm5r9A6K04R2vU3olYgoKeo1Cg3eeu5nhftAw== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tcp-port-used@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" @@ -16593,21 +16926,6 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - terser-webpack-plugin@^2.1.2: version "2.3.8" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz#894764a19b0743f2f704e7c2a848c5283a696724" @@ -16623,6 +16941,22 @@ terser-webpack-plugin@^2.1.2: terser "^4.6.12" webpack-sources "^1.4.3" +"terser-webpack-plugin@npm:@amoo-miki/terser-webpack-plugin@1.4.5-rc.2": + version "1.4.5-rc.2" + resolved "https://registry.yarnpkg.com/@amoo-miki/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5-rc.2.tgz#046c062ef22c126c2544718674bc6624e3651b9c" + integrity sha512-JFSGSzsWgSHEqQXlnHDh3gw+jdVdVlWM2Irdps9P/yWYNY/5VjuG8sdoW4mbuP8/HM893/k8N+ipbeqsd8/xpA== + dependencies: + "@node-rs/xxhash" "^1.3.0" + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^4.0.0" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + terser@^4.1.2, terser@^4.6.12: version "4.8.1" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" @@ -16888,13 +17222,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -traceparent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/traceparent/-/traceparent-1.0.0.tgz#9b14445cdfe5c19f023f1c04d249c3d8e003a5ce" - integrity sha512-b/hAbgx57pANQ6cg2eBguY3oxD6FGVLI1CC2qoi01RmHR7AYpQHPXTig9FkzbWohEsVuHENZHP09aXuw3/LM+w== - dependencies: - random-poly-fill "^1.0.1" - traverse@^0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" @@ -16930,6 +17257,11 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +"true-case-path@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" + integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== + ts-debounce@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ts-debounce/-/ts-debounce-3.0.0.tgz#9beedf59c04de3b5bef8ff28bd6885624df357be" @@ -17211,6 +17543,13 @@ unique-filename@^1.1.1: dependencies: unique-slug "^2.0.0" +unique-filename@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" + integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== + dependencies: + unique-slug "^3.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -17218,6 +17557,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" + integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== + dependencies: + imurmurhash "^0.1.4" + unique-stream@^2.0.2: version "2.3.1" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" @@ -18066,7 +18412,7 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -weak-lru-cache@^1.0.0: +weak-lru-cache@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19" integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw== @@ -18137,11 +18483,12 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack- source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.41.5: - version "4.46.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" - integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== +"webpack@npm:@amoo-miki/webpack@4.46.0-rc.2": + version "4.46.0-rc.2" + resolved "https://registry.yarnpkg.com/@amoo-miki/webpack/-/webpack-4.46.0-rc.2.tgz#36824597c14557a7bb0a8e13203e30275e7b02bd" + integrity sha512-Y/ZqxTHOoDF1kz3SR63Y9SZGTDUpZNNFrisTRHofWhP8QvNX3LMN+TCmEP56UfLaiLVKMcaiFjx8kFb2TgyBaQ== dependencies: + "@node-rs/xxhash" "^1.3.0" "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" "@webassemblyjs/wasm-edit" "1.9.0" @@ -18154,7 +18501,7 @@ webpack@^4.41.5: eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" - loader-utils "^1.2.3" + loader-utils "^2.0.4" memory-fs "^0.4.1" micromatch "^3.1.10" mkdirp "^0.5.3" @@ -18162,7 +18509,7 @@ webpack@^4.41.5: node-libs-browser "^2.2.1" schema-utils "^1.0.0" tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" + terser-webpack-plugin "npm:@amoo-miki/terser-webpack-plugin@1.4.5-rc.2" watchpack "^1.7.4" webpack-sources "^1.4.1" @@ -18478,15 +18825,10 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yaml@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" - integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== +yaml@^1.10.0, yaml@^2.0.0, yaml@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== yargs-parser@20.2.4: version "20.2.4" @@ -18551,6 +18893,19 @@ yargs@^15.0.2, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^17.2.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yargs@~17.6.0: version "17.6.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" @@ -18564,6 +18919,11 @@ yargs@~17.6.0: y18n "^5.0.5" yargs-parser "^21.1.1" +yarn@^1.22.19: + version "1.22.19" + resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" + integrity sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ== + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" From b05c633f905ac457ec0f66c23bb0f30c732d56e2 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Mon, 5 Jun 2023 16:44:07 -0700 Subject: [PATCH 23/37] [Feature Anywhere] Various bug fixes (#4245) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../actions/view_events_option_action.tsx | 11 +++++++---- .../view_events_flyout/components/event_vis_item.tsx | 4 +++- .../public/view_events_flyout/components/styles.scss | 3 ++- .../vis_augmenter/server/capabilities_provider.ts | 2 +- .../vis_augmenter/server/saved_objects/augment_vis.ts | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx index 975f6ac0cc4c..bb8774306d47 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx @@ -5,8 +5,7 @@ import { i18n } from '@osd/i18n'; import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { get } from 'lodash'; -import { CoreStart } from 'opensearch-dashboards/public'; +import { get, isEmpty } from 'lodash'; import { VisualizeEmbeddable } from '../../../../visualizations/public'; import { EmbeddableContext } from '../../../../embeddable/public'; import { Action, IncompatibleActionError } from '../../../../ui_actions/public'; @@ -24,7 +23,7 @@ export class ViewEventsOptionAction implements Action<EmbeddableContext> { constructor() {} public getIconType(): EuiIconType { - return 'apmTrace'; + return 'inspect'; } public getDisplayName() { @@ -35,7 +34,11 @@ export class ViewEventsOptionAction implements Action<EmbeddableContext> { public async isCompatible({ embeddable }: EmbeddableContext) { const vis = (embeddable as VisualizeEmbeddable).vis; - return vis !== undefined && isEligibleForVisLayers(vis); + return ( + vis !== undefined && + isEligibleForVisLayers(vis) && + !isEmpty((embeddable as VisualizeEmbeddable).visLayers) + ); } public async execute({ embeddable }: EmbeddableContext) { diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx index a1ee965bb20f..cc00610c4a33 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/event_vis_item.tsx @@ -32,7 +32,9 @@ export function EventVisItem(props: Props) { > <EuiFlexGroup alignItems="center"> <EuiFlexItem> - <EuiLink href={`${baseUrl.prepend(`${urlPath}`)}`}>{name}</EuiLink> + <EuiLink href={`${baseUrl.prepend(`${urlPath}`)}`} target="_blank"> + {name} + </EuiLink> </EuiFlexItem> <EventVisItemIcon visLayer={props.item.visLayer} /> </EuiFlexGroup> diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss b/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss index 6eaf9bba01b5..03b78fc477f1 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss @@ -1,4 +1,4 @@ -$vis-description-width: 150px; +$vis-description-width: 200px; $event-vis-height: 55px; $timeline-panel-height: 100px; $content-padding-top: 110px; // Padding needed within view events flyout content to sit comfortably below flyout header @@ -22,6 +22,7 @@ $base-vis-min-height: 25vh; // Visualizations require the container to have a va &__visDescription { min-width: $vis-description-width; max-width: $vis-description-width; + word-break: break-word; } &__content { diff --git a/src/plugins/vis_augmenter/server/capabilities_provider.ts b/src/plugins/vis_augmenter/server/capabilities_provider.ts index f9c899d3a0de..db7bfc2b5393 100644 --- a/src/plugins/vis_augmenter/server/capabilities_provider.ts +++ b/src/plugins/vis_augmenter/server/capabilities_provider.ts @@ -5,7 +5,7 @@ export const capabilitiesProvider = () => ({ visAugmenter: { - show: true, + show: false, delete: true, save: true, saveQuery: true, diff --git a/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts b/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts index 1202e6168f77..52188d52998a 100644 --- a/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts +++ b/src/plugins/vis_augmenter/server/saved_objects/augment_vis.ts @@ -12,7 +12,7 @@ export const augmentVisSavedObjectType: SavedObjectsType = { management: { importableAndExportable: true, getTitle(obj) { - return obj.attributes.title; + return `augment-vis-${obj?.attributes?.originPlugin}`; }, getEditUrl(obj) { return `/management/opensearch-dashboards/objects/savedAugmentVis/${encodeURIComponent( From 6189e87631f5696e8e06c127978831aead041d14 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 8 Jun 2023 10:26:09 -0700 Subject: [PATCH 24/37] [Feature Anywhere] More bug fixes (#4251) * Fix bug of misaligned timeline chart in view events flyout Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Fix NPE in isEligibleForVisLayers() Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Add extra NPE checks Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Add regression test Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../vis_augmenter/public/utils/utils.test.ts | 16 ++++++++++++++++ src/plugins/vis_augmenter/public/utils/utils.ts | 10 +++++----- .../vis_type_vega/public/vega_view/vega_view.js | 6 +++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index 250e17e8d595..06249cc088a6 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -239,6 +239,22 @@ describe('utils', () => { } as unknown) as Vis; expect(isEligibleForVisLayers(invalidVis)).toEqual(false); }); + it('vis is ineligible with no seriesParams', async () => { + const invalidVis = ({ + params: { + type: 'pie', + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs: VALID_AGGS, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(invalidVis)).toEqual(false); + }); it('vis is ineligible with valid type and disabled setting', async () => { uiSettingsMock.get.mockImplementation((key: string) => { return key !== PLUGIN_AUGMENTATION_ENABLE_SETTING; diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index 907867630cbe..702c83a6b59c 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -11,7 +11,6 @@ import { buildExpression, ExpressionAstFunctionBuilder, } from '../../../../plugins/expressions/public'; -import { SavedObjectLoader } from '../../../../plugins/saved_objects/public'; import { ISavedAugmentVis, SavedAugmentVisLoader, @@ -28,7 +27,7 @@ export const isEligibleForVisLayers = (vis: Vis): boolean => { // Only support date histogram and ensure there is only 1 x-axis and it has to be on the bottom. // Additionally to have a valid x-axis, there needs to be a segment aggregation const hasValidXaxis = - vis.data.aggs !== undefined && + vis.data?.aggs !== undefined && vis.data.aggs?.byTypeName('date_histogram').length === 1 && vis.params.categoryAxes.length === 1 && vis.params.categoryAxes[0].position === 'bottom' && @@ -37,12 +36,13 @@ export const isEligibleForVisLayers = (vis: Vis): boolean => { // multiple supported yaxis only. If there are other aggregation types, this is not // valid for augmentation const hasCorrectAggregationCount = - vis.data.aggs !== undefined && + vis.data?.aggs !== undefined && vis.data.aggs?.bySchemaName('metric').length > 0 && vis.data.aggs?.bySchemaName('metric').length === vis.data.aggs?.aggs.length - 1; const hasOnlyLineSeries = - vis.params.seriesParams.every((seriesParam: { type: string }) => seriesParam.type === 'line') && - vis.params.type === 'line'; + vis.params?.seriesParams?.every( + (seriesParam: { type: string }) => seriesParam.type === 'line' + ) && vis.params?.type === 'line'; // Checks if the augmentation setting is enabled const config = getUISettings(); const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_view.js index cf49ed4a5470..9a1bc6bcb946 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_view.js @@ -91,7 +91,11 @@ export class VegaView extends VegaBaseView { // to align the data / events. We do this by checking if padding is needed on the left // and/or right, and adding padding based on the y axis config. left: - leftValueAxisPadding && flyoutContext === VisFlyoutContext.EVENT_VIS ? yAxisPadding : 0, + leftValueAxisPadding && + (flyoutContext === VisFlyoutContext.EVENT_VIS || + flyoutContext === VisFlyoutContext.TIMELINE_VIS) + ? yAxisPadding + : 0, right: rightValueAxisPadding && flyoutContext === VisFlyoutContext.EVENT_VIS ? yAxisPadding From bcc8f945d30c0bbefd2c72287bb46021f71c0d5b Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Fri, 9 Jun 2023 11:19:13 -0700 Subject: [PATCH 25/37] Allow plugins get vis embeddable to show in flyouts (#4250) * Allow plugins get vis embeddable to show in flyouts Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> * address comment Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --------- Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --- src/plugins/vis_augmenter/public/index.ts | 2 + .../view_events_flyout/components/index.ts | 1 + .../view_events_flyout/components/utils.tsx | 74 +++++++++++-------- .../components/view_events_flyout.tsx | 13 +++- 4 files changed, 56 insertions(+), 34 deletions(-) diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index 3fc82ecc0267..d5096b6faf1f 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -36,3 +36,5 @@ export * from './saved_augment_vis'; export * from './test_constants'; export * from './triggers'; export * from './actions'; +export { fetchVisEmbeddable } from './view_events_flyout'; +export { setUISettings } from './services'; // Needed for plugin tests related to the CRUD saved object functions diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts b/src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts index 6c933e023882..70564145711c 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/index.ts @@ -5,3 +5,4 @@ export { ViewEventsFlyout } from './view_events_flyout'; export { EventVisEmbeddablesMap, EventVisEmbeddableItem } from './types'; +export { fetchVisEmbeddable } from './utils'; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx index a103e6731c21..7e1c8900d64b 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx @@ -43,6 +43,45 @@ function getValueAxisPositions(embeddable: VisualizeEmbeddable): { left: boolean }; } +/** + * Fetching the base vis to show in the flyout, based on the saved object ID. Add constraints + * such that it is static and won't auto-refresh within the flyout. + * @param savedObjectId the saved object id of the base vis + */ +export async function fetchVisEmbeddable(savedObjectId: string): Promise<VisualizeEmbeddable> { + const embeddableVisFactory = getEmbeddable().getEmbeddableFactory('visualization'); + const contextInput = { + filters: getQueryService().filterManager.getFilters(), + query: getQueryService().queryString.getQuery(), + timeRange: getQueryService().timefilter.timefilter.getTime(), + }; + + const embeddable = (await embeddableVisFactory?.createFromSavedObject(savedObjectId, { + ...contextInput, + visAugmenterConfig: { + inFlyout: true, + flyoutContext: VisFlyoutContext.BASE_VIS, + }, + } as VisualizeInput)) as VisualizeEmbeddable | ErrorEmbeddable; + + if (embeddable instanceof ErrorEmbeddable) { + throw getErrorMessage(embeddable); + } + + embeddable.updateInput({ + // @ts-ignore + refreshConfig: { + value: 0, + pause: true, + }, + }); + + // By waiting for this to complete, embeddable.visLayers will be populated + await embeddable.populateVisLayers(); + + return embeddable; +} + /** * Fetching the base vis to show in the flyout, based on the saved object ID. Add constraints * such that it is static and won't auto-refresh within the flyout. @@ -51,44 +90,15 @@ function getValueAxisPositions(embeddable: VisualizeEmbeddable): { left: boolean * @param setVisEmbeddable custom hook used in base component * @param setErrorMessage custom hook used in base component */ -export async function fetchVisEmbeddable( +export async function fetchVisEmbeddableWithSetters( savedObjectId: string, setTimeRange: Function, setVisEmbeddable: Function, setErrorMessage: Function ): Promise<void> { - const embeddableVisFactory = getEmbeddable().getEmbeddableFactory('visualization'); try { - const contextInput = { - filters: getQueryService().filterManager.getFilters(), - query: getQueryService().queryString.getQuery(), - timeRange: getQueryService().timefilter.timefilter.getTime(), - }; - setTimeRange(contextInput.timeRange); - - const embeddable = (await embeddableVisFactory?.createFromSavedObject(savedObjectId, { - ...contextInput, - visAugmenterConfig: { - inFlyout: true, - flyoutContext: VisFlyoutContext.BASE_VIS, - }, - } as VisualizeInput)) as VisualizeEmbeddable | ErrorEmbeddable; - - if (embeddable instanceof ErrorEmbeddable) { - throw getErrorMessage(embeddable); - } - - embeddable.updateInput({ - // @ts-ignore - refreshConfig: { - value: 0, - pause: true, - }, - }); - - // By waiting for this to complete, embeddable.visLayers will be populated - await embeddable.populateVisLayers(); - + const embeddable = await fetchVisEmbeddable(savedObjectId); + setTimeRange(getQueryService().timefilter.timefilter.getTime()); setVisEmbeddable(embeddable); } catch (err: any) { setErrorMessage(String(err)); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx index c1b5ffda024f..96a9c66ac0a4 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx @@ -22,7 +22,11 @@ import { LoadingFlyoutBody } from './loading_flyout_body'; import { ErrorFlyoutBody } from './error_flyout_body'; import { EventsPanel } from './events_panel'; import { TimelinePanel } from './timeline_panel'; -import { fetchVisEmbeddable, createEventEmbeddables, createTimelineEmbeddable } from './utils'; +import { + fetchVisEmbeddableWithSetters, + createEventEmbeddables, + createTimelineEmbeddable, +} from './utils'; import { EventVisEmbeddablesMap } from './types'; interface Props { @@ -56,7 +60,12 @@ export function ViewEventsFlyout(props: Props) { } useEffect(() => { - fetchVisEmbeddable(props.savedObjectId, setTimeRange, setVisEmbeddable, setErrorMessage); + fetchVisEmbeddableWithSetters( + props.savedObjectId, + setTimeRange, + setVisEmbeddable, + setErrorMessage + ); // adding all of the values to the deps array cause a circular re-render. we don't want // to keep re-fetching the visEmbeddable after it is set. /* eslint-disable react-hooks/exhaustive-deps */ From 1009efeb190e732f50db06dddef3612dd63af143 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 15 Jun 2023 11:05:09 -0700 Subject: [PATCH 26/37] [Feature Anywhere] More bug fixes (#4269) * Change event color; fix UT Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Add empty seriesParams check in isEligibleForVisLayers Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../plugin_resource_delete_action.test.ts | 15 +++++++++++++++ src/plugins/vis_augmenter/public/constants.ts | 2 +- src/plugins/vis_augmenter/public/mocks.ts | 1 + .../vis_augmenter/public/test_constants.ts | 2 +- src/plugins/vis_augmenter/public/utils/utils.ts | 4 +++- .../actions/view_events_option_action.test.ts | 17 +++++++++++++++++ 6 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts b/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts index 0060b7837f1f..907b383c60a0 100644 --- a/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts +++ b/src/plugins/vis_augmenter/public/actions/plugin_resource_delete_action.test.ts @@ -7,6 +7,21 @@ import { createPointInTimeEventsVisLayer } from '../mocks'; import { generateAugmentVisSavedObject } from '../saved_augment_vis'; import { PluginResourceDeleteAction } from './plugin_resource_delete_action'; +jest.mock('src/plugins/vis_augmenter/public/services.ts', () => { + return { + getSavedAugmentVisLoader: () => { + return { + delete: () => {}, + findAll: () => { + return { + hits: [], + }; + }, + }; + }, + }; +}); + const sampleSavedObj = generateAugmentVisSavedObject( 'test-id', { diff --git a/src/plugins/vis_augmenter/public/constants.ts b/src/plugins/vis_augmenter/public/constants.ts index cd9adbaf0063..9d422cf743a5 100644 --- a/src/plugins/vis_augmenter/public/constants.ts +++ b/src/plugins/vis_augmenter/public/constants.ts @@ -4,7 +4,7 @@ */ export const VIS_LAYER_COLUMN_TYPE = 'vis_layer'; -export const EVENT_COLOR = 'red'; +export const EVENT_COLOR = '#D36086'; // This is the value of ouiColorVis2 - we need the raw value so that vega can understand it export const HOVER_PARAM = 'HOVER'; export const EVENT_MARK_SIZE = 40; export const EVENT_MARK_SIZE_ENLARGED = 80; diff --git a/src/plugins/vis_augmenter/public/mocks.ts b/src/plugins/vis_augmenter/public/mocks.ts index d3ff8422287d..33a6c3868e61 100644 --- a/src/plugins/vis_augmenter/public/mocks.ts +++ b/src/plugins/vis_augmenter/public/mocks.ts @@ -148,6 +148,7 @@ export const createMockVisEmbeddable = ( mockVisualizeInput ); mockVisEmbeddable.getTitle = () => title; + mockVisEmbeddable.visLayers = [createPointInTimeEventsVisLayer()]; return mockVisEmbeddable; }; diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index c616897d095f..435116078487 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -480,7 +480,7 @@ export const TEST_VIS_LAYERS_MULTIPLE = [ ]; const TEST_RULE_LAYER_SINGLE_VIS_LAYER = { - mark: { type: 'rule', color: 'red', opacity: 1 }, + mark: { type: 'rule', color: EVENT_COLOR, opacity: 1 }, transform: [{ filter: `datum['${TEST_PLUGIN_EVENT_TYPE}'] > 0` }], encoding: { x: { field: TEST_X_AXIS_ID, type: 'temporal' }, diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index 702c83a6b59c..ef643d87c21e 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -40,9 +40,11 @@ export const isEligibleForVisLayers = (vis: Vis): boolean => { vis.data.aggs?.bySchemaName('metric').length > 0 && vis.data.aggs?.bySchemaName('metric').length === vis.data.aggs?.aggs.length - 1; const hasOnlyLineSeries = + vis.params?.seriesParams !== undefined && vis.params?.seriesParams?.every( (seriesParam: { type: string }) => seriesParam.type === 'line' - ) && vis.params?.type === 'line'; + ) && + vis.params?.type === 'line'; // Checks if the augmentation setting is enabled const config = getUISettings(); const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts index 451087e48a15..cabca0f0dcd7 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.test.ts @@ -47,6 +47,16 @@ jest.mock('src/plugins/vis_augmenter/public/services.ts', () => { }, }; }, + getSavedAugmentVisLoader: () => { + return { + delete: () => {}, + findAll: () => { + return { + hits: [], + }; + }, + }; + }, }; }); @@ -67,6 +77,13 @@ describe('ViewEventsOptionAction', () => { expect(await action.isCompatible({ embeddable: visEmbeddable })).toBe(false); }); + it('is incompatible with VisualizeEmbeddable with valid vis and no vislayers', async () => { + const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); + visEmbeddable.visLayers = []; + const action = new ViewEventsOptionAction(); + expect(await action.isCompatible({ embeddable: visEmbeddable })).toBe(false); + }); + it('is compatible with VisualizeEmbeddable with valid vis', async () => { const visEmbeddable = createMockVisEmbeddable('test-saved-obj-id', 'test-title'); const action = new ViewEventsOptionAction(); From 83e2ff9fca1927ac7fc36b10b1f6759d49a05170 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 15 Jun 2023 13:44:40 -0700 Subject: [PATCH 27/37] [Feature Anywhere] Fix bug of tooltip showing 'undefined' in certain cases (#4281) * Fix undefined tooltip bug when multiple resource types Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Dont overwrite to 0 by default Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Fix to not hardcode only overriding index 0 in table Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> Co-authored-by: Ashish Agrawal <ashish81394@gmail.com> --- .../vis_augmenter/public/test_constants.ts | 133 +++++++++++------- .../vis_augmenter/public/vega/helpers.test.ts | 14 -- .../vis_augmenter/public/vega/helpers.ts | 11 ++ 3 files changed, 93 insertions(+), 65 deletions(-) diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index 435116078487..fdd3fa4373af 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -39,17 +39,17 @@ const TEST_VALUES_SINGLE_ROW_SINGLE_VIS_LAYER = [ ]; const TEST_VALUES_ONLY_VIS_LAYERS = [ - { [TEST_X_AXIS_ID]: 0 }, + { [TEST_X_AXIS_ID]: 0, [TEST_PLUGIN_EVENT_TYPE]: 0 }, { [TEST_X_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 2 }, - { [TEST_X_AXIS_ID]: 10 }, - { [TEST_X_AXIS_ID]: 15 }, - { [TEST_X_AXIS_ID]: 20 }, - { [TEST_X_AXIS_ID]: 25 }, - { [TEST_X_AXIS_ID]: 30 }, + { [TEST_X_AXIS_ID]: 10, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 15, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 20, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 25, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 30, [TEST_PLUGIN_EVENT_TYPE]: 0 }, { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 1 }, - { [TEST_X_AXIS_ID]: 40 }, - { [TEST_X_AXIS_ID]: 45 }, - { [TEST_X_AXIS_ID]: 50 }, + { [TEST_X_AXIS_ID]: 40, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 45, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 50, [TEST_PLUGIN_EVENT_TYPE]: 0 }, ]; const TEST_VALUES_NO_VIS_LAYERS = [ @@ -66,6 +66,20 @@ const TEST_VALUES_NO_VIS_LAYERS = [ { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5 }, ]; +const TEST_VALUES_SINGLE_VIS_LAYER_EMPTY = [ + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 25, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 30, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 40, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 0 }, +]; + const TEST_VALUES_NO_VIS_LAYERS_DIRTY = [ { [TEST_X_AXIS_ID_DIRTY]: 0, [TEST_VALUE_AXIS_ID_DIRTY]: 5 }, { [TEST_X_AXIS_ID_DIRTY]: 5, [TEST_VALUE_AXIS_ID_DIRTY]: 10 }, @@ -81,50 +95,80 @@ const TEST_VALUES_NO_VIS_LAYERS_DIRTY = [ ]; const TEST_VALUES_SINGLE_VIS_LAYER = [ - { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 0 }, { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_EVENT_TYPE]: 2 }, - { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, - { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4 }, - { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, - { [TEST_X_AXIS_ID]: 25 }, - { [TEST_X_AXIS_ID]: 30 }, + { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 25, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 30, [TEST_PLUGIN_EVENT_TYPE]: 0 }, { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 1 }, - { [TEST_X_AXIS_ID]: 40 }, - { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, - { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5 }, + { [TEST_X_AXIS_ID]: 40, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 0 }, ]; const TEST_VALUES_SINGLE_VIS_LAYER_ON_BOUNDS = [ { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 2 }, - { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10 }, - { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, - { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4 }, - { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, - { [TEST_X_AXIS_ID]: 25 }, - { [TEST_X_AXIS_ID]: 30 }, - { [TEST_X_AXIS_ID]: 35 }, - { [TEST_X_AXIS_ID]: 40 }, - { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, + { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 25, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 30, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 40, [TEST_PLUGIN_EVENT_TYPE]: 0 }, + { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3, [TEST_PLUGIN_EVENT_TYPE]: 0 }, { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE]: 1 }, ]; const TEST_VALUES_MULTIPLE_VIS_LAYERS = [ - { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, + { + [TEST_X_AXIS_ID]: 0, + [TEST_VALUE_AXIS_ID]: 5, + [TEST_PLUGIN_EVENT_TYPE]: 0, + [TEST_PLUGIN_EVENT_TYPE_2]: 0, + }, { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_EVENT_TYPE]: 2, [TEST_PLUGIN_EVENT_TYPE_2]: 1, }, - { [TEST_X_AXIS_ID]: 10, [TEST_VALUE_AXIS_ID]: 6 }, - { [TEST_X_AXIS_ID]: 15, [TEST_VALUE_AXIS_ID]: 4, [TEST_PLUGIN_EVENT_TYPE_2]: 1 }, - { [TEST_X_AXIS_ID]: 20, [TEST_VALUE_AXIS_ID]: 5 }, - { [TEST_X_AXIS_ID]: 25 }, - { [TEST_X_AXIS_ID]: 30 }, - { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 1 }, - { [TEST_X_AXIS_ID]: 40 }, - { [TEST_X_AXIS_ID]: 45, [TEST_VALUE_AXIS_ID]: 3 }, - { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5, [TEST_PLUGIN_EVENT_TYPE_2]: 2 }, + { + [TEST_X_AXIS_ID]: 10, + [TEST_VALUE_AXIS_ID]: 6, + [TEST_PLUGIN_EVENT_TYPE]: 0, + [TEST_PLUGIN_EVENT_TYPE_2]: 0, + }, + { + [TEST_X_AXIS_ID]: 15, + [TEST_VALUE_AXIS_ID]: 4, + [TEST_PLUGIN_EVENT_TYPE]: 0, + [TEST_PLUGIN_EVENT_TYPE_2]: 1, + }, + { + [TEST_X_AXIS_ID]: 20, + [TEST_VALUE_AXIS_ID]: 5, + [TEST_PLUGIN_EVENT_TYPE]: 0, + [TEST_PLUGIN_EVENT_TYPE_2]: 0, + }, + { [TEST_X_AXIS_ID]: 25, [TEST_PLUGIN_EVENT_TYPE]: 0, [TEST_PLUGIN_EVENT_TYPE_2]: 0 }, + { [TEST_X_AXIS_ID]: 30, [TEST_PLUGIN_EVENT_TYPE]: 0, [TEST_PLUGIN_EVENT_TYPE_2]: 0 }, + { [TEST_X_AXIS_ID]: 35, [TEST_PLUGIN_EVENT_TYPE]: 1, [TEST_PLUGIN_EVENT_TYPE_2]: 0 }, + { [TEST_X_AXIS_ID]: 40, [TEST_PLUGIN_EVENT_TYPE]: 0, [TEST_PLUGIN_EVENT_TYPE_2]: 0 }, + { + [TEST_X_AXIS_ID]: 45, + [TEST_VALUE_AXIS_ID]: 3, + [TEST_PLUGIN_EVENT_TYPE]: 0, + [TEST_PLUGIN_EVENT_TYPE_2]: 0, + }, + { + [TEST_X_AXIS_ID]: 50, + [TEST_VALUE_AXIS_ID]: 5, + [TEST_PLUGIN_EVENT_TYPE]: 0, + [TEST_PLUGIN_EVENT_TYPE_2]: 2, + }, ]; export const TEST_COLUMNS_NO_VIS_LAYERS = [ @@ -204,6 +248,7 @@ export const TEST_DATATABLE_NO_VIS_LAYERS_DIRTY = { export const TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY = { ...TEST_DATATABLE_NO_VIS_LAYERS, columns: TEST_COLUMNS_SINGLE_VIS_LAYER, + rows: TEST_VALUES_SINGLE_VIS_LAYER_EMPTY, } as OpenSearchDashboardsDatatable; export const TEST_DATATABLE_SINGLE_VIS_LAYER = { @@ -390,20 +435,6 @@ export const TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS = [ }, ]; -export const TEST_VIS_LAYERS_SINGLE_EMPTY_EVENTS = [ - { - originPlugin: TEST_PLUGIN, - type: VisLayerTypes.PointInTimeEvents, - pluginResource: { - type: TEST_PLUGIN_RESOURCE_TYPE, - id: TEST_PLUGIN_RESOURCE_ID, - name: TEST_PLUGIN_RESOURCE_NAME, - urlPath: TEST_PLUGIN_RESOURCE_PATH, - }, - pluginEventType: TEST_PLUGIN_EVENT_TYPE, - }, -]; - export const TEST_VIS_LAYERS_SINGLE_ON_BOUNDS = [ { originPlugin: TEST_PLUGIN, diff --git a/src/plugins/vis_augmenter/public/vega/helpers.test.ts b/src/plugins/vis_augmenter/public/vega/helpers.test.ts index f0df25a81546..ce5c68075afe 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.test.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.test.ts @@ -38,7 +38,6 @@ import { TEST_SPEC_SINGLE_VIS_LAYER, TEST_VIS_LAYERS_MULTIPLE, TEST_VIS_LAYERS_SINGLE, - TEST_VIS_LAYERS_SINGLE_EMPTY_EVENTS, TEST_VIS_LAYERS_SINGLE_INVALID_BOUNDS, TEST_VIS_LAYERS_SINGLE_ON_BOUNDS, } from '../test_constants'; @@ -409,19 +408,6 @@ describe('helpers', function () { ) ).toStrictEqual(TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY); }); - // below case should not happen since only VisLayers with a populated - // set of events should be passed from the plugins. but, if it does - // happen, we can handle it more gracefully instead of throwing an error - it('vis layer with empty events adds nothing to datatable', function () { - expect( - addPointInTimeEventsLayersToTable( - TEST_DATATABLE_NO_VIS_LAYERS, - TEST_DIMENSIONS, - // @ts-ignore - TEST_VIS_LAYERS_SINGLE_EMPTY_EVENTS - ) - ).toStrictEqual(TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY); - }); it('vis layer with events on edge of bounds are added', function () { expect( addPointInTimeEventsLayersToTable( diff --git a/src/plugins/vis_augmenter/public/vega/helpers.ts b/src/plugins/vis_augmenter/public/vega/helpers.ts index fa2073e3bb63..846a40adf33e 100644 --- a/src/plugins/vis_augmenter/public/vega/helpers.ts +++ b/src/plugins/vis_augmenter/public/vega/helpers.ts @@ -228,6 +228,17 @@ export const addPointInTimeEventsLayersToTable = ( if (isEmpty(visLayer.events)) return; const visLayerColumnId = `${visLayer.pluginEventType}`; + // Add placeholder values of 0 for every event value. This is so the tooltip + // can render correctly without showing the 'undefined' string + let row = 0; + while (row < augmentedTable.rows.length) { + augmentedTable.rows[row] = { + ...augmentedTable.rows[row], + [visLayerColumnId]: get(augmentedTable.rows[row], visLayerColumnId, 0) as number, + }; + row++; + } + // if only one row / one datapoint, put all events into this bucket if (augmentedTable.rows.length === 1) { augmentedTable.rows[0] = { From 2362c35232808cabc4162df526e32ae4fd2f2d50 Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Mon, 19 Jun 2023 07:29:43 -0700 Subject: [PATCH 28/37] Merge main changes to Feature-Anywhere branch (#4295) * Fix header icon (#3910) (#3915) * fixes header change * Update src/core/public/chrome/ui/header/header_help_menu.tsx * fixes snapshots --------- (cherry picked from commit 3cca08851cb1527705c60bbb4202d50174f034af) Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> * Add server side private IP blocking for data source endpoints validation (#3912) Signed-off-by: Kristen Tian <tyarong@amazon.com> * Docs (Jest): Update jest documentation links (#3931) Signed-off-by: Josh Romero <rmerqg@amazon.com> * Revert "[CCI] Replace jquery usage in console plugin with native methods (#3733)" (#3929) This reverts commit ffe455615eef10d1719884fd131f6953976583ff. * [BUG][Dashboard listing] push to history if dashboard otherwise nav (#3922) History push will just to the current route. However, dashboardsProvider was implemented with the expectation that it was a different app. So when a plugin registered it was attempting to navigate to `app/dashboard#/app/{url}` Add tests and extra data test subject. Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * remove jquery console release note for https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3929 revert (#3930) Signed-off-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Ashwin P Chandran <ashwinpc@amazon.com> * [CCI] Update js-yaml to v4.0.5 (#3770) * Update js-yaml to 4.0.5 (#3659) * Update CHANGELOG.md (#3659) Co-authored-by: Sergey Myssak <sergey.myssak@gmail.com> Signed-off-by: Andrey Myssak <andreymyssak@gmail.com> --------- Signed-off-by: Andrey Myssak <andreymyssak@gmail.com> Signed-off-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Sergey Myssak <sergey.myssak@gmail.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> * Update README.md (#3788) * Update README.md Signed-off-by: Melissa Vagi <vagimeli@amazon.com> * Update README.md Co-authored-by: Miki <amoo_miki@yahoo.com> --------- Signed-off-by: Melissa Vagi <vagimeli@amazon.com> Co-authored-by: Miki <miki@amazon.com> Co-authored-by: Miki <amoo_miki@yahoo.com> * Bump yaml to 2.2.2 (#3947) Signed-off-by: Manasvini B Suryanarayana <manasvis@amazon.com> Co-authored-by: Sean Neumann <1413295+seanneumann@users.noreply.github.com> * Bump `joi` to v14 to avoid the possibility of prototype poisoning in a nested dependency (#3952) Signed-off-by: Miki <miki@amazon.com> * [Doc] Add communication guide (#3837) * docs(COMMUNICATION): Add communication guide with info on slack, forum, and developer office hours link from README, CONTRIBUTING, DEVELOPER_GUIDE Signed-off-by: Josh Romero <rmerqg@amazon.com> --------- Signed-off-by: Josh Romero <rmerqg@amazon.com> * Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests (#3976) The latest version of chromedriver is 112.0.1 which does not support node 14. This PR hardcodes chromedriver to 112.0.0 temporarily. Pls revert it once we bump to node 18. Issue Resolved https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3975 Signed-off-by: ananzh <ananzh@amazon.com> * Fix wording and duplicate code in embeddable example plugin (#3911) * Fix wording and duplicate code in embeddable example plugin Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> * Fix some wording in the embeddable readme Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> --------- Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> * [CI] setup Chrome and utilize binary path (#3997) Within the CI, the virtual runner that we are utilizing has Chrome installed already. The version of Chrome is installed periodically. The most recent version of Chrome requires updates to dependencies that drop support for Node 14. This downloads chrome in the CI and then checks the chromedriver from the environment variable `TEST_BROWSER_BINARY_PATH`. Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * [Dashboards listing] fix listing limit (#4021) Initial page size was passed to the search function instead of the listing limit causing the max amount received to be significantly less than the previously implementation. Saved objects per page is `20` by default and the listing limit per page is `1000` by default. Issue: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4017 Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * [CCI] Fix EUI/OUI type errors (#3798) * Update find_test_subject imports for tests Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Update to available imports for findTestSubject Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Fix available import for Query and custom icon Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Add changelog entry Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Add ts-ignore Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> --------- Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> Co-authored-by: Qingyang(Abby) Hu <abigailhu2000@gmail.com> * Fix bottom bar visibility using create portal (#3336) (#3978) Signed-off-by: Sergey Myssak <sergey.myssak@gmail.com> Co-authored-by: Andrey Myssak <andreymyssak@gmail.com> * Adds threshold to code coverage changes for project (#4040) * Fixes code coverage workflow failures for the project test due to inderect flakey changes Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Adds changelog Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> --------- Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Updates PR template for screenshots and test instructions (#4042) Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Replace re2 with RegExp in timeline and add unit tests (#3908) Remove re2 usage and replace it with JavaScript built-in RegExp object. Also add more unit tests to make sure that using RegExp has same expressions as using re2 library. Issue Resolve https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3901 Signed-off-by: Anan Zhuang <ananzh@amazon.com> * [Console] [CCI] Remove unused ul element and its custom styling. (#3993) * remove unused ul element Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> * Update CHANGELOG.md Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> --------- Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> * Add 1.3.10 release note (#4060) (#4063) * Add release note for 1.3.10 * Address comments and add one CVE PR --------- (cherry picked from commit 43715871f3a9fc9b2a0fcac765805a33b4b305d5) Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [Multiple Datasource] Support Amazon OpenSearch Serverless (#3957) * [Multiple Datasource]Support Amazon OpenSearch Serverless in SigV4 * remove experimental text in yml * Refactor create data source form for authentication Signed-off-by: Su <szhongna@amazon.com> * Remove Sass from `tile_map` plugin (#4110) * Remove Sass from tile_map plugin Signed-off-by: Matt Provost <provomat@amazon.com> * Update changelog Signed-off-by: Matt Provost <provomat@amazon.com> --------- Signed-off-by: Matt Provost <provomat@amazon.com> * Design for New Saved Object Service Interface for Custom Repository (#3954) * Adds design document for new saved object service interface for custom repository Signed-off-by: Bandini Bhopi <bandinib@amazon.com> * enhance grouping for context menu options (#3924) * enhance grouping for context menu options * build panels tests and more comments Signed-off-by: David Sinclair <dsincla@rei.com> --------- Signed-off-by: David Sinclair <david@sinclair.tech> Signed-off-by: David Sinclair <dsincla@rei.com> Signed-off-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> * Adding Tao and Zilong to MAINTAINERS (#4137) * Adding Tao and Zilong to MAINTAINERS Signed-off-by: Yan Zeng <zengyan@amazon.com> * [MD]Update data-test-subj for functional tests & fix bug in edit flow (#4126) Signed-off-by: Su <szhongna@amazon.com> * Add support for Node.js >=14.20.1 <19 (#4071) * Bump Node.js requirements to 18 Signed-off-by: Miki <miki@amazon.com> * Replace `lmdb-store` with `lmdb` Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Bump `elastic-apm-node` to the latest minor Signed-off-by: Miki <miki@amazon.com> * Replace webpack and plugins with a patched version that uses xxhash64 * Use `xxhash64` as the hashing algorithm of webpack * Upgrade `globby` * Remove `fibers` Signed-off-by: Miki <miki@amazon.com> * Replace `fs.rmdir` with `fs.rm` in cross-platform tests Signed-off-by: Miki <miki@amazon.com> * Increase listener limit Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Add promise-stripping serializer Signed-off-by: Miki <miki@amazon.com> * Bump heap for CI Signed-off-by: Miki <miki@amazon.com> * Correct use of fs/promises in @osd/pm Signed-off-by: Miki <miki@amazon.com> * Use fs/promise in plugin post-install cleanup Signed-off-by: Miki <miki@amazon.com> * Set the test server's host to `0.0.0.0` Signed-off-by: Miki <miki@amazon.com> * Sync `.node-version` file Signed-off-by: Miki <miki@amazon.com> * Support both `isPrimary`, for Node 18, and `isMaster`, for Node 14 Signed-off-by: Miki <miki@amazon.com> * Add types when using `isDeepStrictEqual` Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Add names to `SchemaError` to log more specific errors Signed-off-by: Miki <miki@amazon.com> * Fix failing vega visualization tests outside the CI Signed-off-by: Miki <miki@amazon.com> * Fix snapshot of errors thrown for undefined accessors Signed-off-by: Miki <miki@amazon.com> * Fix flakiness of log_rotator Signed-off-by: Miki <miki@amazon.com> * Fix asynchronous `fs` usafe in plugin discover Signed-off-by: Miki <miki@amazon.com> * Fix mocks in @osd/optimizer Signed-off-by: Miki <miki@amazon.com> * Fix memory leaks caused by setting states on unloaded components Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Bump Node in Dockerfile Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Remove the response `close` event as an indicator of the requesting finishing https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3601#issuecomment-1515110477 Signed-off-by: Miki <miki@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * [BWC] Timeout after 3 mins of waiting for OSD to be running in tests Signed-off-by: Miki <miki@amazon.com> * Make build use the same node version that tests are run against Signed-off-by: Miki <miki@amazon.com> * Make Node resolve DNS by IPv4 first * This is helpful to resolve `locahost` to `127.0.0.1` Signed-off-by: Miki <miki@amazon.com> * Standardize patterns used by plugin discovery * Enhance absolute path serialization on Windows Signed-off-by: Miki <amoo_miki@yahoo.com> * Mock fetch in SenseEditor tests Signed-off-by: Miki <amoo_miki@yahoo.com> * Restore node-sass usage to fix build performance * `sass-loader@10` is the last version that supports webpack@4 * `sass` is extremely slow when using the legacy API (`render`) and to use the "Modern API" (`compileStringAsync`), `sass-loader@13` would be needed. * The performance of `sass@10` is made acceptable only with `fibers` but that is deprecated and doesn't work on Node 18 Signed-off-by: Anan Zhuang <ananzh@amazon.com> Signed-off-by: Miki <miki@amazon.com> * Revert "[CI] setup Chrome and utilize binary path (#3997)" This reverts commit 0188d052 Signed-off-by: Miki <miki@amazon.com> * Prevent fast-fail while running functional test in CI Signed-off-by: Miki <miki@amazon.com> * Revert "Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests (#3976)" This reverts commit 5ea0cbe25223e24badfe27f79813ee313c9af0b0. Signed-off-by: Miki <miki@amazon.com> * Save Cypress results artifacts during CI Signed-off-by: Miki <miki@amazon.com> * Add missing required dependency on `set-value` * Also force all to ^4.1.0 due to a vulnerability fixed in 3.1.0. Signed-off-by: Miki <miki@amazon.com> * Prevent multiple calls to bootstrap's shutdown Signed-off-by: Miki <miki@amazon.com> * Use Node 18.16.0 in distributions * Bump jest-canvas-mock to fix failing tests * Extend Node engines versions Signed-off-by: Miki <miki@amazon.com> * Normalize test snapshots across Node 14, 16, and 18 Signed-off-by: Miki <miki@amazon.com> * Update CHANGELOG for Node.js >=14.20.1 <19 support Signed-off-by: Miki <miki@amazon.com> --------- Signed-off-by: Miki <miki@amazon.com> Signed-off-by: Miki <amoo_miki@yahoo.com> Signed-off-by: Anan Zhuang <ananzh@amazon.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> * Remove timeline application (#3971) * Remove timeline application In this PR, we made the following changes: First of all, clean out some advanced settings specific to timeline application and tests. * Remove timelion:default_rows: This setting defines the default number of rows that a new Timelion sheet should have. * Remove timelion:default_rows: This setting defines the default number of columns that a new Timelion sheet should have. * Remove timelion:showTutorial. Second, remove src/plugin/timeline completely and modify timeline vis. Third, remove all the functional tests related to timeline application. Issue resolve https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3519 https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3593 Signed-off-by: ananzh <ananzh@amazon.com> --------- Signed-off-by: Anan Zhuang <ananzh@amazon.com> Signed-off-by: ananzh <ananzh@amazon.com> * Use `exec` in the CLI shell scripts to prevent new process creation (#3955) Signed-off-by: Miki <miki@amazon.com> * chore (lychee): Add company.net to exclusion list (#4171) Signed-off-by: Josh Romero <rmerqg@amazon.com> * Bundle Node 14 as a fallback for operating systems that cannot run Node 18 (#4151) Signed-off-by: ananzh <ananzh@amazon.com> Signed-off-by: Miki <miki@amazon.com> * Refactor authentication description message (#4179) resolves https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4173 Signed-off-by: Su <szhongna@amazon.com> * [CI] skip checksum verification for cypress tests (#4188) Snapshot checksum verification caused failure in test runs: https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4187 Skipping the verification to enable the tests run as the snapshot of OpenSearch should not impact the tests. Issue: n/a Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * Adds plugin manifest config to define OpenSearch plugin dependency and verifies if it is installed (#3116) Resolves Issue -https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2799 Signed-off-by: Manasvini B Suryanarayana <manasvis@amazon.com> * [Table Visualization] Remove custom styling for text-align:center in favor of OUI utility class. (#4164) * remove custom styling in favor of oui utility class Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> * Update CHANGELOG.md Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> --------- Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> * Add new MAINTAINERS to CODEOWNERS file (#4199) * Add new code owners Signed-off-by: Tao Liu <liutaoaz@amazon.com> * modify changelog.md Signed-off-by: Tao Liu <liutaoaz@amazon.com> --------- Signed-off-by: Tao Liu <liutaoaz@amazon.com> * Add 2.8.0 release notes (#4204) * Add 2.8.0 release notes Co-authored-by: Josh Romero <rmerqg@amazon.com> Signed-off-by: Kawika Avilla <kavilla414@gmail.com> * Chore(CHANGELOG): Update with 2.7, 2.8 releases (#3890) * Chore(CHANGELOG): Update with 2.7 release * align changelog with 2.8 release notes * update 2.8 release notes * add 1.3.10 release notes to changelog --------- Signed-off-by: Josh Romero <rmerqg@amazon.com> * [Saved Object Service] Adds Repository Factory Provider (#4149) * Adds Repository Factory Provider Signed-off-by: Bandini Bhopi <bandinib@amazon.com> * add category option for context menus (#4144) * enhance grouping for context menu options Signed-off-by: David Sinclair <david@sinclair.tech> * change log Signed-off-by: David Sinclair <david@sinclair.tech> * remove type export Signed-off-by: David Sinclair <david@sinclair.tech> * revert border and prevent destroy options Signed-off-by: David Sinclair <david@sinclair.tech> * update comments for building panels Signed-off-by: David Sinclair <dsincla@rei.com> * build panels tests and more comments Signed-off-by: David Sinclair <dsincla@rei.com> * add category option for context menus Signed-off-by: David Sinclair <dsincla@rei.com> * changelog Signed-off-by: David Sinclair <dsincla@rei.com> * add order to groups Signed-off-by: David Sinclair <dsincla@rei.com> * documentation, shorter copyrighty, minor cleanup Signed-off-by: David Sinclair <dsincla@rei.com> * changelog Signed-off-by: David Sinclair <dsincla@rei.com> --------- Signed-off-by: David Sinclair <david@sinclair.tech> Signed-off-by: David Sinclair <dsincla@rei.com> Signed-off-by: Ashish Agrawal <ashish81394@gmail.com> Co-authored-by: Ashish Agrawal <ashish81394@gmail.com> * [CCI] Add bluebird replaces for src/plugins/saved_objects (#4026) * Add bluebird replaces for src/plugins/saved_objects * Add changelog entry --------- Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> * Validate and correct change log after 2.8 release (#4275) Signed-off-by: Su <szhongna@amazon.com> --------- Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Signed-off-by: Kristen Tian <tyarong@amazon.com> Signed-off-by: Josh Romero <rmerqg@amazon.com> Signed-off-by: Kawika Avilla <kavilla414@gmail.com> Signed-off-by: Andrey Myssak <andreymyssak@gmail.com> Signed-off-by: Melissa Vagi <vagimeli@amazon.com> Signed-off-by: Manasvini B Suryanarayana <manasvis@amazon.com> Signed-off-by: Miki <miki@amazon.com> Signed-off-by: ananzh <ananzh@amazon.com> Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com> Signed-off-by: Alexei Karikov <karikov.alist.ru@gmail.com> Signed-off-by: Sergey Myssak <sergey.myssak@gmail.com> Signed-off-by: Anan Zhuang <ananzh@amazon.com> Signed-off-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> Signed-off-by: Su <szhongna@amazon.com> Signed-off-by: Matt Provost <provomat@amazon.com> Signed-off-by: Bandini Bhopi <bandinib@amazon.com> Signed-off-by: David Sinclair <david@sinclair.tech> Signed-off-by: David Sinclair <dsincla@rei.com> Signed-off-by: Yan Zeng <zengyan@amazon.com> Signed-off-by: Miki <amoo_miki@yahoo.com> Signed-off-by: Tao Liu <liutaoaz@amazon.com> Signed-off-by: Ashish Agrawal <ashish81394@gmail.com> Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> Co-authored-by: opensearch-trigger-bot[bot] <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Josh Romero <rmerqg@amazon.com> Co-authored-by: Kristen Tian <105667444+kristenTian@users.noreply.github.com> Co-authored-by: Kawika Avilla <kavilla414@gmail.com> Co-authored-by: Ashwin P Chandran <ashwinpc@amazon.com> Co-authored-by: Andrey Myssak <40265277+andreymyssak@users.noreply.github.com> Co-authored-by: Sergey Myssak <sergey.myssak@gmail.com> Co-authored-by: Melissa Vagi <vagimeli@amazon.com> Co-authored-by: Miki <miki@amazon.com> Co-authored-by: Miki <amoo_miki@yahoo.com> Co-authored-by: Manasvini B Suryanarayana <manasvis@amazon.com> Co-authored-by: Sean Neumann <1413295+seanneumann@users.noreply.github.com> Co-authored-by: Anan Zhuang <ananzh@amazon.com> Co-authored-by: Qingyang(Abby) Hu <abigailhu2000@gmail.com> Co-authored-by: Alexei Karikov <karikov.alist.ru@gmail.com> Co-authored-by: Andrey Myssak <andreymyssak@gmail.com> Co-authored-by: Sirazh Gabdullin <sirazh.gabdullin@nu.edu.kz> Co-authored-by: Zhongnan Su <szhongna@amazon.com> Co-authored-by: Matt Provost <provomat@amazon.com> Co-authored-by: Bandini <63824432+bandinib-amzn@users.noreply.github.com> Co-authored-by: David Sinclair <24573542+sikhote@users.noreply.github.com> Co-authored-by: Yan Zeng <46499415+zengyan-amazon@users.noreply.github.com> Co-authored-by: Tao Liu <33105471+Flyingliuhub@users.noreply.github.com> --- .github/CODEOWNERS | 2 +- .github/workflows/cypress_workflow.yml | 7 +- CHANGELOG.md | 742 ++++++++++++------ .../context_menu_examples.tsx | 7 +- ...anel_group_options_and_context_actions.tsx | 83 ++ ...nsearch-dashboards.release-notes-1.3.10.md | 4 +- ...ensearch-dashboards.release-notes-2.8.0.md | 35 + .../public/plugins/plugins_service.test.ts | 1 + src/core/server/legacy/legacy_service.ts | 1 + .../discovery/plugin_manifest_parser.test.ts | 79 ++ .../discovery/plugin_manifest_parser.ts | 26 + .../integration_tests/plugins_service.test.ts | 3 + src/core/server/plugins/plugin.test.ts | 1 + src/core/server/plugins/plugin.ts | 2 + .../server/plugins/plugin_context.test.ts | 1 + src/core/server/plugins/plugin_context.ts | 1 + .../server/plugins/plugins_service.test.ts | 3 + .../server/plugins/plugins_system.test.ts | 77 +- src/core/server/plugins/plugins_system.ts | 34 +- src/core/server/plugins/types.ts | 6 + .../saved_objects_service.mock.ts | 1 + .../saved_objects_service.test.ts | 63 +- .../saved_objects/saved_objects_service.ts | 42 +- .../service/lib/scoped_client_provider.ts | 25 +- .../public/saved_object/saved_object.test.ts | 19 +- src/plugins/ui_actions/README.md | 9 + .../build_eui_context_menu_panels.test.ts | 120 +++ .../build_eui_context_menu_panels.tsx | 75 +- .../ui_actions/public/util/presentable.ts | 8 + .../public/components/table_vis_app.scss | 5 - .../public/components/table_vis_component.tsx | 2 +- 31 files changed, 1223 insertions(+), 261 deletions(-) create mode 100644 examples/ui_actions_explorer/public/context_menu_examples/panel_group_options_and_context_actions.tsx create mode 100644 release-notes/opensearch-dashboards.release-notes-2.8.0.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7899a0afa723..9346def6aafd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @ananzh @kavilla @seanneumann @AMoo-Miki @ashwin-pc @joshuarrrr @abbyhu2000 @zengyan-amazon @kristenTian @zhongnansu @manasvinibs +* @ananzh @kavilla @seanneumann @AMoo-Miki @ashwin-pc @joshuarrrr @abbyhu2000 @zengyan-amazon @kristenTian @zhongnansu @manasvinibs @ZilongX @Flyingliuhub diff --git a/.github/workflows/cypress_workflow.yml b/.github/workflows/cypress_workflow.yml index a487ac774a7c..5e78785f9b88 100644 --- a/.github/workflows/cypress_workflow.yml +++ b/.github/workflows/cypress_workflow.yml @@ -12,7 +12,10 @@ env: START_CMD: 'node ../scripts/opensearch_dashboards --dev --no-base-path --no-watch' OPENSEARCH_SNAPSHOT_CMD: 'node ../scripts/opensearch snapshot' SPEC: 'cypress/integration/core-opensearch-dashboards/opensearch-dashboards/**/*.js,' - CYPRESS_ENV: 'env CYPRESS_VISBUILDER_ENABLED=true CYPRESS_DATASOURCE_MANAGEMENT_ENABLED=false' + CYPRESS_BROWSER: 'chromium' + CYPRESS_VISBUILDER_ENABLED: true + CYPRESS_DATASOURCE_MANAGEMENT_ENABLED: false + OSD_SNAPSHOT_SKIP_VERIFY_CHECKSUM: true jobs: cypress-tests: @@ -76,7 +79,7 @@ jobs: working-directory: ${{ env.FTR_PATH }} start: ${{ env.OPENSEARCH_SNAPSHOT_CMD }}, ${{ env.START_CMD }} wait-on: 'http://localhost:9200, http://localhost:5601' - command: ${{ env.CYPRESS_ENV }} yarn cypress:run-without-security --browser chromium --spec ${{ env.SPEC }} + command: yarn cypress:run-without-security --browser ${{ env.CYPRESS_BROWSER }} --spec ${{ env.SPEC }} # Screenshots are only captured on failure, will change this once we do visual regression tests - uses: actions/upload-artifact@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a7acdd0a965..04dc5d82b498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,298 +10,598 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### 🛡 Security -- [Legacy Maps Plugin] Prevent reverse-tabnabbing ([#2540](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2540)) -- Eliminate dependency on `got` versions older than 11.8.5 ([#2801](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2801)) -- [Multi DataSource] Add explicit no spellcheck on password fields ([#2818](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2818)) -- [CVE-2022-25912] Bumps simple-git from 3.4.0 to 3.15.0 ([#3036](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3036)) -- [CVE-2022-35256] Bumps node version from 14.20.0 to 14.20.1 ([#3166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3166)) -- [CVE-2022-46175] Bumps json5 version from 1.0.1 and 2.2.1 to 1.0.2 and 2.2.3 ([#3201](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3201)) -- [CVE-2022-25860] Bumps simple-git from 3.15.1 to 3.16.0 ([#3345](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3345)) -- [Security] Bumps hapi/statehood to 7.0.4 ([#3411](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3411)) -- [CVE-2023-25166] Bump formula to 3.0.1 ([#3416](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3416)) -- [CVE-2023-25653] Bump node-jose to 2.2.0 ([#3445](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3445)) -- [CVE-2023-26486][cve-2023-26487] Bump vega from 5.22.1 to 5.23.0 ([#3533](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3533)) -- [CVE-2023-0842] Bump xml2js from 0.4.23 to 0.5.0 ([#3842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3842)) -- [Multi DataSource] Add private IP blocking validation on server side ([#3912](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3912)) +- [CVE-2022-37599] Bump loader-utils from `2.0.3` to `2.0.4` ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)). Backwards-compatible fixes included in v2.6.0 and v1.3.7 releases. +- [CVE-2022-37603] Bump loader-utils from `2.0.3` to `2.0.4` ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)). Backwards-compatible fixes included in v2.6.0 and v1.3.7 releases. +- [WS-2021-0638] Bump mocha from `7.2.0` to `10.1.0` ([#2711](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2711)) - Bump `joi` to v14 to avoid the possibility of prototype poisoning in a nested dependency ([#3952](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3952)) -- [CVE-2023-2251] Bump yaml to 2.2.2 ([#3947](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3947)) ### 📈 Features/Enhancements -- [MD] Support legacy client for data source ([#2204](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2204)) -- [MD] Add data source signing support ([#2510](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2510)) -- [Plugin Helpers] Facilitate version changes ([#2398](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2398)) -- [MD] Display error toast for create index pattern with data source ([#2506](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2506)) -- [Multi DataSource] UX enhancement on index pattern management stack ([#2505](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2505)) -- [Multi DataSource] UX enhancement on Data source management stack ([#2521](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2521)) -- [Multi DataSource] UX enhancement on Index Pattern management stack ([#2527](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2527)) -- [Multi DataSource] Add data source column into index pattern table ([#2542](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2542)) -- [Multi DataSource] UX enhancement for Data source management creation page ([#2051](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2501)) -- [Multi DataSource] Add experimental callout for index pattern section ([#2523](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2523)) -- [Multi DataSource] Add data source config to opensearch-dashboards-docker ([#2557](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2557)) -- [Multi DataSource] Make text content dynamically translated & update unit tests ([#2570](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2570)) -- [Vis Builder] Change classname prefix wiz to vb ([#2581](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2581/files)) -- [Vis Builder] Change wizard to vis_builder in file names and paths ([#2587](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2587)) -- [Windows] Facilitate building and running OSD and plugins on Windows platforms ([#2601](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2601)) -- [Windows] Add `@osd/cross-platform` package to standardize path handling across platforms ([#2703](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2703)) -- [Multi DataSource] Address UX comments on Data source list and create page ([#2625](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2625)) -- [Vis Builder] Rename wizard to visBuilder in i18n id and formatted message id ([#2635](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2635)) -- [Vis Builder] Rename wizard to visBuilder in class name, type name and function name ([#2639](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2639)) -- [Vis Builder] Rename wizard on save modal and visualization table ([#2645](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2645)) -- [Vis Builder] Adds functional tests to CI ([#2728](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2728)) -- [Vis Builder] Enable VisBuilder by default ([#2725](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2725)) -- Change save object type, wizard id and name to visBuilder #2673 ([#2673](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2673)) -- [Multi DataSource] Update MD data source documentation link ([#2693](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2693)) -- [Save Object Aggregation View] Add extension point in saved object management to register namespaces and show filter ([#2656](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2656)) -- [Save Object Aggregation View] Fix for export all after scroll count response changed in PR#2656 ([#2696](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2696)) -- [Vis Builder] Add an experimental table visualization in vis builder ([#2705](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2705)) -- [Vis Builder] Add field summary popovers ([#2682](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2682)) -- [I18n] Register ru, ru-RU locale ([#2817](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2817)) -- Add yarn opensearch arg to setup plugin dependencies ([#2544](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2544)) -- [Multi DataSource] Test the connection to an external data source when creating or updating ([#2973](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2973)) -- Add Dashboards-list integrations for Plugins ([#3090](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3090) ) -- [Table Visualization] Refactor table visualization using React and DataGrid component ([#2863](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2863)) -- [Vis Builder] Add redux store persistence ([#3088](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3088)) -- [Multi DataSource] Improve test connection ([#3110](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3110)) -- [Vis Builder] Add app filter and query persistence without using state container ([#3100](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3100)) -- [Optimizer] Increase timeout waiting for the exiting of an optimizer worker ([#3193](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3193)) -- [Data] Update `createAggConfig` so that newly created configs can be added to beginning of `aggConfig` array ([#3160](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3160)) -- Add disablePrototypePoisoningProtection configuration to prevent JS client from erroring when cluster utilizes JS reserved words ([#2992](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2992)) -- [Multiple DataSource] Add support for SigV4 authentication ([#3058](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3058)) -- Make build scripts find and use the latest version of Node.js that satisfies `engines.node` ([#3467](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3467)) -- [Multiple DataSource] Refactor test connection to support SigV4 auth type ([#3456](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3456)) -- [Darwin] Add support for Darwin for running OpenSearch snapshots with `yarn opensearch snapshot` ([#3537](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3537)) -- [Vis Builder] Add metric to metric, bucket to bucket aggregation persistence ([#3495](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3495)) -- Use mirrors to download Node.js binaries to escape sporadic 404 errors ([#3619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3619)) -- [Multiple DataSource] Refactor dev tool console to use opensearch-js client to send requests ([#3544](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3544)) -- Add `osd-xsrf` header to all requests that incorrectly used `node-version` to satisfy XSRF protection ([#3643](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3643)) -- [Data] Add geo shape filter field ([#3605](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3605)) -- [Notifications] Adds id to toast api for deduplication ([#3752](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3752)) -- [VisBuilder] Add UI actions handler ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) -- [Dashboard] Indicate that IE is no longer supported ([#3641](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3641)) -- [UI] Add support for comma delimiters in the global filter bar ([#3686](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3686)) -- [Multiple DataSource] Allow create and distinguish index pattern with same name but from different datasources ([#3571](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3571)) -- [Multiple DataSource] Integrate multiple datasource with dev tool console ([#3754](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3754)) -- Add satisfaction survey link to help menu ([#3676] (https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3676)) -- [Vis Builder] Add persistence to visualizations inner state ([#3751](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3751)) -- [Table Visualization] Move format table, consolidate types and add unit tests ([#3397](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3397)) +- Add plugin manifest config to define OpenSearch plugin dependency and verify if it is installed on the cluster ([#3116](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3116)) +- Replace re2 with RegExp in timeline and add unit tests ([#3908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3908)) +- Add category option within groups for context menus ([#4144](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4144)) +- [Saved Object Service] Add Repository Factory Provider ([#4149](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4149)) + +### 🐛 Bug Fixes + +- [Chore] Update deprecated url methods (url.parse(), url.format()) ([#2910](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2910)) +- Cleanup unused url ([#3847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3847)) + +### 🚞 Infrastructure + +- Re-enable CI workflows for feature branches ([#2908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2908)) +- Upgrade yarn version to be compatible with @opensearch-project/opensearch ([#3443](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3443)) +- Add an achievement badger to the PR ([#3721](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3721)) + +### 📝 Documentation + +- [Doc] Add COMMUNICATIONS.md with info about Slack, forum, office hours ([#3837](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3837)) +- [Saved Object Service] Adds design doc for new Saved Object Service Interface for Custom Repository [#3954](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3954) + +### 🛠 Maintenance + +- Removes `minimatch` manual resolution ([#3019](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3019)) +- Upgrade `vega-lite` dependency from `4.17.0` to `^5.6.0` ([#3076](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3076)). Backwards-compatible version included in v2.5.0 release. +- Bump `js-yaml` from `3.14.0` to `4.1.0` ([#3770](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3770)) +- Adding @ZilongX and @Flyingliuhub as maintainers. ([#4137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4137)) +- Add new MAINTAINERS to CODEOWNERS file. ([#4199](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4199)) + +### 🪛 Refactoring + +- [Console] Remove unused ul element and its custom styling ([#3993](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3993)) +- Fix EUI/OUI type errors ([#3798](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3798)) +- Remove unused Sass in `tile_map` plugin ([#4110](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4110)) +- [Table Visualization] Remove custom styling for text-align:center in favor of OUI utility class. ([#4164](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4164)) +- Replace the use of `bluebird` in `saved_objects` plugin ([#4026](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4026)) + +### 🔩 Tests + +## [2.8.0 - 2023-06-06](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.8.0) + +### Deprecations + +- Remove timeline application ([#3971](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3971)) + +### 🛡 Security + +- [CVE-2023-2251] Bump `yaml` to `2.2.2` ([#3947](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3947)) + +### 📈 Features/Enhancements + - [Multiple Datasource] Support Amazon OpenSearch Serverless ([#3957](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3957)) - Add support for Node.js >=14.20.1 <19 ([#4071](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4071)) - Bundle Node.js 14 as a fallback for operating systems that cannot run Node.js 18 ([#4151](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4151)) +- Enhance grouping for context menus ([#3924](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3924)) + +### 🐛 Bug Fixes + +- [BUG] Fix bottom bar visibility using createPortal ([#3978](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3978)) +- [Dashboards Listing] Fix listing limit to utilize `savedObjects:listingLimit` instead of `savedObjects:perPage` ([#4021](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4021)) + +### 🚞 Infrastructure + +- Install chrome driver for functional tests from path set by environment variable `TEST_BROWSER_BINARY_PATH`([#3997](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3997)) +- Add threshold to code coverage config to prevent workflow failures ([#4040](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4040)) +- [CI] Skip checksum verification on OpenSearch snapshot for cypress tests ([#4188](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4188)) + +### 📝 Documentation + +### 🛠 Maintenance + +- Use `exec` in the CLI shell scripts to prevent new process creation ([#3955](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3955)) + +### 🪛 Refactoring + +### 🔩 Tests + +## [1.3.10 - 2023-05-18](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.7.0) + +### 🛡 Security + +- [CVE-2020-15366][1.x] Bump ajv from 4.11.8 to 6.12.6 ([#4035](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4035)) +- [CVE-2022-48285][1.x] Bump jszip from 3.7.1 to 3.10.1 ([#4011](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4011)) +- [CVE-2021-35065][1.x] Bump glob-parent from 6.0.0 to 6.0.2 ([#4005](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4005)) +- [CVE-2022-25851][1.x] Bump jpeg-js from 0.4.1 to 0.4.4 ([#3860](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3860)) +- [CVE-2022-25858][1.x] Bump terser from 4.8.0 to 4.8.1 ([#3786](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3786)) +- [CVE-2021-23490][1.x] Bump parse-link-header from 1.0.1 to 2.0.0 ([#3820](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3820)) +- [CVE-2021-3765][1.x] Bump validator from 8.2.0 to 13.9.0 ([#3753](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3753)) +- [CVE-2022-25758][1.x] Bump scss-tokenizer from 0.3.0 to 0.4.3 ([#3789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3789)) +- [CVE-2021-3803][1.x] Bump nth-check from 1.0.2 to 2.0.1 ([#3745](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3745)) +- Bump highlight.js from 9.18.5 to 10.7.3 to solve security concerns ([#4062](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4062)) + +### 📈 Features/Enhancements + +- Add tooltip to help icon ([#3872](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3872)) + +### 🐛 Bug Fixes + +### 📝 Documentation + +- Update jest documentation links ([#3939](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3939)) + +### 🛠 Maintenance + +- Add threshold to code coverage changes for project ([#4050](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4050)) +- Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests ([#4039](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4039)) +- Update MAINTAINERS.md and CODEOWNERS ([#3938](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3938)) +- Add opensearch-dashboards-docker-dev to .gitignore ([#3781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3781)) + +### 🪛 Refactoring + +### 🔩 Tests + +## [2.7.0 - 2023-05-02](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.7.0) + +### Deprecations + +### 🛡 Security + +- [CVE-2023-26486] Bump vega from `5.22.1` to `5.23.0` ([#3533](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3533)) +- [CVE-2023-26487] Bump vega from `5.22.1` to `5.23.0` ([#3533](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3533)) +- [CVE-2023-0842] Bump xml2js from `0.4.23` to `0.5.0` ([#3842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3842)) +- [Multi DataSource] Add private IP blocking validation on server side ([#3912](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3912)) + +### 📈 Features/Enhancements + +- Add satisfaction survey link to help menu ([#3676](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3676)) +- Add `osd-xsrf` header to all requests that incorrectly used `node-version` to satisfy XSRF protection ([#3643](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3643)) +- [Dashboard] Add Dashboards-list integrations for Plugins ([#3090](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3090) ) +- [Data] Add geo shape filter field ([#3605](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3605)) +- [Doc Links] Add downgrade logic for branch in DocLinkService ([#3483](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3483)) +- [Monaco editor] Add json worker support ([#3424](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3424)) +- [Multiple DataSource] Allow create and distinguish index pattern with same name but from different datasources ([#3604](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3604)) +- [Multiple DataSource] Integrate multiple datasource with dev tool console ([#3754](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3754)) +- [Notifications] Add id to toast api for deduplication ([#3752](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3752)) +- [UI] Add support for comma delimiters in the global filter bar ([#3686](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3686)) +- [UI] Indicate that IE is no longer supported ([#3641](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3641)) +- [Vega] Add Filter custom label for opensearchDashboardsAddFilter ([#3640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3640)) +- [VisBuilder] Add metric to metric, bucket to bucket aggregation persistence ([#3495](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3495)) +- [VisBuilder] Add UI actions handler ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Add persistence to visualizations inner state ([#3751](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3751)) ### 🐛 Bug Fixes -- [Vis Builder] Fixes auto bounds for timeseries bar chart visualization ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) -- [Vis Builder] Fixes visualization shift when editing agg ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) -- [Vis Builder] Renames "Histogram" to "Bar" in vis type picker ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) -- [Vis Builder] Update vislib params and misc fixes ([2610](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2610)) -- [Vis Builder] Bug fixes for datasource picker and auto time interval ([2632](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2632)) -- [MD] Add data source param to low-level search call in Discover ([#2431](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2431)) -- [Multi DataSource] Skip data source view in index pattern step when pick default ([#2574](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2574)) -- [Multi DataSource] Address UX comments on Edit Data source page ([#2629](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2629)) -- [BUG] Fix suggestion list cutoff issue ([#2607](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2607)) -- [Multi DataSource] Address UX comments on index pattern management stack ([#2611](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2611)) -- [Multi DataSource] Apply get indices error handling in step index pattern ([#2652](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2652)) -- [Vis Builder] Last Updated Timestamp for visbuilder savedobject is getting Generated ([#2628](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2628)) -- [Vis Builder] fixes filters for table visualisation ([#3210](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3210)) -- Removed Leftover X Pack references ([#2638](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2638)) -- Removes Add Integration button ([#2723](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2723)) -- Change geckodriver version to make consistency ([#2772](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2772)) -- [Multi DataSource] Update default audit log path ([#2793](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2793)) -- [Table Visualization] Fix first column sort issue ([#2828](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2828)) -- Temporary workaround for task-kill exceptions on Windows when it is passed a pid for a process that is already dead ([#2842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2842)) -- [Vis Builder] Fix empty workspace animation does not work in firefox ([#2853](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2853)) -- Bumped `del` version to fix MacOS race condition ([#2847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2873)) -- [Chore] Update deprecated url methods (url.parse(), url.format()) ([#1561](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1561)) -- [Build] Fixed "Last Access Time" not being set by `scanCopy` on Windows ([#2964](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2964)) -- [Vis Builder] Add global data persistence for vis builder #2896 ([#2896](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2896)) -- Update `leaflet-vega` and fix its usage ([#3005](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3005)) -- [Table Visualization][bug] Fix Url content display issue in table ([#2918](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2918)) -- Fixes misleading embaddable plugin error message ([#3043](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3043)) -- [MD] Update dummy url in tests to follow lychee url allowlist ([#3099](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3099)) -- Adds config override to fix obsolete theme:version config value of v8 (beta) rendering issue ([#3045](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3045)) -- [CI] Update test workflow to increase network-timeout for yarn for installing dependencies ([#3118](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3118)) -- [VisBuilder] Fixes pipeline aggs ([#3137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3137)) -- [Region Maps] Fixes bug that prevents selected join field to be used ([#3213](Fix bug that prevents selected join field to be used)) -- [Multi DataSource]Update test connection button text([#3247](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3247)) -- [Region Maps] Add ui setting to configure custom vector map's size parameter([#3399](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3399)) -- [Search Telemetry] Fixes search telemetry's observable object that won't be GC-ed([#3390](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3390)) - Clean up and rebuild `@osd/pm` ([#3570](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3570)) - Omit adding the `osd-version` header when the Fetch request is to an external origin ([#3643](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3643)) -- [Vega] Add Filter custom label for opensearchDashboardsAddFilter ([#3640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3640)) +- [Console] Fix/update documentation links in Dev Tools console ([#3724](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3724)) +- [Console] Fix dev tool console autocomplete not loading issue ([#3775](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3775)) +- [Console] Fix dev tool console run command with query parameter error ([#3813](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3813)) +- [Table Visualization] Fix table rendering empty unused space ([#3797](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3797)) +- [Table Visualization] Fix data table not adjusting height on the initial load ([#3816](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3816)) - [Timeline] Fix y-axis label color in dark mode ([#3698](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3698)) +- [TSVB] Fix undefined serial diff aggregation documentation link ([#3503](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3503)) +- [UI] Add clarifying tooltips to header navigation icons ([#3626](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3626)) - [VisBuilder] Fix multiple warnings thrown on page load ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [VisBuilder] Fix Firefox legend selection issue ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [VisBuilder] Fix type errors ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [VisBuilder] Fix indexpattern selection in filter bar ([#3751](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3751)) -- [Table Visualization] Fix table rendering empty unused space ([#3797](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3797)) -- [Table Visualization] Fix data table not adjusting height on the initial load ([#3816](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3816)) -- Cleanup unused url ([#3847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3847)) -- [BUG] Docked navigation impacts visibility of bottom bar component ([#3978](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3978)) ### 🚞 Infrastructure -- Add CHANGELOG.md and related workflows ([#2414](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2414)) -- Update backport custom branch name to utilize head template ([#2766](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2766)) -- Re-enable CI workflows for feature branckes ([#2908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2908)) -- Add Windows CI workflows ([#2966](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2966)) -- Add automatic selection of the appropriate version of chrome driver to run functional tests ([#2990](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2990)) -- Add recording of functional test artifacts if they fail ([#3190](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3190)) -- Improve yarn's performance in workflows by caching yarn's cache folder ([#3194](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3194)) -- Fix detection of Chrome's version on Darwin during CI ([#3296](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3296)) -- Upgrade yarn version to be compatible with @openearch-project/opensearch ([#3443](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3443)) +- Use mirrors to download Node.js binaries to escape sporadic 404 errors ([#3619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3619)) +- [CI] Update NOTICE file, add validation to GitHub CI ([#3051](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3051)) - [CI] Reduce redundancy by using matrix strategy on Windows and Linux workflows ([#3514](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3514)) -- Add an achievement badger to the PR ([#3721](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3721)) -- Install chrome driver for functional tests from path set by environment variable `TEST_BROWSER_BINARY_PATH`([#3997](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3997)) -- Adds threshold to code coverage config to prevent workflow failures ([#4040](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4040)) +- [Darwin] Add support for Darwin for running OpenSearch snapshots with `yarn opensearch snapshot` ([#3537](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3537)) ### 📝 Documentation -- Add the release runbook to RELEASING.md ([#2533](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2533)) -- [MD] Add design documents of multiple data source feature [#2538](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2538) -- [MD] Tweak multiple data source design doc [#2724](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2724) -- Corrected README and help command of osd-plugin-helpers ([#2810](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2810)) -- Add `current-usage.md` and more details to `README.md` of `charts` plugin ([#2695](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2695)) -- [Doc] Add readme for global query persistence ([#3001](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3001)) -- Updates NOTICE file, adds validation to GitHub CI ([#3051](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3051)) -- [Doc] Add current plugin persistence implementation readme ([#3081](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3081)) -- [Doc] Improve DEVELOPER_GUIDE to make first time setup quicker and easier ([#3421](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3421)) - Correct copyright date range of NOTICE file and notice generator ([#3308](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3308)) - Simplify the in-code instructions for upgrading `re2` ([#3328](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3328)) +- [Doc] Improve DEVELOPER_GUIDE to make first time setup quicker and easier ([#3421](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3421)) +- [Doc] Update DEVELOPER_GUIDE with added manual bootstrap timeout solution and max virtual memory error solution with docker ([#3764](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3764)) +- [Doc] Add second command to install yarn step in DEVELOPER_GUIDE ([#3633](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3633)) - [Doc] Add docker dev set up instruction ([#3444](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3444)) -- [Doc] UI actions explorer ([#3614](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3614)) -- [Doc] Update SECURITY.md with instructions for nested dependencies and backporting ([#3497](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3497)) -- [Doc] [Console] Fix/update documentation links in Dev Tools console ([#3724](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3724)) -- [Doc] Update DEVELOPER_GUIDE.md with added manual bootstrap timeout solution and max virtual memory error solution with docker ([#3764](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3764)) -- [Doc] Add COMMUNICATIONS.md with info about Slack, forum, office hours ([#3837](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3837)) - [Doc] Add docker files and instructions for debugging Selenium functional tests ([#3747](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3747)) -- [Saved Object Service] Adds design doc for new Saved Object Service Interface for Custom Repository [#3954](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3954) +- [Doc] Update SECURITY with instructions for nested dependencies and backporting ([#3497](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3497)) +- [TSVB] Fix typo in TSVB README ([#3518](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3518)) +- [UI Actions] Improve UI actions explorer ([#3614](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3614)) ### 🛠 Maintenance -- Adding @zhongnansu as maintainer. ([#2590](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2590)) -- [Timeline] Update default expressions from `.es(*)` to `.opensearch(*)`. ([2720](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2720)) -- Removes `minimatch` manual resolution ([#3019](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3019)) -- Remove `github-checks-reporter`, an unused dependency ([#3126](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3126)) -- Upgrade `vega-lite` dependency to ^5.6.0 ([#3076](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3076)) -- Bumps `re2` and `supertest` ([3018](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3018)) -- Bump `vega-tooltip` version from ^0.24.2 to ^0.30.0 ([#3358](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3358)) -- Allow relaxing the Node.js runtime version requirement ([3402](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3402)) -- Relax the Node.js requirement to `^14.20.1` ([3463](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3463)) -- Bump the version of Node.js installed by `nvm` to `14.21.3` ([3463](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3463)) -- Remove the unused `renovate.json5` file ([3489](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3489)) -- Allow selecting the Node.js binary using `NODE_HOME` and `OSD_NODE_HOME` ([3508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3508)) -- Bump `styled-components` from 5.3.5 to 5.3.9 ([#3678](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3678)) -- Bump `js-yaml` from 3.14.0 to 4.1.0 ([#3770](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3770)) -- Bump `oui` from `1.0.0` to `1.1.1` ([#3884](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3884)) -- Use `exec` in the CLI shell scripts to prevent new process creation ([#3955](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3955)) -- Adding @ZilongX and @Flyingliuhub as maintainers. ([#4137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4137)) -- Remove timeline application ([#3971](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3971)) +- Relax the Node.js requirement to `^14.20.1` ([#3463](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3463)) +- Bump the version of Node.js installed by `nvm` to `14.21.3` ([#3463](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3463)) +- Allow selecting the Node.js binary using `NODE_HOME` and `OSD_NODE_HOME` ([#3508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3508)) +- Remove the unused `renovate.json5` file ([#3489](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3489)) +- Bump `styled-components` from `5.3.5` to `5.3.9` ([#3678](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3678)) +- [Timeline] Update default expressions from `.es(*)` to `.opensearch(*)`. ([#2720](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2720)) ### 🪛 Refactoring -- [MD] Refactor data source error handling ([#2661](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2661)) -- Refactor and improve Discover field summaries ([#2391](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2391)) -- [Vis Builder] Removed Hard Coded Strings and Used i18n to transalte([#2867](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2867)) -- [Console] Replace jQuery.ajax with core.http when calling OSD APIs in console ([#3080](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3080)) +- Remove automatic addition of `osd-version` header to requests outside of OpenSearch Dashboards ([#3643](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3643)) +- [Console] Replace jQuery usage in console plugin with native methods ([#3733](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3733)) +- [Doc Links] Clean up docs_link_service organization so that strings are in the right categories. ([#3685](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3685)) - [I18n] Fix Listr type errors and error handlers ([#3629](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3629)) +- [Multiple DataSource] Refactor dev tool console to use opensearch-js client to send requests ([#3544](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3544)) - [Multiple DataSource] Present the authentication type choices in a drop-down ([#3693](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3693)) -- [Console] Remove unused ul element and its custom styling ([#3993](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3993)) -- Fix EUI/OUI type errors ([#3798](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3798)) -- Remove unused Sass in `tile_map` plugin ([#4110](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4110)) +- [Table Visualization] Move format table, consolidate types and add unit tests ([#3397](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3397)) ### 🔩 Tests -- [Multi DataSource] Add unit test coverage for Update Data source management stack ([#2567](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2567)) -- [BWC Tests] Add BWC tests for 2.5.0 ([#2890](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2890)) -- Add retrial of flaky tests ([#2967](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2967)) -- Fix incorrect validation of time values in JUnit Reporter ([#2965](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2965)) -- Make tests covering plugin installation on cluster snapshots work across platforms ([#2994](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2994)) -- Correct the linting logic for `no-restricted-path` to ignore trailing slashes ([#3020](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3020)) -- [Tests] Bumps `chromedriver` to v107 ([#3017](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3017)) -- [Vis Builder] Adds field unit tests ([#3211](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3211)) -- [BWC Tests] Add BWC tests for 2.6.0 ([#3356](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3356)) -- Prevent primitive linting limitations from being applied to unit tests found under `src/setup_node_env` ([#3403](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3403)) +- Update caniuse to `1.0.30001460` to fix failed integration tests ([#3538](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3538)) - [Tests] Fix unit tests for `get_keystore` ([#3854](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3854)) -- [Tests] Use `scripts/use_node` instead of `node` in functional test plugins ([#3783](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3783)) -## [2.x] +## [1.3.9 - 2023-04-04](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/1.3.9) -### 💥 Breaking Changes +### 🛡 Security + +- [CVE-2022-2499] Resolve qs from 6.5.2 and 6.7.0 to 6.11.0 in 1.x ([#3451](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3451)) +- [CVE-2020-36632] [REQUIRES PLUGIN VALIDATION] Bump flat from 4.1.1 to 5.0.2 ([#3539](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3539)). To the best of our knowledge, this is a non-breaking change, but if your plugin relies on `mocha` tests, validate that they still work correctly (and plan to migrate them to `jest` [in preparation for `mocha` deprecation](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/1572). +- [CVE-2023-25653] Bump node-jose to 2.2.0 ([#3445](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3445)) +- [CVE-2021-23807] Bump jsonpointer from 4.1.0 to 5.0.1 ([#3535](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3535)) +- [CVE-2021-23424] Bump ansi-html from 0.0.7 to 0.0.8 ([#3536](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3536)) +- [CVE-2022-24999] Bump express from 4.17.1 to 4.18.2 ([#3542](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3542)) + +### 📈 Features/Enhancements + +- [I18n] Register ru, ru-RU locale ([#2817](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2817)) + +### 🐛 Bug Fixes + +- [TSVB] Fix the link to "serial differencing aggregation" documentation ([#3503](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3503)) + +### 📝 Documentation + +- [TSVB] Fix a spelling error in the README file ([#3518](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3518)) +- Simplify the in-code instructions for upgrading `re2` ([#3328](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3328)) +- [Doc] Improve DEVELOPER_GUIDE to make first time setup quicker and easier ([#3421](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3421)) + +### 🛠 Maintenance + +- Update MAINTAINERS.md formatting and maintainer list ([#3338](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3338)) +- Remove `github-checks-reporter`, an unused dependency ([#3126](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3126)) +- [Version] Increment to 1.3.9 ([#3375](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3375)) +- Remove the unused `renovate.json5` file ([3489](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3489)) + +## [2.6.0 - 2023-02-28](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.6.0) ### Deprecations +- [CVE-2020-36632] [REQUIRES PLUGIN VALIDATION] Bump flat from `4.1.1` to `5.0.2` ([#3419](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3419)). To the best of our knowledge, this is a non-breaking change, but if your plugin relies on `mocha` tests, validate that they still work correctly (and plan to migrate them to `jest` [in preparation for `mocha` deprecation](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/1572). + +### 🛡 Security + +- [CVE-2022-37599] Bump loader-utils from `2.0.3` to `2.0.4` ([#3318](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3318)) +- [CVE-2022-37603] Bump loader-utils from `2.0.3` to `2.0.4` ([#3318](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3318)) +- [CVE-2022-25860] Bump simple-git from `3.15.1` to `3.16.0` ([#3345](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3345)) +- [CVE-2022-25881] Resolve http-cache-semantics from `4.1.0` to `4.1.1` ([#3409](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3409)) +- [Security] Bump hapi/statehood from `7.0.3` to `7.0.4` ([#3411](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3411)) +- [CVE-2023-25166] Bump formula from `3.0.0` to `3.0.1` ([#3416](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3416)) +- [CVE-2020-36632] [REQUIRES PLUGIN VALIDATION] Bump flat from `4.1.1` to `5.0.2` ([#3419](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3419)). To the best of our knowledge, this is a non-breaking change, but if your plugin relies on `mocha` tests, validate that they still work correctly (and plan to migrate them to `jest` [in preparation for `mocha` deprecation](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/1572). +- [CVE-2023-25653] Bump node-jose from `2.1.1` to `2.2.0` ([#3445](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3445)) +- [CVE-2022-24999] Resolve qs from `6.5.3` to `6.11.0` ([#3450](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3450)) +- [CVE-2022-25758] Bump node-sass from `6.0.1` to `7.0.3` and sass-loader from `10.2.1` to `10.4.1` to bump scss-tokenizer from `0.2.3` to `0.4.3` ([#3455](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3455)) +- [CVE-2020-24025] Bump node-sass from `6.0.1` to `7.0.3` ([#3455](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3455)) + +### 📈 Features/Enhancements + +- Add disablePrototypePoisoningProtection configuration to prevent JS client from erroring when cluster utilizes JS reserved words ([#2992](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2992)) +- [Multiple DataSource] Add support for SigV4 authentication ([#3058](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3058)) +- [Multiple DataSource] Refactor test connection to support SigV4 auth type ([#3456](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3456)) + +### 🐛 Bug Fixes + +- [Search Telemetry] Fix search telemetry's observable object that won't be GC-ed([#3390](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3390)) +- [Region Maps] Add ui setting to configure custom vector map's size parameter([#3399](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3399)) + +### 🚞 Infrastructure + +- Fix detection of Chrome's version on Darwin during CI ([#3296](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3296)) + +### 📝 Documentation + +- [Docs] Fix documentation link for date math ([#3207](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3207)) + +### 🛠 Maintenance + +- Bump `re2` and `supertest` ([#3018](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3018)) +- Upgrade vega-tooltip to `0.30.0` to support custom tooltips ([#3359](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3359)) +- Allow relaxing the Node.js runtime version requirement ([#3402](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3402)) +- Make build scripts find and use the latest version of Node.js that satisfies `engines.node` ([#3467](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3467)) +- Add `@opensearch-project/opensearch@^2.x` as dependency aliased as `@opensearch-project/opensearch-next` ([#3469](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3469)) + +### 🪛 Refactoring + +### 🔩 Tests + +- [BWC Tests] Add BWC tests for `2.6.0` ([#3356](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3356)) +- Prevent primitive linting limitations from being applied to unit tests found under `src/setup_node_env` ([#3403](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3403)) + +## [1.3.8 - 2023-02-15](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/1.3.8) + +### 🛡 Security + +- [CVE-2022-25901] Bump supertest from 2.0.5 to 2.0.12 ([#3326](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3326)) +- [CVE-2022-25860] Bump simple-git from 3.15.1 to 3.16.0 ([#3345](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3345)) +- [CVE-2022-46175] Bump json5 version from 1.0.1 and 2.2.1 to 1.0.2 and 2.2.3 ([#3201](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3201)) +- [CVE-2022-25912] Bump simple-git from 3.4.0 to 3.15.0 ([#3036](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3036)) +- Bump decode-uri-component from 0.2.0 to 0.2.2 ([#3009](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3009)) + +### 🐛 Bug Fixes + +- [BUG] Fixes misleading embeddable plugin error message ([#3043](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3043)) +- [BUG] Trim trailing slashes before checking no-restricted-path rule ([#3020](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3020)) + +### 🚞 Infrastructure + +- Lock workflow tests to Chrome and ChromeDriver 107 as the last combination that run on Node.js v10 ([#3299](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3299)) +- Update yarn timeout for GitHub workflow on Windows ([#3118](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3118)) +- Add Windows CI to the GitHub workflow ([#2966](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2966)) + +### 📝 Documentation + +- Fix documentation link for date math ([#3207](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3207)) + +### 🔩 Tests + +- [BWC] Updates to BWC tests ([#1190](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1190)) +- Automates chromedriver version selection for tests ([#2990](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2990)) + +## [2.5.0 - 2023-01-25](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.5.0) + +### 🛡 Security + +- Introduce guidelines for reporting vulnerable dependencies ([#2674](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2674)) +- Bump decode-uri-component from 0.2.0 to 0.2.2 ([3009](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3009)) +- [CVE-2022-25912] Bump simple-git from 3.4.0 to 3.15.0 ([#3036](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3036)) +- [CVE-2022-35256] Bump node version from 14.20.0 to 14.20.1 [#3166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3166)) +- [CVE-2022-46175] Bump json5 version from 1.0.1 and 2.2.1 to 1.0.2 and 2.2.3 ([#3201](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3201)) + +### 📈 Features/Enhancements + +- [CLI] Enhance `yarn opensearch snapshot` to facilitate installing plugins on an OpenSearch cluster ([#2734](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2734)) +- [I18n] Register ru, ru-RU locale ([#2817](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2817)) +- [Multi DataSource] Introduce validation of new or modified connections to external data sources ([#2973](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2973), [#3110](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3110)) +- [VisBuilder] Create global data persistence for VisBuilder ([#2896](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2896)) +- [VisBuilder] Introduce Redux store persistence ([#3088](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3088)) +- [VisBuilder] Enable persistence for app filter and query without using state containers ([#3100](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3100)) +- [Data] Make the newly created configurations get added to beginning of the `aggConfig` array when using `createAggConfig` ([#3160](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3160)) +- [Optimizer] Increase the amount of time an optimizer worker is provided to exit before throwing an error ([#3193](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3193)) + +### 🐛 Bug Fixes + +- Upgrade the `del` library to fix a race condition on macOS ([#2847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2873)) +- [Table Visualization] Fix a problem with table visualizations that prevented URLs from being rendered correctly ([#2918](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2918)) +- [Embeddable] Fix a misleading error message ([#3043](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3043)) +- Fix rendering issues when the obsolete `v8 (beta)` theme was carried over by an upgrade ([#3045](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3045)) +- [Multi Datasource] Replace the mock URL in tests ([#3099](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3099)) +- [CI] Increase Yarn's timeout for installing dependencies in workflows ([#3118](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3118)) +- [VisBuilder] Fix an issue that caused a crash when certain filters were added to a table visualization ([#3210](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3210)) +- [VisBuilder] Fix errors throws when pipeline aggregations, like cumulative sum, were used in VisBuilder ([#3137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3137)) +- [Region Maps] Fix the problem of join fields being unusable ([#3213](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3213)) +- [Multi DataSource] Update test connection button text ([#3247](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3247)) + +### 🚞 Infrastructure + +- Bump the version of the `2.x` branch to 2.5.0 ([#2884](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2884)) +- [CI] Create workflows that test and build on Windows ([#2966](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2966)) +- [CI] Automate ChromeDriver installation for running functional tests ([#2990](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2990)) +- Create the Release Notes for the 1.3.7 release ([#3066](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3066)) +- [CI] Improve workflows by retaining Yarn's cache folder ([#3194](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3194)) + +### 📝 Documentation + +- Publish the release runbook ([#2533](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2533)) +- Document the capabilities of the Charts plugin and its current usage ([#2695](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2695)) +- Document the correct version selection switch in `@osd/plugin-helpers` ([#2810](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2810)) +- Document the global query persistence ([#3001](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3001)) +- Document data persistence for plugins ([#3081](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3081)) + +### 🪛 Refactoring + +- [VisBuilder] Extend the use of i18n ([#2867](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2867)) +- [Console] Switch to using `core.http` when calling OSD APIs in console ([#3080](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3080)) +- [Table Visualization] Refactor table visualization using React and DataGrid component ([#2863](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2863)) + +### 🛠 Maintenance + +- Remove an unused dependency on `github-checks-reporter` ([#3126](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3126)) +- Introduce `vega-lite@5`, aliased as `vega-lite-next` ([#3151](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3151)) + +### 🔩 Tests + +- Enable retrying of flaky tests ([#2967](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2967)) +- Enhance cross-platform testing of plugin installation on cluster snapshots ([#2994](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2994)) +- [Tests] Bump `chromedriver` to v107 ([#3017](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3017)) +- [CI] Disable the execution of the Build and Test workflow when the changes are limited to the docs folder ([#3197](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3197)) +- Correct the linting logic for `no-restricted-path` to ignore trailing slashes ([#3020](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3020)) +- [VisBuilder] Create unit tests for field utilities ([#3211](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3211)) + +## [2.4.1 - 2022-12-14](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.4.1) + +### 🐛 Bug Fixes + +- Update `leaflet-vega` and fixed its usage ([#3005](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3005)) + +### 🔩 Tests + +- Correct the linting logic for `no-restricted-path` to ignore trailing slashes ([#3020](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3020)) + +## [1.3.7 - 2022-12-14](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/1.3.7) + +### 🛡 Security + +- [CVE-2022-0144] Bump shelljs from 0.8.4 to 0.8.5 ([#2511](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2511)) +- [Legacy Maps Plugin] Prevent reverse-tabnabbing ([#2540](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2540)) +- [CVE-2022-3517] Bump minimatch from 3.0.4 to 3.0.5 and [IBM X-Force ID: 220063] unset-value from 1.0.1 to 2.0.1 ([#2640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2640)) +- [CVE-2022-0155] Bump follow-redirects to 1.15.2 ([#2653](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2653)) +- [CVE-2022-0536] Bump follow-redirects to 1.15.2 ([#2653](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2653)) +- [CVE-2021-24033] Remove storybook package ([#2660](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2660)) +- [CVE-2021-42740] Remove storybook package ([#2660](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2660)) +- [CVE-2022-23647] Bump prismjs to 1.29.0 ([#2668](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2668)) +- [CVE-2022-37599] Bump loader-utils to 2.0.4 ([#2995](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2995)) +- [CVE-2022-37603] Bump loader-utils to 2.0.4 ([#2995](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2995)) + +### 📈 Features/Enhancements + +- [Windows] Facilitate building and running OSD and plugins on Windows platforms ([#2601](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2601)) +- [Windows] Add helper functions to work around the differences of platforms ([#2681](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2681)) +- [Windows] Add `@osd/cross-platform` package to standardize path handling across platforms ([#2703](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2703)) + +### 🐛 Bug Fixes + +- [Chore] Visualize link fix [#2395](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2395) +- [BUG] Fix suggestion list cutoff issue ([#2607](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2607)) +- Remove Leftover X Pack references ([#2638](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2638)) +- Bump `del` version to fix MacOS race condition ([#2847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2873)) +- Temporary workaround for task-kill exceptions on Windows when it is passed a pid for a process that is already dead ([#2842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2842)) +- [Build] Fixed "Last Access Time" not being set by `scanCopy` on Windows ([#2964](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2964)) +- Update `leaflet-vega` and fix its usage ([#3005](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3005)) + +### 🚞 Infrastructure + +- Update backport custom branch name to utilize head template ([#2766](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2766)) + +### 📝 Documentation + +- Add the release runbook to RELEASING.md ([#2533](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2533)) +- Security-CVEs fixes guidelines [#2674](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2674) +- Correct README and help command of osd-plugin-helpers ([#2810](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2810)) + +### 🛠 Maintenance + +- Increment version to 1.3.7 [#2528](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2528) + +### 🔩 Tests + +- Bump `chromedriver` to 106 to fix function test fail issue [#2514](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2514) +- Fix incorrect validation of time values in JUnit Reporter ([#2965](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2965)) + +## [2.4.0 - 2022-11-15](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.4.0) + ### 🛡 Security +- Bump percy-agent to use non-beta version ([#2415](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2415)) - Use a forced CSP-compliant interpreter with Vega visualizations ([#2352](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2352)) -- Bump moment-timezone from 0.5.34 to 0.5.37 ([#2361](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2361)) +- [CVE-2022-33987] Bump makelogs to remove dependency on got ([#2801](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2801)) - [CVE-2022-33987] Upgrade geckodriver to 3.0.2 ([#2166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2166)) -- Bumps percy-agent to use non-beta version ([#2415](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2415)) -- Resolve sub-dependent d3-color version and potential security issue ([#2454](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2454)) -- [CVE-2022-3517] Bumps minimatch from 3.0.4 to 3.0.5 and [IBM X-Force ID: 220063] unset-value from 1.0.1 to 2.0.1 ([#2640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2640)) -- [CVE-2022-37601] Bump loader-utils to 2.0.3 ([#2689](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2689)) -- [CVE-2022-37599] Bump loader-utils to 2.0.4 ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)) -- [CVE-2022-37603] Bump loader-utils to 2.0.4 ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)) -- [WS-2021-0638][security] bump mocha to 10.1.0 ([#2711](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2711)) -- [CVE-2022-25881] Resolve http-cache-semantics to 4.1.1 ([#3409](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3409)) +- [CVE-2022-3517] Bump minimatch to 3.0.5 and [IBM X-Force ID: 220063] unset-value to 2.0.1 ([#2640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2640)) +- [CVE-2022-37601] Bump loader-utils to 2.0.3 ([#2706](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2706)) +- [GMS-2022-4708] Resolve sub-dependent d3-color version and potential security issue ([#2454](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2454)) +- [Legacy Maps] Prevent reverse-tabnabbing ([#2540](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2540)) +- [WS-2022-0284] [WS-2022-0280] Bump moment-timezone from 0.5.34 to 0.5.37 ([#2361](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2361)) +- [Multi DataSource] Prevent spell-checking the password fields ([#2818](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2818)) ### 📈 Features/Enhancements -- Add updated_at column to objects' tables ([#1218](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1218)) -- [Viz Builder] State validation before dispatching and loading ([#2351](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2351)) -- [Viz Builder] Create a new wizard directly on a dashboard ([#2384](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2384)) -- [Viz Builder] Edit wizard directly on dashboard ([#2508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2508)) -- [Multi DataSource] UX enhacement on index pattern management stack ([#2505](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2505)) +- Add extension point in saved object management to register namespaces and show filter ([#2656](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2656)) +- Add updated_at column to Saved Objects' tables ([#1218](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1218)) +- Change the links in the visualize plugin to use `href` rather than `onClick` ([#2395](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2395)) +- Improve Discover field summaries ([#2391](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2391)) +- Remove Add Integration button ([#2723](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2723)) +- [Multi DataSource] Add data source column into index pattern table ([#2542](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2542)) +- [Multi DataSource] Add data source config to opensearch-dashboards-docker ([#2557](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2557)) +- [Multi DataSource] Add data source signing support ([#2510](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2510)) +- [Multi DataSource] Add experimental callout for index pattern section ([#2523](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2523)) +- [Multi DataSource] Address UX comments on Data source list and create page ([#2625](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2625)) +- [Multi DataSource] Apply get indices error handling in step index pattern ([#2652](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2652)) +- [Multi DataSource] Display error toast for create index pattern with data source ([#2506](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2506)) +- [Multi DataSource] Make text content dynamically translated & update unit tests ([#2570](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2570)) +- [Multi DataSource] Support legacy client for data source ([#2204](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2204)) +- [Multi DataSource] UX enhancement on Data source management creation page ([#2051](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2501)) - [Multi DataSource] UX enhancement on Data source management stack ([#2521](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2521)) +- [Multi DataSource] UX enhancement on Index Pattern management stack ([#2505](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2505))([#2527](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2527)) - [Multi DataSource] UX enhancement on Update stored password modal for Data source management stack ([#2532](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2532)) -- [Monaco editor] Add json worker support ([#3424](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3424)) -- Enhance grouping for context menus ([#3169](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3169)) -- Replace re2 with RegExp in timeline and add unit tests ([#3908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3908)) +- [Plugin Helpers] Facilitate version changes ([#2398](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2398)) +- [VisBuilder] Add an experimental table visualization in vis builder ([#2705](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2705)) +- [VisBuilder] Add field summary popovers ([#2682](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2682)) +- [VisBuilder] Add index pattern info when loading embeddable ([#2363](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2363)) +- [VisBuilder] Add state validation before dispatching and loading ([#2351](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2351)) +- [VisBuilder] Change VisBuilder flag for docker config ([#2804](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2804)) +- [VisBuilder] Change classname prefix wiz to vb ([#2581](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2581/files)) +- [VisBuilder] Change save object type, wizard id and name to visBuilder ([#2673](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2673)) +- [VisBuilder] Change wizard to vis_builder in file names and paths ([#2587](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2587)) +- [VisBuilder] Create a new wizard directly on a dashboard ([#2384](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2384)) +- [VisBuilder] Edit wizard directly on dashboard ([#2508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2508)) +- [VisBuilder] Enable VisBuilder by default ([#2725](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2725)) +- [VisBuilder] Rename wizard on save modal and visualization table ([#2645](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2645)) +- [VisBuilder] Rename wizard to visBuilder in class name, type name and function name ([#2639](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2639)) +- [VisBuilder] Rename wizard to visBuilder in i18n id and formatted message id ([#2635](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2635)) +- [Windows] Add cross-platform helpers ([#2681](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2681)) +- [Windows] Consume `@osd/cross-platform` package to standardize path handling across platforms ([#2703](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2703)) +- [Windows] Facilitate building and running OSD and plugins on Windows platforms ([#2601](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2601)) ### 🐛 Bug Fixes -- [Viz Builder] Fixes time series for new chart types ([#2309](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2309)) -- [Viz Builder] Add index pattern info when loading embeddable ([#2363](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2363)) -- Fixes management app breadcrumb error ([#2344](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2344)) -- [BUG] Fix suggestion list cutoff issue ([#2607](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2607)) -- [TSVB] Fixes undefined serial diff aggregation documentation link ([#3503](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3503)) -- [Console] Fix dev tool console autocomplete not loading issue ([#3775](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3775)) -- [Console] Fix dev tool console run command with query parameter error ([#3813](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3813)) -- Add clarifying tooltips to header navigation ([#3573](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3573)) -- [Dashboards Listing] Fix listing limit to utilize `savedObjects:listingLimit` instead of `savedObjects:perPage` ([#4021](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4021)) +- Fix management app breadcrumb error ([#2344](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2344)) +- Fix suggestion list cutoff issue ([#2607](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2607)) +- Remove Leftover X Pack references ([#2638](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2638)) +- [Multi DataSource] Add data source param to low-level search call in Discover ([#2431](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2431)) +- [Multi DataSource] Address UX comments on Edit Data source page ([#2629](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2629)) +- [Multi DataSource] Address UX comments on index pattern management stack ([#2611](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2611)) +- [Multi DataSource] Enhance data source error handling ([#2661](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2661)) +- [Multi DataSource] Skip data source view in index pattern step when default is chosen ([#2574](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2574)) +- [Multi DataSource] Update default audit log path ([#2793](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2793)) +- [Save Object Aggregation View] Fix for export all after scroll count response changed ([#2696](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2696)) +- [VisBuilder] Add additional aggregation parameters to Vislib charts (Bar, Line and Area) ([#2610](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2610)) +- [VisBuilder] Add missing test subject property of `DisabledVisualization` ([#2610](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2610)) +- [VisBuilder] Fix Date Histogram auto bounds showing per 0 millisecond ([#2632](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2632)) +- [VisBuilder] Fix Histogram updating bounds when date range updates ([#2632](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2632)) +- [VisBuilder] Fix auto bounds for time-series bar chart visualization ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) +- [VisBuilder] Fix broken UX after switching index pattern while editing an aggregation ([#2632](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2632)) +- [VisBuilder] Fix rendering issues with time series for new chart types ([#2309](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2309)) +- [VisBuilder] Fix the missing `Last Updated` timestamp in visualization list ([#2628](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2628)) +- [VisBuilder] Fix visualization shift when editing an aggregation ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) +- [VisBuilder] Rename "Histogram" to "Bar" in visualization type picker ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) +- [Table Visualization] Fix an issue preventing sorting the first column ([#2828](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2828)) +- Temporary workaround for task-kill exceptions on Windows when it is passed a pid for a process that is already dead ([#2842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2842)) ### 🚞 Infrastructure -- Add path ignore for markdown files for CI ([#2312](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2312)) -- Updating WS scans to ignore BWC artifacts in `cypress` ([#2408](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2408)) +- Add CHANGELOG.md and related workflows ([#2414](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2414)) +- Update WhiteSource scans to ignore Backward Compatibility artifacts in `cypress` ([#2408](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2408)) +- [CI] Add Backward Compatibility tests for 2.4.0 ([#2393](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2393)) +- [CI] Add path ignore for markdown files ([#2312](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2312)) +- [CI] Prevent backport workflow from running on unmerged PRs ([#2746](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2746)) - [CI] Run functional test repo as workflow ([#2503](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2503)) -- Add downgrade logic for branch in DocLinkService([#3483](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3483)) +- [CI] Update backport custom branch name ([#2766](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2766)) +- [CI] Update backport workflow to ignore changelog conflicts ([#2729](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2729)) ### 📝 Documentation -- README.md for saving index pattern relationship ([#2276](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2276)) -- Remove extra typo from README. ([#2403](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2403)) -- Add sample config for multi data source feature in yml template. ([#2428](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2428)) -- README.md for dataSource and dataSourceManagement Plugin ([#2448](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2448)) -- Updates functionl testing information in Testing.md ([#2492](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2492)) -- Fixes typo in TSVB README ([#3518](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3518)) +- Add CHANGELOG.md and Release Notes for 2.4.0 ([#2809](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2809)) +- Add README.md for `dataSource` and `dataSourceManagement` plugins ([#2448](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2448)) +- Add README.md for saving index pattern relationship ([#2276](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2276)) +- Remove a repeated "to" from the README.md file ([#2403](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2403)) +- Update functional testing information in TESTING.md ([#2492](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2492)) +- [Multi DataSource] Add design documents of multiple data source feature [#2538](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2538) +- [Multi DataSource] Add sample configuration for multi data source to the yml template ([#2428](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2428)) +- [Multi DataSource] Tweak multiple data source design doc ([#2724](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2724)) +- [Multi DataSource] Update MD data source documentation link ([#2693](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2693)) + +### 🔩 Tests + +- Update caniuse to fix failed integration tests ([#2322](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2322)) +- [Multi DataSource] Add unit test coverage for Update Data source management stack ([#2567](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2567)) +- [VisBuilder] Enable VisBuilder cypress tests ([#2728](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2728)) ### 🛠 Maintenance +- Add @zengyan-amazon as a maintainer ([#2419](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2419)) - Increment from 2.3 to 2.4. ([#2295](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2295)) -- Adding @zengyan-amazon as maintainer ([#2419](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2419)) -- Updating @tmarkley to Emeritus status. ([#2423](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2423)) -- Adding sample config for multi data source in yml config template. ([#2428](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2428)) -- Adding @kristenTian as maintainer. ([#2450](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2450)) +- Add CHANGELOG.md for 2.4.0 ([#2809](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2809)) -### 🪛 Refactoring +## [1.3.6 - 2022-10-06](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/1.3.6) -- [Tech Debt] Clean up docs_link_service organization so that strings are in the right categories. ([#3685](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3685)) +### 🛡 Security + +- [CVE-2021-3807] Resolves ansi-regex to v5.0.1 ([#2425](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2425)) +- [CVE-2022-23713] Handle invalid query, index and date in vega charts filter handlers ([#1932](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1932)) +- Use a forced CSP-compliant interpreter with Vega visualizations ([#2352](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2352)) +- Bump moment-timezone from 0.5.34 to 0.5.37 ([#2361](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2361)) + +### 📈 Features/Enhancements + +- Custom healthcheck with filters ([#2232](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2232), [#2277](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2277)). To configure see example in [config/opensearch_dashboards.yml](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/6e2ec97459ae179c86201c611ce744c2c24ce150/config/opensearch_dashboards.yml#L44-L46) + +### 🚞 Infrastructure + +- Add CHANGELOG.md and related workflows ([#2414](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2414)) +- Extends plugin-helpers to be used for automating version changes ([#2398](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2398),[#2486](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2486)) + +### 🛠 Maintenance + +- Version Increment to 1.3.6 ([#2420](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2420)) ### 🔩 Tests - Update caniuse to fix failed integration tests ([#2322](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2322)) -- Update caniuse to 1.0.30001460 to fix failed integration tests ([#3538](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3538)) - -[unreleased]: https://github.com/opensearch-project/OpenSearch-Dashboards/compare/2.3.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch-Dashboards/compare/2.3.0...2.x diff --git a/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx b/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx index b01d04c1608b..1f6ba03e966b 100644 --- a/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx +++ b/examples/ui_actions_explorer/public/context_menu_examples/context_menu_examples.tsx @@ -36,6 +36,7 @@ import { PanelViewWithSharingLong } from './panel_view_with_sharing_long'; import { PanelEdit } from './panel_edit'; import { PanelEditWithDrilldowns } from './panel_edit_with_drilldowns'; import { PanelEditWithDrilldownsAndContextActions } from './panel_edit_with_drilldowns_and_context_actions'; +import { PanelGroupOptionsAndContextActions } from './panel_group_options_and_context_actions'; export const ContextMenuExamples: React.FC = () => { return ( @@ -59,7 +60,6 @@ export const ContextMenuExamples: React.FC = () => { <PanelViewWithSharingLong /> </EuiFlexItem> </EuiFlexGroup> - <EuiFlexGroup> <EuiFlexItem> <PanelEdit /> @@ -71,6 +71,11 @@ export const ContextMenuExamples: React.FC = () => { <PanelEditWithDrilldownsAndContextActions /> </EuiFlexItem> </EuiFlexGroup> + <EuiFlexGroup> + <EuiFlexItem> + <PanelGroupOptionsAndContextActions /> + </EuiFlexItem> + </EuiFlexGroup> </EuiText> ); }; diff --git a/examples/ui_actions_explorer/public/context_menu_examples/panel_group_options_and_context_actions.tsx b/examples/ui_actions_explorer/public/context_menu_examples/panel_group_options_and_context_actions.tsx new file mode 100644 index 000000000000..20dc73406c55 --- /dev/null +++ b/examples/ui_actions_explorer/public/context_menu_examples/panel_group_options_and_context_actions.tsx @@ -0,0 +1,83 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as React from 'react'; +import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui'; +import useAsync from 'react-use/lib/useAsync'; +import { buildContextMenuForActions, Action } from '../../../../src/plugins/ui_actions/public'; +import { sampleAction } from './util'; + +export const PanelGroupOptionsAndContextActions: React.FC = () => { + const [open, setOpen] = React.useState(false); + + const context = {}; + const trigger: any = 'TEST_TRIGGER'; + const drilldownGrouping: Action['grouping'] = [ + { + id: 'drilldowns', + getDisplayName: () => 'Uncategorized group', + getIconType: () => 'popout', + order: 20, + }, + ]; + const exampleGroup: Action['grouping'] = [ + { + id: 'example', + getDisplayName: () => 'Example group', + getIconType: () => 'cloudStormy', + order: 20, + category: 'visAug', + }, + ]; + const alertingGroup: Action['grouping'] = [ + { + id: 'alerting', + getDisplayName: () => 'Alerting', + getIconType: () => 'cloudStormy', + order: 20, + category: 'visAug', + }, + ]; + const anomaliesGroup: Action['grouping'] = [ + { + id: 'anomalies', + getDisplayName: () => 'Anomalies', + getIconType: () => 'cloudStormy', + order: 30, + category: 'visAug', + }, + ]; + const actions = [ + sampleAction('test-1', 100, 'Edit visualization', 'pencil'), + sampleAction('test-2', 99, 'Clone panel', 'partial'), + + sampleAction('test-9', 10, 'Create drilldown', 'plusInCircle', drilldownGrouping), + sampleAction('test-10', 9, 'Manage drilldowns', 'list', drilldownGrouping), + + sampleAction('test-11', 10, 'Example action', 'dashboardApp', exampleGroup), + sampleAction('test-11', 10, 'Alertin action 1', 'dashboardApp', alertingGroup), + sampleAction('test-12', 9, 'Alertin action 2', 'dashboardApp', alertingGroup), + sampleAction('test-13', 8, 'Anomalies 1', 'cloudStormy', anomaliesGroup), + sampleAction('test-14', 7, 'Anomalies 2', 'link', anomaliesGroup), + ]; + + const panels = useAsync(() => + buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context, trigger })), + }) + ); + + return ( + <EuiPopover + button={<EuiButton onClick={() => setOpen((x) => !x)}>Grouping with categories</EuiButton>} + isOpen={open} + panelPaddingSize="none" + anchorPosition="downLeft" + closePopover={() => setOpen(false)} + > + <EuiContextMenu initialPanelId={'mainMenu'} panels={panels.value} /> + </EuiPopover> + ); +}; diff --git a/release-notes/opensearch-dashboards.release-notes-1.3.10.md b/release-notes/opensearch-dashboards.release-notes-1.3.10.md index be08d46aac93..a640c32356ce 100644 --- a/release-notes/opensearch-dashboards.release-notes-1.3.10.md +++ b/release-notes/opensearch-dashboards.release-notes-1.3.10.md @@ -19,8 +19,6 @@ ### 🐛 Bug Fixes -- [TSVB] Fix the link to "serial differencing aggregation" documentation ([#3503](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3503)) - ### 📝 Documentation - Update jest documentation links ([#3939](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3939)) @@ -28,6 +26,6 @@ ### 🛠 Maintenance - Add threshold to code coverage changes for project ([#4050](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4050)) -- Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests ([#4039]()) +- Temporarily hardcode chromedriver to 112.0.0 to enable all ftr tests ([#4039](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4039)) - Update MAINTAINERS.md and CODEOWNERS ([#3938](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3938)) - Add opensearch-dashboards-docker-dev to .gitignore ([#3781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3781)) diff --git a/release-notes/opensearch-dashboards.release-notes-2.8.0.md b/release-notes/opensearch-dashboards.release-notes-2.8.0.md new file mode 100644 index 000000000000..ee4f1a239d89 --- /dev/null +++ b/release-notes/opensearch-dashboards.release-notes-2.8.0.md @@ -0,0 +1,35 @@ +## Version 2.8.0 Release Notes + +### Deprecations + +- Remove timeline application ([#3971](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3971)) + +### 🛡 Security + +- [CVE-2023-2251] Bump `yaml` to `2.2.2` ([#3947](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3947)) + +### 📈 Features/Enhancements + +- [Multiple Datasource] Support Amazon OpenSearch Serverless ([#3957](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3957)) +- Add support for Node.js >=14.20.1 <19 ([#4071](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4071)) +- Bundle Node.js 14 as a fallback for operating systems that cannot run Node.js 18 ([#4151](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4151)) +- Enhance grouping for context menus ([#3924](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3924)) + +### 🐛 Bug Fixes + +- [BUG] Fix bottom bar visibility using createPortal ([#3978](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3978)) +- [Dashboards Listing] Fix listing limit to utilize `savedObjects:listingLimit` instead of `savedObjects:perPage` ([#4021](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4021)) + +### 🚞 Infrastructure + +- Install chrome driver for functional tests from path set by environment variable `TEST_BROWSER_BINARY_PATH`([#3997](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3997)) +- Add threshold to code coverage config to prevent workflow failures ([#4040](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4040)) +- [CI] Skip checksum verification on OpenSearch snapshot for cypress tests ([#4188](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4188)) + +### 🛠 Maintenance + +- Use `exec` in the CLI shell scripts to prevent new process creation ([#3955](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3955)) + +## 🎉 Welcome + +Thank you to all the first-time contributors who made this release possible: @sikhote, @SergeyMyssak! diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 5ca7c491f72f..5fa3bf888b5c 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -84,6 +84,7 @@ function createManifest( version: 'some-version', configPath: ['path'], requiredPlugins: required, + requiredOpenSearchPlugins: optional, optionalPlugins: optional, requiredBundles: [], }; diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index ae7e85b98d67..a48ff12e859a 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -275,6 +275,7 @@ export class LegacyService implements CoreService { addClientWrapper: setupDeps.core.savedObjects.addClientWrapper, registerType: setupDeps.core.savedObjects.registerType, getImportExportObjectLimit: setupDeps.core.savedObjects.getImportExportObjectLimit, + setRepositoryFactoryProvider: setupDeps.core.savedObjects.setRepositoryFactoryProvider, }, status: { isStatusPageAnonymous: setupDeps.core.status.isStatusPageAnonymous, diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts index 18b715d95050..66fd1336a554 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts @@ -247,6 +247,79 @@ test('return error when manifest contains unrecognized properties', async () => }); }); +describe('requiredOpenSearchPlugins', () => { + test('return error when plugin `requiredOpenSearchPlugins` is a string and not an array of string', async () => { + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ + id: 'id1', + version: '7.0.0', + server: true, + requiredOpenSearchPlugins: 'abc', + }) + ) + ); + + await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ + message: `The "requiredOpenSearchPlugins" in plugin manifest for "id1" should be an array of strings. (invalid-manifest, ${pluginManifestPath})`, + type: PluginDiscoveryErrorType.InvalidManifest, + path: pluginManifestPath, + }); + }); + + test('return error when `requiredOpenSearchPlugins` is not a string', async () => { + mockReadFilePromise.mockResolvedValue( + Buffer.from(JSON.stringify({ id: 'id2', version: '7.0.0', requiredOpenSearchPlugins: 2 })) + ); + + await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ + message: `The "requiredOpenSearchPlugins" in plugin manifest for "id2" should be an array of strings. (invalid-manifest, ${pluginManifestPath})`, + type: PluginDiscoveryErrorType.InvalidManifest, + path: pluginManifestPath, + }); + }); + + test('return error when plugin requiredOpenSearchPlugins is an array that contains non-string values', async () => { + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ id: 'id3', version: '7.0.0', requiredOpenSearchPlugins: ['plugin1', 2] }) + ) + ); + + await expect(parseManifest(pluginPath, packageInfo, logger)).rejects.toMatchObject({ + message: `The "requiredOpenSearchPlugins" in plugin manifest for "id3" should be an array of strings. (invalid-manifest, ${pluginManifestPath})`, + type: PluginDiscoveryErrorType.InvalidManifest, + path: pluginManifestPath, + }); + }); + + test('Happy path when plugin `requiredOpenSearchPlugins` is an array of string', async () => { + mockReadFilePromise.mockResolvedValue( + Buffer.from( + JSON.stringify({ + id: 'id1', + version: '7.0.0', + server: true, + requiredOpenSearchPlugins: ['plugin1', 'plugin2'], + }) + ) + ); + + await expect(parseManifest(pluginPath, packageInfo, logger)).resolves.toEqual({ + id: 'id1', + configPath: 'id_1', + version: '7.0.0', + opensearchDashboardsVersion: '7.0.0', + optionalPlugins: [], + requiredPlugins: [], + requiredOpenSearchPlugins: ['plugin1', 'plugin2'], + requiredBundles: [], + server: true, + ui: false, + }); + }); +}); + describe('configPath', () => { test('falls back to plugin id if not specified', async () => { mockReadFilePromise.mockResolvedValue( @@ -301,6 +374,7 @@ test('set defaults for all missing optional fields', async () => { opensearchDashboardsVersion: '7.0.0', optionalPlugins: [], requiredPlugins: [], + requiredOpenSearchPlugins: [], requiredBundles: [], server: true, ui: false, @@ -317,6 +391,7 @@ test('return all set optional fields as they are in manifest', async () => { opensearchDashboardsVersion: '7.0.0', requiredPlugins: ['some-required-plugin', 'some-required-plugin-2'], optionalPlugins: ['some-optional-plugin'], + requiredOpenSearchPlugins: ['test-opensearch-plugin-1', 'test-opensearch-plugin-2'], ui: true, }) ) @@ -330,6 +405,7 @@ test('return all set optional fields as they are in manifest', async () => { optionalPlugins: ['some-optional-plugin'], requiredBundles: [], requiredPlugins: ['some-required-plugin', 'some-required-plugin-2'], + requiredOpenSearchPlugins: ['test-opensearch-plugin-1', 'test-opensearch-plugin-2'], server: false, ui: true, }); @@ -344,6 +420,7 @@ test('return manifest when plugin expected OpenSearch Dashboards version matches version: 'some-version', opensearchDashboardsVersion: '7.0.0-alpha2', requiredPlugins: ['some-required-plugin'], + requiredOpenSearchPlugins: [], server: true, }) ) @@ -356,6 +433,7 @@ test('return manifest when plugin expected OpenSearch Dashboards version matches opensearchDashboardsVersion: '7.0.0-alpha2', optionalPlugins: [], requiredPlugins: ['some-required-plugin'], + requiredOpenSearchPlugins: [], requiredBundles: [], server: true, ui: false, @@ -383,6 +461,7 @@ test('return manifest when plugin expected OpenSearch Dashboards version is `ope opensearchDashboardsVersion: 'opensearchDashboards', optionalPlugins: [], requiredPlugins: ['some-required-plugin'], + requiredOpenSearchPlugins: [], requiredBundles: [], server: true, ui: true, diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.ts index 2a5ccf611f0c..ee435d2e310a 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.ts @@ -61,6 +61,7 @@ const KNOWN_MANIFEST_FIELDS = (() => { version: true, configPath: true, requiredPlugins: true, + requiredOpenSearchPlugins: true, optionalPlugins: true, ui: true, server: true, @@ -156,6 +157,28 @@ export async function parseManifest( ); } + if ( + manifest.requiredOpenSearchPlugins !== undefined && + (!Array.isArray(manifest.requiredOpenSearchPlugins) || + !manifest.requiredOpenSearchPlugins.every((plugin) => typeof plugin === 'string')) + ) { + throw PluginDiscoveryError.invalidManifest( + manifestPath, + new Error( + `The "requiredOpenSearchPlugins" in plugin manifest for "${manifest.id}" should be an array of strings.` + ) + ); + } + + if ( + Array.isArray(manifest.requiredOpenSearchPlugins) && + manifest.requiredOpenSearchPlugins.length > 0 + ) { + log.info( + `Plugin ${manifest.id} has a dependency on following OpenSearch plugin(s): "${manifest.requiredOpenSearchPlugins}".` + ); + } + const expectedOpenSearchDashboardsVersion = typeof manifest.opensearchDashboardsVersion === 'string' && manifest.opensearchDashboardsVersion ? manifest.opensearchDashboardsVersion @@ -198,6 +221,9 @@ export async function parseManifest( opensearchDashboardsVersion: expectedOpenSearchDashboardsVersion, configPath: manifest.configPath || snakeCase(manifest.id), requiredPlugins: Array.isArray(manifest.requiredPlugins) ? manifest.requiredPlugins : [], + requiredOpenSearchPlugins: Array.isArray(manifest.requiredOpenSearchPlugins) + ? manifest.requiredOpenSearchPlugins + : [], optionalPlugins: Array.isArray(manifest.optionalPlugins) ? manifest.optionalPlugins : [], requiredBundles: Array.isArray(manifest.requiredBundles) ? manifest.requiredBundles : [], ui: includesUiPlugin, diff --git a/src/core/server/plugins/integration_tests/plugins_service.test.ts b/src/core/server/plugins/integration_tests/plugins_service.test.ts index 34310ea3de37..4d1660f65c75 100644 --- a/src/core/server/plugins/integration_tests/plugins_service.test.ts +++ b/src/core/server/plugins/integration_tests/plugins_service.test.ts @@ -57,6 +57,7 @@ describe('PluginsService', () => { disabled = false, version = 'some-version', requiredPlugins = [], + requiredOpenSearchPlugins = [], requiredBundles = [], optionalPlugins = [], opensearchDashboardsVersion = '7.0.0', @@ -68,6 +69,7 @@ describe('PluginsService', () => { disabled?: boolean; version?: string; requiredPlugins?: string[]; + requiredOpenSearchPlugins?: string[]; requiredBundles?: string[]; optionalPlugins?: string[]; opensearchDashboardsVersion?: string; @@ -84,6 +86,7 @@ describe('PluginsService', () => { configPath: `${configPath}${disabled ? '-disabled' : ''}`, opensearchDashboardsVersion, requiredPlugins, + requiredOpenSearchPlugins, requiredBundles, optionalPlugins, server, diff --git a/src/core/server/plugins/plugin.test.ts b/src/core/server/plugins/plugin.test.ts index 9543d379493c..04df84ab8d12 100644 --- a/src/core/server/plugins/plugin.test.ts +++ b/src/core/server/plugins/plugin.test.ts @@ -69,6 +69,7 @@ function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): Plug configPath: 'path', opensearchDashboardsVersion: '7.0.0', requiredPlugins: ['some-required-dep'], + requiredOpenSearchPlugins: ['some-os-plugins'], optionalPlugins: ['some-optional-dep'], requiredBundles: [], server: true, diff --git a/src/core/server/plugins/plugin.ts b/src/core/server/plugins/plugin.ts index 89a2aecc82f3..cc53e1b443e9 100644 --- a/src/core/server/plugins/plugin.ts +++ b/src/core/server/plugins/plugin.ts @@ -66,6 +66,7 @@ export class PluginWrapper< public readonly configPath: PluginManifest['configPath']; public readonly requiredPlugins: PluginManifest['requiredPlugins']; public readonly optionalPlugins: PluginManifest['optionalPlugins']; + public readonly requiredOpenSearchPlugins: PluginManifest['requiredOpenSearchPlugins']; public readonly requiredBundles: PluginManifest['requiredBundles']; public readonly includesServerPlugin: PluginManifest['server']; public readonly includesUiPlugin: PluginManifest['ui']; @@ -95,6 +96,7 @@ export class PluginWrapper< this.configPath = params.manifest.configPath; this.requiredPlugins = params.manifest.requiredPlugins; this.optionalPlugins = params.manifest.optionalPlugins; + this.requiredOpenSearchPlugins = params.manifest.requiredOpenSearchPlugins; this.requiredBundles = params.manifest.requiredBundles; this.includesServerPlugin = params.manifest.server; this.includesUiPlugin = params.manifest.ui; diff --git a/src/core/server/plugins/plugin_context.test.ts b/src/core/server/plugins/plugin_context.test.ts index c55603679c35..ca46a97a237e 100644 --- a/src/core/server/plugins/plugin_context.test.ts +++ b/src/core/server/plugins/plugin_context.test.ts @@ -56,6 +56,7 @@ function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): Plug configPath: 'path', opensearchDashboardsVersion: '7.0.0', requiredPlugins: ['some-required-dep'], + requiredOpenSearchPlugins: ['some-backend-plugin'], requiredBundles: [], optionalPlugins: ['some-optional-dep'], server: true, diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 521a8dd2f7b0..7782fd93041e 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -204,6 +204,7 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>( addClientWrapper: deps.savedObjects.addClientWrapper, registerType: deps.savedObjects.registerType, getImportExportObjectLimit: deps.savedObjects.getImportExportObjectLimit, + setRepositoryFactoryProvider: deps.savedObjects.setRepositoryFactoryProvider, }, status: { core$: deps.status.core$, diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index b020e56539a7..c3ee05d60ed8 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -78,6 +78,7 @@ const createPlugin = ( disabled = false, version = 'some-version', requiredPlugins = [], + requiredOpenSearchPlugins = [], requiredBundles = [], optionalPlugins = [], opensearchDashboardsVersion = '7.0.0', @@ -89,6 +90,7 @@ const createPlugin = ( disabled?: boolean; version?: string; requiredPlugins?: string[]; + requiredOpenSearchPlugins?: string[]; requiredBundles?: string[]; optionalPlugins?: string[]; opensearchDashboardsVersion?: string; @@ -105,6 +107,7 @@ const createPlugin = ( configPath: `${configPath}${disabled ? '-disabled' : ''}`, opensearchDashboardsVersion, requiredPlugins, + requiredOpenSearchPlugins, requiredBundles, optionalPlugins, server, diff --git a/src/core/server/plugins/plugins_system.test.ts b/src/core/server/plugins/plugins_system.test.ts index 2e8aa6b4a4c0..286bb0d97846 100644 --- a/src/core/server/plugins/plugins_system.test.ts +++ b/src/core/server/plugins/plugins_system.test.ts @@ -53,9 +53,16 @@ function createPlugin( { required = [], optional = [], + requiredOSPlugin = [], server = true, ui = true, - }: { required?: string[]; optional?: string[]; server?: boolean; ui?: boolean } = {} + }: { + required?: string[]; + optional?: string[]; + requiredOSPlugin?: string[]; + server?: boolean; + ui?: boolean; + } = {} ) { return new PluginWrapper({ path: 'some-path', @@ -65,6 +72,7 @@ function createPlugin( configPath: 'path', opensearchDashboardsVersion: '7.0.0', requiredPlugins: required, + requiredOpenSearchPlugins: requiredOSPlugin, optionalPlugins: optional, requiredBundles: [], server, @@ -187,7 +195,7 @@ test('correctly orders plugins and returns exposed values for "setup" and "start } const plugins = new Map([ [ - createPlugin('order-4', { required: ['order-2'] }), + createPlugin('order-4', { required: ['order-2'], requiredOSPlugin: ['test-plugin'] }), { setup: { 'order-2': 'added-as-2' }, start: { 'order-2': 'started-as-2' }, @@ -244,6 +252,17 @@ test('correctly orders plugins and returns exposed values for "setup" and "start startContextMap.get(plugin.name) ); + const opensearch = startDeps.opensearch; + opensearch.client.asInternalUser.cat.plugins.mockResolvedValue({ + body: [ + { + name: 'node-1', + component: 'test-plugin', + version: 'v1', + }, + ], + } as any); + expect([...(await pluginsSystem.setupPlugins(setupDeps))]).toMatchInlineSnapshot(` Array [ Array [ @@ -484,6 +503,16 @@ describe('start', () => { afterAll(() => { jest.useRealTimers(); }); + const opensearch = startDeps.opensearch; + opensearch.client.asInternalUser.cat.plugins.mockResolvedValue({ + body: [ + { + name: 'node-1', + component: 'test-plugin', + version: 'v1', + }, + ], + } as any); it('throws timeout error if "start" was not completed in 30 sec.', async () => { const plugin: PluginWrapper = createPlugin('timeout-start'); jest.spyOn(plugin, 'setup').mockResolvedValue({}); @@ -517,4 +546,48 @@ describe('start', () => { const log = logger.get.mock.results[0].value as jest.Mocked<Logger>; expect(log.info).toHaveBeenCalledWith(`Starting [2] plugins: [order-1,order-0]`); }); + + it('validates opensearch plugin installation when dependency is fulfilled', async () => { + [ + createPlugin('order-1', { requiredOSPlugin: ['test-plugin'] }), + createPlugin('order-2'), + ].forEach((plugin, index) => { + jest.spyOn(plugin, 'setup').mockResolvedValue(`setup-as-${index}`); + jest.spyOn(plugin, 'start').mockResolvedValue(`started-as-${index}`); + pluginsSystem.addPlugin(plugin); + }); + + await pluginsSystem.setupPlugins(setupDeps); + const pluginsStart = await pluginsSystem.startPlugins(startDeps); + expect(pluginsStart).toBeInstanceOf(Map); + expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); + }); + + it('validates opensearch plugin installation and does not error out when plugin is not installed', async () => { + [ + createPlugin('id-1', { requiredOSPlugin: ['missing-opensearch-dep'] }), + createPlugin('id-2'), + ].forEach((plugin, index) => { + jest.spyOn(plugin, 'setup').mockResolvedValue(`setup-as-${index}`); + jest.spyOn(plugin, 'start').mockResolvedValue(`started-as-${index}`); + pluginsSystem.addPlugin(plugin); + }); + + await pluginsSystem.setupPlugins(setupDeps); + const pluginsStart = await pluginsSystem.startPlugins(startDeps); + expect(pluginsStart).toBeInstanceOf(Map); + expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); + }); + + it('validates opensearch plugin installation and does not error out when there is no dependency', async () => { + [createPlugin('id-1'), createPlugin('id-2')].forEach((plugin, index) => { + jest.spyOn(plugin, 'setup').mockResolvedValue(`setup-as-${index}`); + jest.spyOn(plugin, 'start').mockResolvedValue(`started-as-${index}`); + pluginsSystem.addPlugin(plugin); + }); + await pluginsSystem.setupPlugins(setupDeps); + const pluginsStart = await pluginsSystem.startPlugins(startDeps); + expect(pluginsStart).toBeInstanceOf(Map); + expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/core/server/plugins/plugins_system.ts b/src/core/server/plugins/plugins_system.ts index b3adc71323f9..6445f38f8e70 100644 --- a/src/core/server/plugins/plugins_system.ts +++ b/src/core/server/plugins/plugins_system.ts @@ -158,13 +158,45 @@ export class PluginsSystem { timeout: 30 * Sec, errorMessage: `Start lifecycle of "${pluginName}" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.`, }); - contracts.set(pluginName, contract); } + await this.healthCheckOpenSearchPlugins(deps); return contracts; } + private async healthCheckOpenSearchPlugins(deps: PluginsServiceStartDeps) { + // make _cat/plugins?format=json call to the OpenSearch instance + const opensearchPlugins = await this.getOpenSearchPlugins(deps); + for (const pluginName of this.satupPlugins) { + this.log.debug(`For plugin "${pluginName}"...`); + const plugin = this.plugins.get(pluginName)!; + const pluginBackendDeps = new Set([...plugin.requiredOpenSearchPlugins]); + pluginBackendDeps.forEach((opensearchPlugin) => { + if (!opensearchPlugins.find((obj) => obj.component === opensearchPlugin)) { + this.log.warn( + `OpenSearch plugin "${opensearchPlugin}" is not installed on the cluster for the OpenSearch Dashboards plugin to function as expected.` + ); + } + }); + } + } + + private async getOpenSearchPlugins(deps: PluginsServiceStartDeps) { + // Makes cat.plugin api call to fetch list of OpenSearch plugins installed on the cluster + try { + const { body } = await deps.opensearch.client.asInternalUser.cat.plugins<any[]>({ + format: 'JSON', + }); + return body; + } catch (error) { + this.log.warn( + `Cat API call to OpenSearch to get list of plugins installed on the cluster has failed: ${error}` + ); + return []; + } + } + public async stopPlugins() { if (this.plugins.size === 0 || this.satupPlugins.length === 0) { return; diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index cf769b45daa3..12b28f48f237 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -154,6 +154,12 @@ export interface PluginManifest { */ readonly requiredPlugins: readonly PluginName[]; + /** + * An optional list of component names of the backend OpenSearch plugins that **must be** installed on the cluster + * for this plugin to function properly. + */ + readonly requiredOpenSearchPlugins: readonly PluginName[]; + /** * List of plugin ids that this plugin's UI code imports modules from that are * not in `requiredPlugins`. diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index ae36b83c0cdd..74168c436c3d 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -79,6 +79,7 @@ const createSetupContractMock = () => { addClientWrapper: jest.fn(), registerType: jest.fn(), getImportExportObjectLimit: jest.fn(), + setRepositoryFactoryProvider: jest.fn(), }; setupContract.getImportExportObjectLimit.mockReturnValue(100); diff --git a/src/core/server/saved_objects/saved_objects_service.test.ts b/src/core/server/saved_objects/saved_objects_service.test.ts index 42ee52567e5f..98d1da393319 100644 --- a/src/core/server/saved_objects/saved_objects_service.test.ts +++ b/src/core/server/saved_objects/saved_objects_service.test.ts @@ -41,7 +41,7 @@ import { errors as opensearchErrors } from '@opensearch-project/opensearch'; import { SavedObjectsService } from './saved_objects_service'; import { mockCoreContext } from '../core_context.mock'; import { Env } from '../config'; -import { configServiceMock } from '../mocks'; +import { configServiceMock, savedObjectsRepositoryMock } from '../mocks'; import { opensearchServiceMock } from '../opensearch/opensearch_service.mock'; import { opensearchClientMock } from '../opensearch/client/mocks'; import { httpServiceMock } from '../http/http_service.mock'; @@ -49,6 +49,7 @@ import { httpServerMock } from '../http/http_server.mocks'; import { SavedObjectsClientFactoryProvider } from './service/lib'; import { NodesVersionCompatibility } from '../opensearch/version_check/ensure_opensearch_version'; import { SavedObjectsRepository } from './service/lib/repository'; +import { SavedObjectRepositoryFactoryProvider } from './service/lib/scoped_client_provider'; jest.mock('./service/lib/repository'); @@ -169,6 +170,27 @@ describe('SavedObjectsService', () => { expect(typeRegistryInstanceMock.registerType).toHaveBeenCalledWith(type); }); }); + + describe('#setRepositoryFactoryProvider', () => { + it('throws error if a repository is already registered', async () => { + const coreContext = createCoreContext(); + const soService = new SavedObjectsService(coreContext); + const setup = await soService.setup(createSetupDeps()); + + const firstRepository: SavedObjectRepositoryFactoryProvider = () => + savedObjectsRepositoryMock.create(); + const secondRepository: SavedObjectRepositoryFactoryProvider = () => + savedObjectsRepositoryMock.create(); + + setup.setRepositoryFactoryProvider(firstRepository); + + expect(() => { + setup.setRepositoryFactoryProvider(secondRepository); + }).toThrowErrorMatchingInlineSnapshot( + `"custom repository factory is already set, and can only be set once"` + ); + }); + }); }); describe('#start()', () => { @@ -281,6 +303,15 @@ describe('SavedObjectsService', () => { }).toThrowErrorMatchingInlineSnapshot( `"cannot call \`registerType\` after service startup."` ); + + const customRpository: SavedObjectRepositoryFactoryProvider = () => + savedObjectsRepositoryMock.create(); + + expect(() => { + setup.setRepositoryFactoryProvider(customRpository); + }).toThrowErrorMatchingInlineSnapshot( + '"cannot call `setRepositoryFactoryProvider` after service startup."' + ); }); describe('#getTypeRegistry', () => { @@ -368,6 +399,36 @@ describe('SavedObjectsService', () => { expect(includedHiddenTypes).toEqual(['someHiddenType']); }); + + it('Should not create SavedObjectsRepository when custom repository is registered ', async () => { + const coreContext = createCoreContext({ skipMigration: false }); + const soService = new SavedObjectsService(coreContext); + const coreSetup = createSetupDeps(); + const setup = await soService.setup(coreSetup); + + const customRpository: SavedObjectRepositoryFactoryProvider = () => + savedObjectsRepositoryMock.create(); + setup.setRepositoryFactoryProvider(customRpository); + + const coreStart = createStartDeps(); + const { createInternalRepository } = await soService.start(coreStart); + createInternalRepository(); + + expect(SavedObjectsRepository.createRepository as jest.Mocked<any>).not.toHaveBeenCalled(); + }); + + it('Should create SavedObjectsRepository when no custom repository is registered ', async () => { + const coreContext = createCoreContext({ skipMigration: false }); + const soService = new SavedObjectsService(coreContext); + const coreSetup = createSetupDeps(); + await soService.setup(coreSetup); + + const coreStart = createStartDeps(); + const { createInternalRepository } = await soService.start(coreStart); + createInternalRepository(); + + expect(SavedObjectsRepository.createRepository as jest.Mocked<any>).toHaveBeenCalled(); + }); }); }); }); diff --git a/src/core/server/saved_objects/saved_objects_service.ts b/src/core/server/saved_objects/saved_objects_service.ts index 92649d36beb4..b6fc21617bcc 100644 --- a/src/core/server/saved_objects/saved_objects_service.ts +++ b/src/core/server/saved_objects/saved_objects_service.ts @@ -56,6 +56,7 @@ import { ISavedObjectsRepository, SavedObjectsRepository } from './service/lib/r import { SavedObjectsClientFactoryProvider, SavedObjectsClientWrapperFactory, + SavedObjectRepositoryFactoryProvider, } from './service/lib/scoped_client_provider'; import { Logger } from '../logging'; import { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry'; @@ -166,6 +167,14 @@ export interface SavedObjectsServiceSetup { * Returns the maximum number of objects allowed for import or export operations. */ getImportExportObjectLimit: () => number; + + /** + * Set the default {@link SavedObjectRepositoryFactoryProvider | factory provider} for creating Saved Objects repository. + * Only one repository can be set, subsequent calls to this method will fail. + */ + setRepositoryFactoryProvider: ( + respositoryFactoryProvider: SavedObjectRepositoryFactoryProvider + ) => void; } /** @@ -291,6 +300,8 @@ export class SavedObjectsService private typeRegistry = new SavedObjectTypeRegistry(); private started = false; + private respositoryFactoryProvider?: SavedObjectRepositoryFactoryProvider; + constructor(private readonly coreContext: CoreContext) { this.logger = coreContext.logger.get('savedobjects-service'); } @@ -348,6 +359,15 @@ export class SavedObjectsService this.typeRegistry.registerType(type); }, getImportExportObjectLimit: () => this.config!.maxImportExportSize, + setRepositoryFactoryProvider: (repositoryProvider) => { + if (this.started) { + throw new Error('cannot call `setRepositoryFactoryProvider` after service startup.'); + } + if (this.respositoryFactoryProvider) { + throw new Error('custom repository factory is already set, and can only be set once'); + } + this.respositoryFactoryProvider = repositoryProvider; + }, }; } @@ -422,13 +442,21 @@ export class SavedObjectsService opensearchClient: OpenSearchClient, includedHiddenTypes: string[] = [] ) => { - return SavedObjectsRepository.createRepository( - migrator, - this.typeRegistry, - opensearchDashboardsConfig.index, - opensearchClient, - includedHiddenTypes - ); + if (this.respositoryFactoryProvider) { + return this.respositoryFactoryProvider({ + migrator, + typeRegistry: this.typeRegistry, + includedHiddenTypes, + }); + } else { + return SavedObjectsRepository.createRepository( + migrator, + this.typeRegistry, + opensearchDashboardsConfig.index, + opensearchClient, + includedHiddenTypes + ); + } }; const repositoryFactory: SavedObjectsRepositoryFactory = { diff --git a/src/core/server/saved_objects/service/lib/scoped_client_provider.ts b/src/core/server/saved_objects/service/lib/scoped_client_provider.ts index 439cd31afa9c..fea1a1641a6f 100644 --- a/src/core/server/saved_objects/service/lib/scoped_client_provider.ts +++ b/src/core/server/saved_objects/service/lib/scoped_client_provider.ts @@ -31,8 +31,31 @@ import { PriorityCollection } from './priority_collection'; import { SavedObjectsClientContract } from '../../types'; import { SavedObjectsRepositoryFactory } from '../../saved_objects_service'; -import { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry'; +import { + ISavedObjectTypeRegistry, + SavedObjectTypeRegistry, +} from '../../saved_objects_type_registry'; import { OpenSearchDashboardsRequest } from '../../../http'; +import { ISavedObjectsRepository } from './repository'; +import { IOpenSearchDashboardsMigrator } from '../../migrations'; + +/** + * Options passed to each SavedObjectRepositoryFactoryProvider to aid in creating the repository instance. + * @public + */ +export interface SavedObjectsRepositoryOptions { + migrator: IOpenSearchDashboardsMigrator; + typeRegistry: SavedObjectTypeRegistry; + includedHiddenTypes: string[]; +} + +/** + * Provider to invoke to a factory function for creating ISavedObjectRepository {@link ISavedObjectRepository} instances. + * @public + */ +export type SavedObjectRepositoryFactoryProvider = ( + options: SavedObjectsRepositoryOptions +) => ISavedObjectsRepository; /** * Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. diff --git a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts index 7e10a68c2d1f..3b1eea303f23 100644 --- a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts +++ b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts @@ -28,7 +28,6 @@ * under the License. */ -import Bluebird from 'bluebird'; import { createSavedObjectClass } from './saved_object'; import { SavedObject, @@ -76,16 +75,16 @@ describe('Saved Object', () => { */ function stubOpenSearchResponse(mockDocResponse: SimpleSavedObject<SavedObjectAttributes>) { // Stub out search for duplicate title: - savedObjectsClientStub.get = jest.fn().mockReturnValue(Bluebird.resolve(mockDocResponse)); - savedObjectsClientStub.update = jest.fn().mockReturnValue(Bluebird.resolve(mockDocResponse)); + savedObjectsClientStub.get = jest.fn().mockReturnValue(Promise.resolve(mockDocResponse)); + savedObjectsClientStub.update = jest.fn().mockReturnValue(Promise.resolve(mockDocResponse)); savedObjectsClientStub.find = jest .fn() - .mockReturnValue(Bluebird.resolve({ savedObjects: [], total: 0 })); + .mockReturnValue(Promise.resolve({ savedObjects: [], total: 0 })); savedObjectsClientStub.bulkGet = jest .fn() - .mockReturnValue(Bluebird.resolve({ savedObjects: [mockDocResponse] })); + .mockReturnValue(Promise.resolve({ savedObjects: [mockDocResponse] })); } function stubSavedObjectsClientCreate( @@ -94,7 +93,7 @@ describe('Saved Object', () => { ) { savedObjectsClientStub.create = jest .fn() - .mockReturnValue(resolve ? Bluebird.resolve(resp) : Bluebird.reject(resp)); + .mockReturnValue(resolve ? Promise.resolve(resp) : Promise.reject(resp)); } /** @@ -193,7 +192,7 @@ describe('Saved Object', () => { return createInitializedSavedObject({ type: 'dashboard', id: myId }).then((savedObject) => { savedObjectsClientStub.create = jest.fn().mockImplementation(() => { expect(savedObject.id).toBe(myId); - return Bluebird.resolve({ id: myId }); + return Promise.resolve({ id: myId }); }); savedObject.copyOnSave = false; @@ -227,7 +226,7 @@ describe('Saved Object', () => { return createInitializedSavedObject({ type: 'dashboard', id }).then((savedObject) => { savedObjectsClientStub.create = jest.fn().mockImplementation(() => { expect(savedObject.isSaving).toBe(true); - return Bluebird.resolve({ + return Promise.resolve({ type: 'dashboard', id, _version: 'foo', @@ -246,7 +245,7 @@ describe('Saved Object', () => { return createInitializedSavedObject({ type: 'dashboard' }).then((savedObject) => { savedObjectsClientStub.create = jest.fn().mockImplementation(() => { expect(savedObject.isSaving).toBe(true); - return Bluebird.reject(''); + return Promise.reject(); }); expect(savedObject.isSaving).toBe(false); @@ -681,7 +680,7 @@ describe('Saved Object', () => { ); indexPattern.title = indexPattern.id!; savedObject.searchSource!.setField('index', indexPattern); - return Bluebird.resolve(indexPattern); + return Promise.resolve(indexPattern); }); expect(!!savedObject.searchSource!.getField('index')).toBe(false); diff --git a/src/plugins/ui_actions/README.md b/src/plugins/ui_actions/README.md index 28e3b2d63d2e..4431a47a06ed 100644 --- a/src/plugins/ui_actions/README.md +++ b/src/plugins/ui_actions/README.md @@ -97,3 +97,12 @@ Use the UI actions explorer in the Developer examples to learn more about the se ```sh yarn start --run-examples ``` + +## Action Properties + +Refer to [./public/actions/action.ts](./public/actions/action.ts) for all properties, keeping in mind it extends the [presentable](./public/util/presentable.ts) interface. Here are some properties that provide special functionality and customization. + +- `order` is used when there is more than one action matched to a trigger and within context menus. Higher numbers are displayed first. +- `getDisplayName` is a function that can return either a string or a JSX element. Returning a JSX element allows flexibility with formatting. +- `getIconType` can be used to add an icon before the display name. +- `grouping` determines where this item should appear as a submenu. Each group can also contain a category, which is used within context menus to organize similar groups into the same section of the menu. See examples explorer for more details about what this looks like within a context menu. diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts index b9afca9fb99c..e70561bea221 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts @@ -448,3 +448,123 @@ test('groups with deep nesting', async () => { ] `); }); + +// Tests with: +// a regular action +// a group with 2 actions uncategorized +// a group with 2 actions with a category of "test-category" and low order of 10 +// a group with 1 actions with a category of "test-category" and high order of 20 +test('groups with categories and order', async () => { + const grouping1 = [ + { + id: 'test-group', + getDisplayName: () => 'Test group', + getIconType: () => 'bell', + }, + ]; + const grouping2 = [ + { + id: 'test-group-2', + getDisplayName: () => 'Test group 2', + getIconType: () => 'bell', + category: 'test-category', + order: 10, + }, + ]; + const grouping3 = [ + { + id: 'test-group-3', + getDisplayName: () => 'Test group 3', + getIconType: () => 'bell', + category: 'test-category', + order: 20, + }, + ]; + + const actions = [ + createTestAction({ + dispayName: 'Foo 1', + }), + createTestAction({ + dispayName: 'Bar 1', + grouping: grouping1, + }), + createTestAction({ + dispayName: 'Bar 2', + grouping: grouping1, + }), + createTestAction({ + dispayName: 'Qux 1', + grouping: grouping2, + }), + createTestAction({ + dispayName: 'Qux 2', + grouping: grouping2, + }), + // It is expected that, because there is only 1 action within this group, + // it will be added to the mainMenu as a single item, but next to other + // groups of the same category. When a group has a category, but only one + // item, we just add that single item; otherwise, we add a link to the group + createTestAction({ + dispayName: 'Waldo 1', + grouping: grouping3, + }), + ]; + const menu = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: 'TEST' as any })), + }); + + expect(menu.map(resultMapper)).toMatchInlineSnapshot(` + Array [ + Object { + "items": Array [ + Object { + "name": "Foo 1", + }, + Object { + "isSeparator": true, + }, + Object { + "name": "Test group", + }, + Object { + "isSeparator": true, + }, + Object { + "name": "Waldo 1", + }, + Object { + "name": "Test group 2", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Bar 1", + }, + Object { + "name": "Bar 2", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Qux 1", + }, + Object { + "name": "Qux 2", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Waldo 1", + }, + ], + }, + ] + `); +}); diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx index 6d69be1f3faa..81710767e0a9 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx @@ -64,6 +64,8 @@ type PanelDescriptor = EuiContextMenuPanelDescriptor & { _level?: number; _icon?: string; items: ItemDescriptor[]; + _category?: string; + _order?: number; }; const onClick = (action: Action, context: ActionExecutionContext<object>, close: () => void) => ( @@ -125,7 +127,7 @@ const removeItemMetaFields = (items: ItemDescriptor[]): EuiContextMenuPanelItemD const removePanelMetaFields = (panels: PanelDescriptor[]): EuiContextMenuPanelDescriptor[] => { const euiPanels: EuiContextMenuPanelDescriptor[] = []; for (const panel of panels) { - const { _level: omit, _icon: omit2, ...rest } = panel; + const { _level: omit, _icon: omit2, _category: omit3, _order: omit4, ...rest } = panel; euiPanels.push({ ...rest, items: removeItemMetaFields(rest.items) }); } return euiPanels; @@ -179,6 +181,8 @@ export async function buildContextMenuForActions({ items: [], _level: i, _icon: group.getIconType ? group.getIconType(context) : 'empty', + _category: group.category, + _order: group.order, }; // If there are multiple groups and this is not the first group, @@ -231,17 +235,57 @@ export async function buildContextMenuForActions({ // Any additional items are hidden behind a "more" item wrapMainPanelItemsIntoSubmenu(panels, 'mainMenu'); + // This will be used to store items that eventually are placed into the + // mainMenu panel. Specifying a category allows for placing groups into the + // mainMenu so they appear without the separator between them. + const categories = {}; + for (const panel of Object.values(panels)) { - // If the panel is a root-level panel, such as the parent of a group, - // then create mainMenu item for this panel - if (panel._level === 0) { + // Do nothing if not root-level panel, such as the parent of a group + if (panel._level !== 0) { + continue; + } + + // Proceed to create mainMenu item for this panel + + // If a category is specified, store either a link to the panel or the + // item within to that category. We will deal with the category after + // looping through all panels. + if (panel._category) { + // Create array to store category items + if (!categories[panel._category]) { + categories[panel._category] = []; + } + + // If multiple items in the panel, store a link to this panel into the category. + // Otherwise, just store the single item into the category. + if (panel.items.length > 1) { + categories[panel._category].push({ + order: panel._order, + items: [ + { + name: panel.title || panel.id, + icon: panel._icon || 'empty', + panel: panel.id, + }, + ], + }); + } else { + categories[panel._category].push({ + order: panel._order || 0, + items: panel.items, + }); + } + } else { + // If no category, continue with adding items to the mainMenu + // Add separator with unique key if needed if (panels.mainMenu.items.length) { panels.mainMenu.items.push({ isSeparator: true, key: `${panel.id}separator` }); } // If a panel has more than one child, then allow items to be grouped - // and link to it in the mainMenu. Otherwise, flatten the group. + // and link to it in the mainMenu. Otherwise, link to the single item. // Note: this only happens on the root level panels, not for inner groups. if (panel.items.length > 1) { panels.mainMenu.items.push({ @@ -255,6 +299,27 @@ export async function buildContextMenuForActions({ } } + // For each category, add a separator before each one and then add category items. + // This is for the mainMenu panel. + Object.keys(categories).forEach((key) => { + // Get the items sorted by group order, allowing for groups within categories + // to be ordered. A category consists of an order and its items. + // Higher orders are sorted to the top. + const sortedEntries = categories[key].sort((a, b) => b.order - a.order); + const sortedItems = sortedEntries.reduce( + (items, category) => [...items, ...category.items], + [] + ); + + // Add separator with unique key if needed + if (panels.mainMenu.items.length) { + panels.mainMenu.items.push({ isSeparator: true, key: `${key}separator` }); + } + + panels.mainMenu.items.push(...sortedItems); + }); + const panelList = Object.values(panels); + return removePanelMetaFields(panelList); } diff --git a/src/plugins/ui_actions/public/util/presentable.ts b/src/plugins/ui_actions/public/util/presentable.ts index 428644e1c2c6..9aaeada8a16e 100644 --- a/src/plugins/ui_actions/public/util/presentable.ts +++ b/src/plugins/ui_actions/public/util/presentable.ts @@ -94,6 +94,14 @@ export interface PresentableGroup<Context extends object = object> Pick<Presentable<Context>, 'getDisplayName' | 'getDisplayNameTooltip' | 'getIconType' | 'order'> > { id: string; + /** + * This allows groups to be categorized with other groups. Within a UI action + * context menu, this means that an item, which links to a group, will be + * placed in the menu adjacent to similar items that link to groups of the + * same category. + * See PanelGroupOptionsAndContextActions example to learn more. + */ + category?: string; } export type PresentableGrouping<Context extends object = object> = Array<PresentableGroup<Context>>; diff --git a/src/plugins/vis_type_table/public/components/table_vis_app.scss b/src/plugins/vis_type_table/public/components/table_vis_app.scss index aafcd40e7382..f0b82a1cd8d8 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_app.scss +++ b/src/plugins/vis_type_table/public/components/table_vis_app.scss @@ -17,11 +17,6 @@ flex: 0 0 auto; } -// Style for table component title -.visTable__component__title { - text-align: center; -} - // Modifier for visTables__group when displayed in columns .visTable__groupInColumns { flex-direction: row; diff --git a/src/plugins/vis_type_table/public/components/table_vis_component.tsx b/src/plugins/vis_type_table/public/components/table_vis_component.tsx index 1b16ec170a84..79cf768f66e8 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_component.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_component.tsx @@ -104,7 +104,7 @@ export const TableVisComponent = ({ return ( <> {title && ( - <EuiTitle size="xs" className="visTable__component__title"> + <EuiTitle size="xs" className="eui-textCenter"> <h3>{title}</h3> </EuiTitle> )} From 12b6d44d2a20751d1ea858496527ce571db1e586 Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Mon, 19 Jun 2023 11:43:58 -0700 Subject: [PATCH 29/37] Group ViewEvents option and bug fix (#4309) Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --- src/plugins/vis_augmenter/public/utils/utils.ts | 5 +++-- .../actions/view_events_option_action.tsx | 10 ++++++++++ .../public/view_events_flyout/components/utils.tsx | 11 ++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index ef643d87c21e..77aa14a204c8 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -23,7 +23,7 @@ import { PLUGIN_AUGMENTATION_ENABLE_SETTING } from '../../common/constants'; import { getUISettings } from '../services'; import { IUiSettingsClient } from '../../../../core/public'; -export const isEligibleForVisLayers = (vis: Vis): boolean => { +export const isEligibleForVisLayers = (vis: Vis, uiSettingsClient?: IUiSettingsClient): boolean => { // Only support date histogram and ensure there is only 1 x-axis and it has to be on the bottom. // Additionally to have a valid x-axis, there needs to be a segment aggregation const hasValidXaxis = @@ -45,8 +45,9 @@ export const isEligibleForVisLayers = (vis: Vis): boolean => { (seriesParam: { type: string }) => seriesParam.type === 'line' ) && vis.params?.type === 'line'; + // Checks if the augmentation setting is enabled - const config = getUISettings(); + const config = uiSettingsClient ?? getUISettings(); const isAugmentationEnabled = config.get(PLUGIN_AUGMENTATION_ENABLE_SETTING); return isAugmentationEnabled && hasValidXaxis && hasCorrectAggregationCount && hasOnlyLineSeries; }; diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx index bb8774306d47..6410e8a13634 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/actions/view_events_option_action.tsx @@ -20,6 +20,16 @@ export class ViewEventsOptionAction implements Action<EmbeddableContext> { public readonly id = VIEW_EVENTS_OPTION_ACTION; public order = 1; + public grouping: Action['grouping'] = [ + { + id: VIEW_EVENTS_OPTION_ACTION, + getDisplayName: this.getDisplayName, + getIconType: this.getIconType, + category: 'vis_augmenter', + order: 10, + }, + ]; + constructor() {} public getIconType(): EuiIconType { diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx index 7e1c8900d64b..aa07c497fcad 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx @@ -4,7 +4,7 @@ */ import { get } from 'lodash'; -import { ErrorEmbeddable } from '../../../../embeddable/public'; +import { EmbeddableStart, ErrorEmbeddable } from '../../../../embeddable/public'; import { VisualizeEmbeddable, VisualizeInput } from '../../../../visualizations/public'; import { getEmbeddable, getQueryService } from '../../services'; import { @@ -47,9 +47,14 @@ function getValueAxisPositions(embeddable: VisualizeEmbeddable): { left: boolean * Fetching the base vis to show in the flyout, based on the saved object ID. Add constraints * such that it is static and won't auto-refresh within the flyout. * @param savedObjectId the saved object id of the base vis + * @param embeddableStart Optional EmbeddableStart passed in for plugins to utilize the function */ -export async function fetchVisEmbeddable(savedObjectId: string): Promise<VisualizeEmbeddable> { - const embeddableVisFactory = getEmbeddable().getEmbeddableFactory('visualization'); +export async function fetchVisEmbeddable( + savedObjectId: string, + embeddableStart?: EmbeddableStart +): Promise<VisualizeEmbeddable> { + const embeddableLoader = embeddableStart ?? getEmbeddable(); + const embeddableVisFactory = embeddableLoader.getEmbeddableFactory('visualization'); const contextInput = { filters: getQueryService().filterManager.getFilters(), query: getQueryService().queryString.getQuery(), From 4536bb8c06b3af85d9408386afc883f3d085dcc5 Mon Sep 17 00:00:00 2001 From: Ashish Agrawal <ashish81394@gmail.com> Date: Wed, 21 Jun 2023 13:50:41 -0700 Subject: [PATCH 30/37] Allow queryStart to be passed into fetchVisEmbeddable (#4336) Signed-off-by: Ashish Agrawal <ashisagr@amazon.com> --- .../public/view_events_flyout/components/utils.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx index aa07c497fcad..4c06d3b87f0c 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/utils.tsx @@ -14,6 +14,7 @@ import { VisLayer, } from '../../types'; import { EventVisEmbeddableItem, EventVisEmbeddablesMap } from './types'; +import { QueryStart } from '../../../../data/public'; export function getErrorMessage(errorEmbeddable: ErrorEmbeddable): string { return errorEmbeddable.error instanceof Error @@ -48,17 +49,20 @@ function getValueAxisPositions(embeddable: VisualizeEmbeddable): { left: boolean * such that it is static and won't auto-refresh within the flyout. * @param savedObjectId the saved object id of the base vis * @param embeddableStart Optional EmbeddableStart passed in for plugins to utilize the function + * @param queryServiceLoader Optional QueryStart passed in for plugins to utilize the function */ export async function fetchVisEmbeddable( savedObjectId: string, - embeddableStart?: EmbeddableStart + embeddableStart?: EmbeddableStart, + queryStart?: QueryStart ): Promise<VisualizeEmbeddable> { const embeddableLoader = embeddableStart ?? getEmbeddable(); const embeddableVisFactory = embeddableLoader.getEmbeddableFactory('visualization'); + const queryService = queryStart ?? getQueryService(); const contextInput = { - filters: getQueryService().filterManager.getFilters(), - query: getQueryService().queryString.getQuery(), - timeRange: getQueryService().timefilter.timefilter.getTime(), + filters: queryService.filterManager.getFilters(), + query: queryService.queryString.getQuery(), + timeRange: queryService.timefilter.timefilter.getTime(), }; const embeddable = (await embeddableVisFactory?.createFromSavedObject(savedObjectId, { From 45ecc1fbd9fa6483d0846f38e0fdd5a6ebb06c69 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 22 Jun 2023 10:28:46 -0700 Subject: [PATCH 31/37] [Feature Anywhere] Fix legacy tests / bug fixes (#4327) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../vis_type_vega/public/data_model/vega_parser.ts | 2 +- .../vis_type_vega/public/expressions/vega_fn.ts | 7 ------- .../vis_type_vega/public/vega_view/vega_base_view.js | 1 - .../vis_type_vega/public/vega_visualization.js | 4 ---- .../vis_type_vislib/public/line_to_expression.ts | 1 - .../public/embeddable/visualize_embeddable.ts | 11 ++++++++--- yarn.lock | 12 ++++++------ 7 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index b73e26ac1af9..4bdd724dbead 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -203,7 +203,7 @@ The URL is an identifier only. OpenSearch Dashboards and your browser will never // top-level height/width values. // See limitations: https://vega.github.io/vega-lite/docs/size.html#limitations const showPointInTimeEvents = - this.visibleVisLayers.get(VisLayerTypes.PointInTimeEvents) === true; + this.visibleVisLayers?.get(VisLayerTypes.PointInTimeEvents) === true; let autosize = this.spec.autosize; let useResize = true; diff --git a/src/plugins/vis_type_vega/public/expressions/vega_fn.ts b/src/plugins/vis_type_vega/public/expressions/vega_fn.ts index cd9cf976873a..6043b851a35c 100644 --- a/src/plugins/vis_type_vega/public/expressions/vega_fn.ts +++ b/src/plugins/vis_type_vega/public/expressions/vega_fn.ts @@ -48,7 +48,6 @@ type Output = Promise<Render<RenderValue>>; interface Arguments { spec: string; - savedObjectId: string; } export interface VisParams { @@ -84,11 +83,6 @@ export const createVegaFn = ( default: '', help: '', }, - savedObjectId: { - types: ['string'], - default: '', - help: '', - }, }, async fn(input, args, context) { const vegaRequestHandler = createVegaRequestHandler(dependencies, context); @@ -108,7 +102,6 @@ export const createVegaFn = ( visType: 'vega', visConfig: { spec: args.spec, - savedObjectId: args.savedObjectId, }, }, }; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 043cc982dcea..71a7dd6cb48d 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -86,7 +86,6 @@ export class VegaBaseView { this._destroyHandlers = []; this._initialized = false; this._enableExternalUrls = getEnableExternalUrls(); - this._visInput = opts.visInput; } async init() { diff --git a/src/plugins/vis_type_vega/public/vega_visualization.js b/src/plugins/vis_type_vega/public/vega_visualization.js index cf4f0907acd4..379670bda413 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.js +++ b/src/plugins/vis_type_vega/public/vega_visualization.js @@ -29,7 +29,6 @@ */ import { i18n } from '@osd/i18n'; -import { get } from 'lodash'; import { getNotifications, getData } from './services'; export const createVegaVisualization = ({ getServiceSettings }) => @@ -92,9 +91,6 @@ export const createVegaVisualization = ({ getServiceSettings }) => filterManager, timefilter, externalAction: this._vis.API.events.externalAction, - visInput: { - savedObjectId: get(this._vis, 'params.savedObjectId'), - }, }; if (vegaParser.useMap) { diff --git a/src/plugins/vis_type_vislib/public/line_to_expression.ts b/src/plugins/vis_type_vislib/public/line_to_expression.ts index 1fb698a52d8b..8650c6013801 100644 --- a/src/plugins/vis_type_vislib/public/line_to_expression.ts +++ b/src/plugins/vis_type_vislib/public/line_to_expression.ts @@ -65,7 +65,6 @@ export const toExpressionAst = async (vis: Vis, params: any) => { // spec via 'line_vega_spec' fn, then set as the arg for the final 'vega' fn const vegaFn = buildExpressionFunction<VegaExpressionFunctionDefinition>('vega', { spec: vegaSpecFnExpressionBuilder, - savedObjectId: get(vis, 'id', ''), }); const ast = buildExpression([opensearchaggsFn, vegaFn]); return ast.toAst(); diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 505c7b045cb4..3682c615d08c 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -62,7 +62,7 @@ import { VIS_EVENT_TO_TRIGGER } from './events'; import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { TriggerId } from '../../../ui_actions/public'; import { SavedObjectAttributes } from '../../../../core/types'; -import { AttributeService } from '../../../dashboard/public'; +import { AttributeService, DASHBOARD_CONTAINER_TYPE } from '../../../dashboard/public'; import { SavedVisualizationsLoader } from '../saved_visualizations'; import { SavedAugmentVisLoader, @@ -417,8 +417,13 @@ export class VisualizeEmbeddable this.abortController = new AbortController(); const abortController = this.abortController; - // By waiting for this to complete, this.visLayers will be populated - await this.populateVisLayers(); + // By waiting for this to complete, this.visLayers will be populated. + // Note we only fetch when in the context of a dashboard - we do not + // show events or have event functionality when in the vis edit view. + const isInDashboard = this.parent?.type === DASHBOARD_CONTAINER_TYPE; + if (isInDashboard) { + await this.populateVisLayers(); + } this.expression = await buildPipeline(this.vis, { timefilter: this.timefilter, diff --git a/yarn.lock b/yarn.lock index e1985b3cd645..42ec3b13a2bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4933,9 +4933,9 @@ axios@^0.27.2: form-data "^4.0.0" axios@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383" - integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -6126,9 +6126,9 @@ compare-versions@3.5.1: integrity sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg== compare-versions@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.1.tgz#14c6008436d994c3787aba38d4087fabe858555e" - integrity sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ== + version "5.0.3" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.3.tgz#a9b34fea217472650ef4a2651d905f42c28ebfd7" + integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== component-emitter@^1.2.1, component-emitter@^1.3.0: version "1.3.0" From f028e8d7b0843da6cd2db9cdc474e4053b04f4f5 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 29 Jun 2023 10:20:00 -0700 Subject: [PATCH 32/37] Fix unrelated changes to be synced with main (#4414) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .github/workflows/add_untriaged_label.yml | 19 ------------------ CHANGELOG.md | 1 + .../notifications/toasts/toasts_api.tsx | 4 ---- .../data_source/server/data_source_service.ts | 20 +------------------ .../server/routes/test_connection.ts | 1 + yarn.lock | 14 ++++++------- 6 files changed, 10 insertions(+), 49 deletions(-) delete mode 100644 .github/workflows/add_untriaged_label.yml diff --git a/.github/workflows/add_untriaged_label.yml b/.github/workflows/add_untriaged_label.yml deleted file mode 100644 index 15b9a5565125..000000000000 --- a/.github/workflows/add_untriaged_label.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Apply 'untriaged' label during issue lifecycle - -on: - issues: - types: [opened, reopened, transferred] - -jobs: - apply-label: - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@v6 - with: - script: | - github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['untriaged'] - }) diff --git a/CHANGELOG.md b/CHANGELOG.md index dadb1948e340..7502ed1191f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add plugin manifest config to define OpenSearch plugin dependency and verify if it is installed on the cluster ([#3116](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3116)) - Replace re2 with RegExp in timeline and add unit tests ([#3908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3908)) +- Hide any output from use_node checking for Node compatibility ([#4237](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4237)) - Add category option within groups for context menus ([#4144](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4144)) - [Saved Object Service] Add Repository Factory Provider ([#4149](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4149)) - [Multiple DataSource] Backend support for adding sample data ([#4268](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4268)) diff --git a/src/core/public/notifications/toasts/toasts_api.tsx b/src/core/public/notifications/toasts/toasts_api.tsx index 4a30dc6dab9e..76d0bf9cf9b2 100644 --- a/src/core/public/notifications/toasts/toasts_api.tsx +++ b/src/core/public/notifications/toasts/toasts_api.tsx @@ -87,10 +87,6 @@ export interface ErrorToastOptions extends ToastOptions { * message will still be shown in the detailed error modal. */ toastMessage?: string; - /** - * Unique ID for the toast. Can be used to prevent duplicate toasts on re-renders. - */ - id?: string; } const normalizeToast = (toastOrTitle: ToastInput): ToastInputFields => { diff --git a/src/plugins/data_source/server/data_source_service.ts b/src/plugins/data_source/server/data_source_service.ts index 2a85d154e121..36a8d2a5ce5f 100644 --- a/src/plugins/data_source/server/data_source_service.ts +++ b/src/plugins/data_source/server/data_source_service.ts @@ -21,11 +21,6 @@ export interface DataSourceServiceSetup { options?: LegacyCallAPIOptions ) => Promise<unknown>; }; - - getTestingClient: ( - params: DataSourceClientParams, - dataSource: DataSourceAttributes - ) => Promise<OpenSearchClient>; } export class DataSourceService { private readonly openSearchClientPool: OpenSearchClientPool; @@ -48,19 +43,6 @@ export class DataSourceService { return configureClient(params, opensearchClientPoolSetup, config, this.logger); }; - const getTestingClient = ( - params: DataSourceClientParams, - dataSource: DataSourceAttributes - ): Promise<OpenSearchClient> => { - return configureTestClient( - params, - dataSource, - opensearchClientPoolSetup, - config, - this.logger - ); - }; - const getDataSourceLegacyClient = (params: DataSourceClientParams) => { return { callAPI: ( @@ -78,7 +60,7 @@ export class DataSourceService { }; }; - return { getDataSourceClient, getDataSourceLegacyClient, getTestingClient }; + return { getDataSourceClient, getDataSourceLegacyClient }; } start() {} diff --git a/src/plugins/data_source/server/routes/test_connection.ts b/src/plugins/data_source/server/routes/test_connection.ts index 33f588b5a5ff..cba42517e535 100644 --- a/src/plugins/data_source/server/routes/test_connection.ts +++ b/src/plugins/data_source/server/routes/test_connection.ts @@ -9,6 +9,7 @@ import { AuthType, DataSourceAttributes, SigV4ServiceName } from '../../common/d import { DataSourceConnectionValidator } from './data_source_connection_validator'; import { DataSourceServiceSetup } from '../data_source_service'; import { CryptographyServiceSetup } from '../cryptography_service'; + export const registerTestConnectionRoute = ( router: IRouter, dataSourceServiceSetup: DataSourceServiceSetup, diff --git a/yarn.lock b/yarn.lock index 42ec3b13a2bf..1b61c9c2b2a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4933,9 +4933,9 @@ axios@^0.27.2: form-data "^4.0.0" axios@^1.1.3: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383" + integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -6126,9 +6126,9 @@ compare-versions@3.5.1: integrity sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg== compare-versions@^5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.3.tgz#a9b34fea217472650ef4a2651d905f42c28ebfd7" - integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== + version "5.0.1" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.1.tgz#14c6008436d994c3787aba38d4087fabe858555e" + integrity sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ== component-emitter@^1.2.1, component-emitter@^1.3.0: version "1.3.0" @@ -18972,4 +18972,4 @@ zlib@^1.0.5: zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" - integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== \ No newline at end of file From 70a9ee09b43707102f1431c1a40eed6ef9ff2208 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 29 Jun 2023 13:48:44 -0700 Subject: [PATCH 33/37] Fix event charts to render with vega (#4443) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../public/embeddable/visualize_embeddable.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 3682c615d08c..dd5e7a8500ff 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -418,10 +418,12 @@ export class VisualizeEmbeddable const abortController = this.abortController; // By waiting for this to complete, this.visLayers will be populated. - // Note we only fetch when in the context of a dashboard - we do not - // show events or have event functionality when in the vis edit view. - const isInDashboard = this.parent?.type === DASHBOARD_CONTAINER_TYPE; - if (isInDashboard) { + // Note we only fetch when in the context of a dashboard or in the view + // events flyout - we do not show events or have event functionality when + // in the vis edit view. + const shouldFetchVisLayers = + this.parent?.type === DASHBOARD_CONTAINER_TYPE || this.visAugmenterConfig?.inFlyout; + if (shouldFetchVisLayers) { await this.populateVisLayers(); } From 0496f6bd243c5c8af408cfe11fdb31523d924b60 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Fri, 30 Jun 2023 11:53:05 -0700 Subject: [PATCH 34/37] Update default setting; fix single snapshot (#4455) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- config/opensearch_dashboards.yml | 4 ++-- .../__snapshots__/relationships.test.tsx.snap | 13 +------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 64fad53ba605..73f31233a783 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -265,6 +265,6 @@ # Set the value of this setting to false to hide the help menu link to the OpenSearch Dashboards user survey # opensearchDashboards.survey.url: "https://survey.opensearch.org" -# Set the value of this setting to false to disable plugin augmentation on Dashboard -# vis_augmenter.pluginAugmentationEnabled: false +# Set the value of this setting to true to enable plugin augmentation on Dashboard +# vis_augmenter.pluginAugmentationEnabled: true diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap index 27fe54c3c9a3..a890c6977de1 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap @@ -643,21 +643,10 @@ exports[`Relationships should render augment-vis objects normally 1`] = ` hasBorder={true} > <EuiTitle + className="eui-textBreakWord" size="m" > <h2> - <EuiToolTip - content="augment-vis" - delay="regular" - position="top" - > - <EuiIcon - aria-label="augment-vis" - size="m" - type="savedObject" - /> - </EuiToolTip> -    MyAugmentVisObject </h2> </EuiTitle> From 4cbd72aa85e309cc44bbd23a3669cd73af8f4d57 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 5 Jul 2023 08:52:08 -0700 Subject: [PATCH 35/37] Add comment about decoupling from embeddables plugin (#4484) * Add comment about decoupling from embeddables plugin Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Update src/plugins/visualizations/public/embeddable/visualize_embeddable.ts Signed-off-by: Ashwin P Chandran <ashwinpc1993@gmail.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> Signed-off-by: Ashwin P Chandran <ashwinpc1993@gmail.com> Co-authored-by: Ashwin P Chandran <ashwinpc1993@gmail.com> --- .../visualizations/public/embeddable/visualize_embeddable.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index dd5e7a8500ff..3a7fdf2bfa5d 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -96,6 +96,10 @@ export interface VisualizeInput extends EmbeddableInput { }; savedVis?: SerializedVis; table?: unknown; + // TODO: This config, along with other VisAugmenter-related fields (visLayers, savedAugmentVisLoader) + // can be decoupled from embeddables plugin entirely. It is only used for changing the underlying + // visualization. Instead, we can use ReactExpressionRenderer for handling the rendering. + // For details, see https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4483 visAugmenterConfig?: VisAugmenterEmbeddableConfig; } From 1cdc0f6a9250e66df497b3ed633cde411fd895f1 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Wed, 5 Jul 2023 15:14:31 -0700 Subject: [PATCH 36/37] [Vis Augmenter] Minor follow up improvements (#4503) * Minor naming changes & comments Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * More nits Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Few more comments Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Expose saved objects common settings values Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> * Add changelog entry Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --------- Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- CHANGELOG.md | 1 + .../public/lib/panel/embeddable_panel.tsx | 2 ++ src/plugins/saved_objects/common/index.ts | 3 +++ .../saved_objects/server/ui_settings.ts | 11 +++++--- .../object_view/saved_object_view.tsx | 2 +- .../triggers/saved_object_delete_trigger.ts | 7 +++++ .../triggers/external_action_trigger.ts | 27 +------------------ src/plugins/vis_augmenter/common/constants.ts | 2 -- .../actions/saved_object_delete_action.ts | 2 +- .../view_events_flyout/components/styles.scss | 5 +--- .../components/view_events_flyout.tsx | 6 ++--- .../vis_augmenter/server/routes/stats.ts | 10 +++---- .../__snapshots__/helpers.test.js.snap | 6 ++--- .../public/expressions/helpers.test.js | 10 +++---- .../public/expressions/helpers.ts | 4 ++- .../public/expressions/line_vega_spec_fn.ts | 6 ++--- .../public/embeddable/visualize_embeddable.ts | 2 +- src/plugins/visualizations/public/plugin.ts | 1 - .../components/visualize_listing.tsx | 1 - 19 files changed, 46 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f77fd00b80..d54edc04772e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Bump OUI to `1.1.2` to make `anomalyDetection` icon available ([#4408](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4408)) - Add `color-scheme` to the root styling ([#4477](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4477)) - [Multiple DataSource] Frontend support for adding sample data ([#4412](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4412)) +- Enable plugins to augment visualizations with additional data and context ([#4361](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4361)) ### 🐛 Bug Fixes diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index 60eb3eb186a8..5a340d3a701c 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -83,6 +83,8 @@ interface Props { SavedObjectFinder: React.ComponentType<any>; stateTransfer?: EmbeddableStateTransfer; hideHeader?: boolean; + // TODO: the below hasBorder and hasShadow fields may be removed as part of + // https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4483 hasBorder?: boolean; hasShadow?: boolean; } diff --git a/src/plugins/saved_objects/common/index.ts b/src/plugins/saved_objects/common/index.ts index 2d63a566b477..248fd2f508f0 100644 --- a/src/plugins/saved_objects/common/index.ts +++ b/src/plugins/saved_objects/common/index.ts @@ -29,4 +29,7 @@ */ export const PER_PAGE_SETTING = 'savedObjects:perPage'; +export const PER_PAGE_VALUE = 20; + export const LISTING_LIMIT_SETTING = 'savedObjects:listingLimit'; +export const LISTING_LIMIT_VALUE = 1000; diff --git a/src/plugins/saved_objects/server/ui_settings.ts b/src/plugins/saved_objects/server/ui_settings.ts index bba4a7e61d89..32f2bda01bae 100644 --- a/src/plugins/saved_objects/server/ui_settings.ts +++ b/src/plugins/saved_objects/server/ui_settings.ts @@ -32,14 +32,19 @@ import { i18n } from '@osd/i18n'; import { schema } from '@osd/config-schema'; import { UiSettingsParams } from 'opensearch-dashboards/server'; -import { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '../common'; +import { + PER_PAGE_SETTING, + PER_PAGE_VALUE, + LISTING_LIMIT_SETTING, + LISTING_LIMIT_VALUE, +} from '../common'; export const uiSettings: Record<string, UiSettingsParams> = { [PER_PAGE_SETTING]: { name: i18n.translate('savedObjects.advancedSettings.perPageTitle', { defaultMessage: 'Objects per page', }), - value: 20, + value: PER_PAGE_VALUE, type: 'number', description: i18n.translate('savedObjects.advancedSettings.perPageText', { defaultMessage: 'Number of objects to show per page in the load dialog', @@ -51,7 +56,7 @@ export const uiSettings: Record<string, UiSettingsParams> = { defaultMessage: 'Objects listing limit', }), type: 'number', - value: 1000, + value: LISTING_LIMIT_VALUE, description: i18n.translate('savedObjects.advancedSettings.listingLimitText', { defaultMessage: 'Number of objects to fetch for the listing pages', }), diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index e5d37e0af8ff..47691fcdbb47 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -191,7 +191,7 @@ export class SavedObjectEdition extends Component< }; formatTitle = (object: SimpleSavedObject<any> | undefined) => { - return object?.attributes?.title ? `'${object.attributes.title}'` : ''; + return object?.attributes?.title ?? ''; }; redirectToListing() { diff --git a/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts b/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts index 9c3ced212d5d..88c25155fa72 100644 --- a/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts +++ b/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts @@ -6,6 +6,13 @@ import { i18n } from '@osd/i18n'; import { Trigger } from '../../../ui_actions/public'; +/** + * This action is currently being used behind-the-scenes in the vis_augmenter plugin + * to clean up related augment-vis saved objects when a visualization is deleted. + * This could be improved upon by potentially moving and maintaining this in the + * saved_objects plugin, expanding to other situations where automatic cleanup may + * be helpful, and communicating this better on the UI (modals, callouts, etc.) + */ export const SAVED_OBJECT_DELETE_TRIGGER = 'SAVED_OBJECT_DELETE_TRIGGER'; export const savedObjectDeleteTrigger: Trigger<'SAVED_OBJECT_DELETE_TRIGGER'> = { id: SAVED_OBJECT_DELETE_TRIGGER, diff --git a/src/plugins/ui_actions/public/triggers/external_action_trigger.ts b/src/plugins/ui_actions/public/triggers/external_action_trigger.ts index 87941fd47b88..37af88f9b545 100644 --- a/src/plugins/ui_actions/public/triggers/external_action_trigger.ts +++ b/src/plugins/ui_actions/public/triggers/external_action_trigger.ts @@ -1,31 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. */ import { i18n } from '@osd/i18n'; diff --git a/src/plugins/vis_augmenter/common/constants.ts b/src/plugins/vis_augmenter/common/constants.ts index 54d4edd37901..438e2b9c5ba3 100644 --- a/src/plugins/vis_augmenter/common/constants.ts +++ b/src/plugins/vis_augmenter/common/constants.ts @@ -8,8 +8,6 @@ export const APP_PATH = { }; export const APP_API = '/api/vis_augmenter'; -// used for limiting results received from the stats API -export const PER_PAGE_REQUEST_NUMBER = 50; export const PLUGIN_AUGMENTATION_ENABLE_SETTING = 'visualization:enablePluginAugmentation'; export const PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING = 'visualization:enablePluginAugmentation.maxPluginObjects'; diff --git a/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts index 2db6d1210448..aa03ac67bdd5 100644 --- a/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts +++ b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts @@ -29,7 +29,7 @@ export class SavedObjectDeleteAction implements Action<SavedObjectDeleteContext> } public async isCompatible({ type, savedObjectId }: SavedObjectDeleteContext) { - return type === 'visualization' && (savedObjectId ? true : false); + return type === 'visualization' && !!savedObjectId; } /** diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss b/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss index 03b78fc477f1..6358f6b51f3a 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/styles.scss @@ -38,14 +38,11 @@ $base-vis-min-height: 25vh; // Visualizations require the container to have a va overflow: auto; overflow-x: hidden; + overflow-y: hidden; scrollbar-gutter: stable both-edges; } } -.hide-y-scroll { - overflow-y: hidden; -} - .show-y-scroll { overflow-y: scroll; } diff --git a/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx b/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx index 96a9c66ac0a4..6cd688e78c8b 100644 --- a/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx +++ b/src/plugins/vis_augmenter/public/view_events_flyout/components/view_events_flyout.tsx @@ -123,12 +123,12 @@ export function ViewEventsFlyout(props: Props) { <EuiFlyoutBody> <EuiFlexGroup className="view-events-flyout__content" direction="column"> <EuiFlexItem - className="view-events-flyout__contentPanel hide-y-scroll date-range-panel-height" + className="view-events-flyout__contentPanel date-range-panel-height" grow={false} > <DateRangeItem timeRange={timeRange as TimeRange} reload={reload} /> </EuiFlexItem> - <EuiFlexItem className="view-events-flyout__contentPanel hide-y-scroll" grow={5}> + <EuiFlexItem className="view-events-flyout__contentPanel" grow={5}> <BaseVisItem embeddable={visEmbeddable as VisualizeEmbeddable} /> </EuiFlexItem> <EuiFlexItem className="view-events-flyout__contentPanel show-y-scroll" grow={5}> @@ -137,7 +137,7 @@ export function ViewEventsFlyout(props: Props) { /> </EuiFlexItem> <EuiFlexItem - className="view-events-flyout__contentPanel hide-y-scroll timeline-panel-height" + className="view-events-flyout__contentPanel timeline-panel-height" grow={false} > <TimelinePanel embeddable={timelineVisEmbeddable as VisualizeEmbeddable} /> diff --git a/src/plugins/vis_augmenter/server/routes/stats.ts b/src/plugins/vis_augmenter/server/routes/stats.ts index e8c4ae76bf24..ffc038233bc8 100644 --- a/src/plugins/vis_augmenter/server/routes/stats.ts +++ b/src/plugins/vis_augmenter/server/routes/stats.ts @@ -10,12 +10,8 @@ import { IRouter, SavedObjectsFindResponse, } from '../../../../core/server'; -import { - APP_API, - APP_PATH, - PER_PAGE_REQUEST_NUMBER, - AugmentVisSavedObjectAttributes, -} from '../../common'; +import { APP_API, APP_PATH, AugmentVisSavedObjectAttributes } from '../../common'; +import { PER_PAGE_VALUE } from '../../../saved_objects/common'; import { getAugmentVisSavedObjects, getStats } from './stats_helpers'; export const registerStatsRoute = (router: IRouter, logger: Logger) => { @@ -33,7 +29,7 @@ export const registerStatsRoute = (router: IRouter, logger: Logger) => { const savedObjectsClient = context.core.savedObjects.client; const augmentVisSavedObjects: SavedObjectsFindResponse<AugmentVisSavedObjectAttributes> = await getAugmentVisSavedObjects( savedObjectsClient, - PER_PAGE_REQUEST_NUMBER + PER_PAGE_VALUE ); const stats = getStats(augmentVisSavedObjects); return response.ok({ diff --git a/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap index 7515b07fb9d2..85552a05278d 100644 --- a/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap +++ b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`helpers createSpecFromDatatable() build complicated line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44,\\"col-2-3\\":60.9375},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154,\\"col-2-3\\":79.5},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144,\\"col-2-3\\":75.875},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133,\\"col-2-3\\":259.25},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149,\\"col-2-3\\":90},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152,\\"col-2-3\\":79.0625},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166,\\"col-2-3\\":85.25},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151,\\"col-2-3\\":92},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143,\\"col-2-3\\":90.75},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148,\\"col-2-3\\":92},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146,\\"col-2-3\\":83.25},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137,\\"col-2-3\\":98},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151,\\"col-2-3\\":87.4375},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157,\\"col-2-3\\":63.75},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151,\\"col-2-3\\":81.5625},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152,\\"col-2-3\\":100.6875},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142,\\"col-2-3\\":98},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151,\\"col-2-3\\":100.8125},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156,\\"col-2-3\\":85.8125},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153,\\"col-2-3\\":98},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162,\\"col-2-3\\":75.9375},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159,\\"col-2-3\\":73.625},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165,\\"col-2-3\\":72.8125},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149,\\"col-2-3\\":82.5},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94,\\"col-2-3\\":54}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"bottom\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}},{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Max products.min_price\\"}],\\"color\\":{\\"datum\\":\\"Max products.min_price\\"}}},{\\"mark\\":\\"rule\\",\\"encoding\\":{\\"x\\":{\\"type\\":\\"temporal\\",\\"field\\":\\"now_field\\"},\\"color\\":{\\"value\\":\\"red\\"},\\"size\\":{\\"value\\":1}}},{\\"mark\\":{\\"type\\":\\"rule\\",\\"color\\":\\"#E7664C\\",\\"strokeDash\\":[8,8]},\\"encoding\\":{\\"y\\":{\\"datum\\":100}}}],\\"transform\\":[{\\"calculate\\":\\"now()\\",\\"as\\":\\"now_field\\"}]}"`; +exports[`helpers createSpecFromXYChartDatatable() build complicated line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44,\\"col-2-3\\":60.9375},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154,\\"col-2-3\\":79.5},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144,\\"col-2-3\\":75.875},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133,\\"col-2-3\\":259.25},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149,\\"col-2-3\\":90},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152,\\"col-2-3\\":79.0625},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166,\\"col-2-3\\":85.25},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151,\\"col-2-3\\":92},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143,\\"col-2-3\\":90.75},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148,\\"col-2-3\\":92},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146,\\"col-2-3\\":83.25},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137,\\"col-2-3\\":98},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151,\\"col-2-3\\":87.4375},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157,\\"col-2-3\\":63.75},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151,\\"col-2-3\\":81.5625},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152,\\"col-2-3\\":100.6875},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142,\\"col-2-3\\":98},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151,\\"col-2-3\\":100.8125},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156,\\"col-2-3\\":85.8125},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153,\\"col-2-3\\":98},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162,\\"col-2-3\\":75.9375},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159,\\"col-2-3\\":73.625},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165,\\"col-2-3\\":72.8125},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149,\\"col-2-3\\":82.5},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94,\\"col-2-3\\":54}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"bottom\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}},{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Max products.min_price\\"}],\\"color\\":{\\"datum\\":\\"Max products.min_price\\"}}},{\\"mark\\":\\"rule\\",\\"encoding\\":{\\"x\\":{\\"type\\":\\"temporal\\",\\"field\\":\\"now_field\\"},\\"color\\":{\\"value\\":\\"red\\"},\\"size\\":{\\"value\\":1}}},{\\"mark\\":{\\"type\\":\\"rule\\",\\"color\\":\\"#E7664C\\",\\"strokeDash\\":[8,8]},\\"encoding\\":{\\"y\\":{\\"datum\\":100}}}],\\"transform\\":[{\\"calculate\\":\\"now()\\",\\"as\\":\\"now_field\\"}]}"`; -exports[`helpers createSpecFromDatatable() build empty chart if no x-axis is defined" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[]}"`; +exports[`helpers createSpecFromXYChartDatatable() build empty chart if no x-axis is defined" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[]}"`; -exports[`helpers createSpecFromDatatable() build simple line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":false,\\"orient\\":\\"left\\",\\"labels\\":true,\\"labelAngle\\":0},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}}]}"`; +exports[`helpers createSpecFromXYChartDatatable() build simple line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":false,\\"orient\\":\\"left\\",\\"labels\\":true,\\"labelAngle\\":0},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}}]}"`; diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.test.js b/src/plugins/vis_type_vega/public/expressions/helpers.test.js index b450997d5cdc..8630b3c69ada 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.test.js +++ b/src/plugins/vis_type_vega/public/expressions/helpers.test.js @@ -8,7 +8,7 @@ import { buildXAxis, buildYAxis, cleanString, - createSpecFromDatatable, + createSpecFromXYChartDatatable, formatDatatable, setupConfig, } from './helpers'; @@ -191,13 +191,13 @@ describe('helpers', function () { }); }); - describe('createSpecFromDatatable()', function () { + describe('createSpecFromXYChartDatatable()', function () { // Following 3 tests fail since they are persisting temporal data // which can cause snapshots to fail depending on the test env they are run on. it.skip('build simple line chart"', function () { expect( JSON.stringify( - createSpecFromDatatable( + createSpecFromXYChartDatatable( formatDatatable(JSON.parse(simpleDatatable)), JSON.parse(simpleVisParams), JSON.parse(simpleDimensions) @@ -209,7 +209,7 @@ describe('helpers', function () { it.skip('build empty chart if no x-axis is defined"', function () { expect( JSON.stringify( - createSpecFromDatatable( + createSpecFromXYChartDatatable( formatDatatable(JSON.parse(simpleDatatable)), JSON.parse(simpleVisParams), JSON.parse(noXAxisDimensions) @@ -221,7 +221,7 @@ describe('helpers', function () { it.skip('build complicated line chart"', function () { expect( JSON.stringify( - createSpecFromDatatable( + createSpecFromXYChartDatatable( formatDatatable(JSON.parse(complexDatatable)), JSON.parse(complexVisParams), JSON.parse(complexDimensions) diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.ts b/src/plugins/vis_type_vega/public/expressions/helpers.ts index bbe04ab411e9..1fffd91b0bdb 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.ts +++ b/src/plugins/vis_type_vega/public/expressions/helpers.ts @@ -168,7 +168,9 @@ const isXAxisColumn = (column: OpenSearchDashboardsDatatableColumn): boolean => return column.meta?.aggConfigParams?.interval !== undefined; }; -export const createSpecFromDatatable = ( +// Given a chart's underlying datatable, generate a vega-lite spec. +// Designed to be used with x-y / temporal visualizations only. +export const createSpecFromXYChartDatatable = ( datatable: OpenSearchDashboardsDatatable, visParams: VisParams, dimensions: VislibDimensions, diff --git a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts index 61a2f45b421c..63ea59114052 100644 --- a/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts +++ b/src/plugins/vis_type_vega/public/expressions/line_vega_spec_fn.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { cloneDeep, isEmpty, get } from 'lodash'; +import { cloneDeep, isEmpty } from 'lodash'; import { i18n } from '@osd/i18n'; import { ExpressionFunctionDefinition, @@ -21,7 +21,7 @@ import { addVisEventSignalsToSpecConfig, augmentEventChartSpec, } from '../../../vis_augmenter/public'; -import { formatDatatable, createSpecFromDatatable } from './helpers'; +import { formatDatatable, createSpecFromXYChartDatatable } from './helpers'; import { VegaVisualizationDependencies } from '../plugin'; type Input = OpenSearchDashboardsDatatable; @@ -89,7 +89,7 @@ export const createLineVegaSpecFn = ( table = addPointInTimeEventsLayersToTable(table, dimensions, pointInTimeEventsVisLayers); } - let spec = createSpecFromDatatable(table, visParams, dimensions, visAugmenterConfig); + let spec = createSpecFromXYChartDatatable(table, visParams, dimensions, visAugmenterConfig); if (!isEmpty(pointInTimeEventsVisLayers) && dimensions.x !== null) { spec = addPointInTimeEventsLayersToSpec(table, dimensions, spec); diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 3a7fdf2bfa5d..284fbc0b4d02 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -518,7 +518,7 @@ export class VisualizeEmbeddable this.visAugmenterConfig?.visLayerResourceIds === undefined ? visLayers : visLayers.filter((visLayer) => - this.visAugmenterConfig?.visLayerResourceIds?.includes(visLayer.pluginResource.id) + this.visAugmenterConfig.visLayerResourceIds.includes(visLayer.pluginResource.id) ); } diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 9b094c9ef754..3542e0cc26ff 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -213,7 +213,6 @@ export class VisualizationsPlugin chrome: core.chrome, overlays: core.overlays, visualizationTypes: types, - savedAugmentVisLoader, }); setSavedVisualizationsLoader(savedVisualizationsLoader); const savedSearchLoader = createSavedSearchesLoader({ diff --git a/src/plugins/visualize/public/application/components/visualize_listing.tsx b/src/plugins/visualize/public/application/components/visualize_listing.tsx index ec768e19885a..67c06665e4f2 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.tsx +++ b/src/plugins/visualize/public/application/components/visualize_listing.tsx @@ -29,7 +29,6 @@ */ import './visualize_listing.scss'; -import { get } from 'lodash'; import React, { useCallback, useRef, useMemo, useEffect } from 'react'; import { i18n } from '@osd/i18n'; import { useUnmount, useMount } from 'react-use'; From 04ae9cb847cd54dabc9847cace25fb8a0536cf8c Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen <ohltyler@amazon.com> Date: Thu, 6 Jul 2023 12:16:46 -0700 Subject: [PATCH 37/37] Add TODOs (#4513) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> --- .../object_view/saved_object_view.tsx | 3 +++ .../public/triggers/saved_object_delete_trigger.ts | 7 +++---- .../public/actions/saved_object_delete_action.ts | 3 +++ .../public/embeddable/visualize_embeddable.ts | 11 ++++++++--- .../application/components/visualize_listing.tsx | 4 ++++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index 47691fcdbb47..30627e69084c 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -170,6 +170,9 @@ export class SavedObjectEdition extends Component< buttonColor: 'danger', } ); + // TODO: This trigger should be maintained and emitted by the saved objects plugin + // when an obj is deleted. Tracking issue: + // https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4499 if (confirmed) { this.props.uiActions .getTrigger(SAVED_OBJECT_DELETE_TRIGGER) diff --git a/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts b/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts index 88c25155fa72..c9104ec4176a 100644 --- a/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts +++ b/src/plugins/saved_objects_management/public/triggers/saved_object_delete_trigger.ts @@ -7,11 +7,10 @@ import { i18n } from '@osd/i18n'; import { Trigger } from '../../../ui_actions/public'; /** - * This action is currently being used behind-the-scenes in the vis_augmenter plugin + * TODO: This action is currently being used behind-the-scenes in the vis_augmenter plugin * to clean up related augment-vis saved objects when a visualization is deleted. - * This could be improved upon by potentially moving and maintaining this in the - * saved_objects plugin, expanding to other situations where automatic cleanup may - * be helpful, and communicating this better on the UI (modals, callouts, etc.) + * This should be moved and maintained by the saved objects plugin. Tracking issue: + * https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4499 */ export const SAVED_OBJECT_DELETE_TRIGGER = 'SAVED_OBJECT_DELETE_TRIGGER'; export const savedObjectDeleteTrigger: Trigger<'SAVED_OBJECT_DELETE_TRIGGER'> = { diff --git a/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts index aa03ac67bdd5..56194e281540 100644 --- a/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts +++ b/src/plugins/vis_augmenter/public/actions/saved_object_delete_action.ts @@ -36,6 +36,9 @@ export class SavedObjectDeleteAction implements Action<SavedObjectDeleteContext> * If deletion of a vis saved object has been triggered, we want to clean up * any augment-vis saved objects that have a reference to this vis since it * is now stale. + * TODO: this should be automatically handled by the saved objects plugin, instead + * of this specific scenario in the vis_augmenter plugin. Tracking issue: + * https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4499 */ public async execute({ type, savedObjectId }: SavedObjectDeleteContext) { if (!(await this.isCompatible({ type, savedObjectId }))) { diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 284fbc0b4d02..e47052bfcd50 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -561,9 +561,14 @@ export class VisualizeEmbeddable )) as ExprVisLayers; const visLayers = exprVisLayers.layers; - // There may be some stale saved objs if any plugin resources have been deleted since last time - // data was fetched from them via the expression functions. Execute this trigger so any listening - // action can perform cleanup. + /** + * There may be some stale saved objs if any plugin resources have been deleted since last time + * data was fetched from them via the expression functions. Execute this trigger so any listening + * action can perform cleanup. + * + * TODO: this should be automatically handled by the saved objects plugin. Tracking issue: + * https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4499 + */ getUiActions().getTrigger(PLUGIN_RESOURCE_DELETE_TRIGGER).exec({ savedObjs: augmentVisSavedObjs, visLayers, diff --git a/src/plugins/visualize/public/application/components/visualize_listing.tsx b/src/plugins/visualize/public/application/components/visualize_listing.tsx index 67c06665e4f2..5dab2f11051c 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.tsx +++ b/src/plugins/visualize/public/application/components/visualize_listing.tsx @@ -141,6 +141,10 @@ export const VisualizeListing = () => { savedObjects.client .delete(item.savedObjectType, item.id) .then(() => { + /** + * TODO: this should be automatically handled by the saved objects plugin. Tracking issue: + * https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4499 + */ uiActions .getTrigger(SAVED_OBJECT_DELETE_TRIGGER) .exec({ type: item.savedObjectType, savedObjectId: item.id });
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 McC&3s#z!vv5Bqh|&Hw-a literal 0 HcmV?d00001 diff --git a/docs/plugins/img/initialization.png b/docs/plugins/img/initialization.png new file mode 100644 index 0000000000000000000000000000000000000000..ba5034694a47e94c57f15bb2ced4210c3105b7c9 GIT binary patch literal 439631 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 E|LC_4!TAWJAE(yerNx0FbChXNv<%96XVJ7@U5 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

X2<%YAgZn}RZXZ(;J>mB6`Y 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

s}ep&a}qH7 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

mv?d!R*Bb3akNAmPE?~r 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 z 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 z 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

~fc2h&23tq6UknD^c+aCuEjvNT0d85EF(5 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�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 literal 0 HcmV?d00001 diff --git a/docs/plugins/img/refresh.png b/docs/plugins/img/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..acc901edc03b7260d51188e024ea13a79e739258 GIT binary patch literal 232464 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 z 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