From 1687089561aea090ce755d265524e4e63a566680 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Thu, 22 Dec 2022 17:07:37 -0800 Subject: [PATCH 1/4] Collect VisLayers in VisualizeEmbeddable render flow Signed-off-by: Tyler Ohlsen --- src/plugins/vis_augmenter/common/types.ts | 60 -------- .../public/expressions/vis_layers.ts | 2 +- src/plugins/vis_augmenter/public/index.ts | 12 +- .../saved_augment_vis.test.ts | 29 ++-- .../saved_augment_vis/saved_augment_vis.ts | 2 +- .../saved_augment_vis_references.test.ts | 7 +- .../saved_augment_vis/utils/test_helpers.ts | 11 +- .../{common => public}/types.test.ts | 0 src/plugins/vis_augmenter/public/types.ts | 57 +++++++- .../{common => public/utils}/index.ts | 2 +- .../vis_augmenter/public/utils/utils.test.ts | 132 ++++++++++++++++++ .../vis_augmenter/public/utils/utils.ts | 54 +++++++ .../visualizations/opensearch_dashboards.json | 2 +- .../create_vis_embeddable_from_object.ts | 4 + .../public/embeddable/visualize_embeddable.ts | 58 +++++++- .../public/legacy/build_pipeline.ts | 3 + src/plugins/visualizations/public/services.ts | 5 + 17 files changed, 356 insertions(+), 84 deletions(-) delete mode 100644 src/plugins/vis_augmenter/common/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 ceb8b1973ba..00000000000 --- 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 ->; diff --git a/src/plugins/vis_augmenter/public/expressions/vis_layers.ts b/src/plugins/vis_augmenter/public/expressions/vis_layers.ts index f99ead0407e..8cfa7d4b4ae 100644 --- a/src/plugins/vis_augmenter/public/expressions/vis_layers.ts +++ b/src/plugins/vis_augmenter/public/expressions/vis_layers.ts @@ -4,7 +4,7 @@ */ import { ExpressionTypeDefinition } from '../../../expressions'; -import { VisLayers } from '../../common'; +import { VisLayers } from '../'; const name = 'vis_layers'; diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index cf736bf6d3e..1e4976d2170 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -18,4 +18,14 @@ export { SavedObjectOpenSearchDashboardsServicesWithAugmentVis, } from './saved_augment_vis'; -export { ISavedAugmentVis, VisLayerExpressionFn, AugmentVisSavedObject } from './types'; +export { + ISavedAugmentVis, + VisLayerExpressionFn, + AugmentVisSavedObject, + VisLayerFunctionDefinition, + VisLayer, + VisLayers, +} from './types'; + +export * from './expressions'; +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 index bc44a0ed6dc..51360f72c33 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 82e6e24a7e3..910ef0b9ea7 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 4a19b84dc40..1b5a0ad6cd9 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/utils/test_helpers.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/utils/test_helpers.ts index c237fa7551c..9bbfb8940b2 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 5ddd191cace..78e812ba53f 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -4,10 +4,54 @@ */ import { SavedObject } from '../../saved_objects/public'; -import { VisLayerTypes } from '../common'; +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 ISavedAugmentVis { id?: string; + title: string; description?: string; pluginResourceId: string; visName?: string; @@ -24,3 +68,14 @@ export interface VisLayerExpressionFn { } export interface AugmentVisSavedObject extends SavedObject, ISavedAugmentVis {} + +export interface VisLayerResponseValue { + visLayers: object; +} + +export type VisLayerFunctionDefinition = ExpressionFunctionDefinition< + string, + VisLayerResponseValue, + any, + Promise +>; 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 9f269633f30..079132ce99d 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 00000000000..3c770b1db8b --- /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 00000000000..efac67f6e79 --- /dev/null +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -0,0 +1,54 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get } from 'lodash'; +import { Vis } from '../../../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'; +}; + +export const getAugmentVisSavedObjs = async ( + visId: string | undefined, + loader: SavedAugmentVisLoader | undefined +): Promise => { + 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[]; + } +}; + +export const buildPipelineFromAugmentVisSavedObjs = (objs: ISavedAugmentVis[]): string => { + const visLayerExpressionFns = [] as Array< + ExpressionAstFunctionBuilder + >; + + try { + objs.forEach((obj: ISavedAugmentVis) => { + visLayerExpressionFns.push( + buildExpressionFunction( + obj.visLayerExpressionFn.name, + obj.visLayerExpressionFn.args + ) + ); + }); + 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 6223ffce380..b7c5e4ab9b4 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 03666a199dc..e09f789f9a6 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 f3808951d51..d8bb68417dc 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 = (o: T): Array => Object.keys(o) as Array; @@ -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,21 @@ export class VisualizeEmbeddable } this.abortController = new AbortController(); const abortController = this.abortController; + + let exprVisLayers = {} as ExprVisLayers; + // TODO: final eligibility will be defined as part of a separate effort. + // This includes not fetching any layers / not showing any layers, when in the + // edit context of the vis + // See https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 + if (isEligibleForVisLayers(this.vis)) { + exprVisLayers = await this.fetchVisLayers(expressionParams, abortController); + } + this.expression = await buildPipeline(this.vis, { timefilter: this.timefilter, timeRange: this.timeRange, abortSignal: this.abortController!.signal, + visLayers: !isEmpty(exprVisLayers) ? exprVisLayers.layers : ([] as VisLayers), }); if (this.handler && !abortController.signal.aborted) { @@ -465,4 +486,37 @@ export class VisualizeEmbeddable { showSaveModal: true, saveModalTitle } ); }; + + /** + * Collects any VisLayers from plugin expressions functions + * by fetching all AugmentVisSavedObjects that match the vis + * saved object ID + */ + fetchVisLayers = async ( + expressionParams: IExpressionLoaderParams, + abortController: AbortController + ): Promise => { + let exprVisLayers = {} as ExprVisLayers; + const augmentVisSavedObjs = await getAugmentVisSavedObjs( + this.vis.id, + this.savedAugmentVisLoader + ); + if (!isEmpty(augmentVisSavedObjs) && !abortController.signal.aborted) { + 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. + exprVisLayers = (await getExpressions().run( + visLayersPipeline, + visLayersPipelineInput, + expressionParams as Record + )) as ExprVisLayers; + } + return exprVisLayers; + }; } diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index 1cbb3bc3887..bd0e0d97678 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 '../../../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 e3f3ba56f6b..1ad0b37d76a 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('UISettings'); @@ -106,3 +107,7 @@ export const [getChrome, setChrome] = createGetterSetter('Chrome'); export const [getSavedSearchLoader, setSavedSearchLoader] = createGetterSetter( 'savedSearchLoader' ); + +export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetterSetter< + SavedAugmentVisLoader +>('SavedAugmentVisLoader'); From c0b877192aae03582bdbef3ee1858ad95a0563d4 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Tue, 7 Mar 2023 11:53:13 -0800 Subject: [PATCH 2/4] Refactor types; address comments Signed-off-by: Tyler Ohlsen --- .../vis_augmenter/public/expressions/index.ts | 2 +- .../expressions/{vis_layers.ts => types.ts} | 18 ++++++++-- src/plugins/vis_augmenter/public/index.ts | 10 ++---- .../public/saved_augment_vis/index.ts | 1 + .../public/saved_augment_vis/types.ts | 20 +++++++++++ src/plugins/vis_augmenter/public/types.ts | 34 ------------------- .../vis_augmenter/public/utils/utils.ts | 12 +++++++ .../public/embeddable/visualize_embeddable.ts | 2 +- 8 files changed, 53 insertions(+), 46 deletions(-) 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 diff --git a/src/plugins/vis_augmenter/public/expressions/index.ts b/src/plugins/vis_augmenter/public/expressions/index.ts index f7bcfbd083f..9f269633f30 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 8cfa7d4b4ae..b907e570e10 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 '../'; +import { ExpressionTypeDefinition, ExpressionFunctionDefinition } from '../../../expressions'; +import { VisLayers, VisLayerTypes } from '../'; const name = 'vis_layers'; @@ -31,3 +31,17 @@ export const visLayers: ExpressionTypeDefinition = { }, }, }; + +export type VisLayerFunctionDefinition = ExpressionFunctionDefinition< + string, + ExprVisLayers, + any, + Promise +>; + +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 1e4976d2170..e374cbe8d2c 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -18,14 +18,8 @@ export { SavedObjectOpenSearchDashboardsServicesWithAugmentVis, } from './saved_augment_vis'; -export { - ISavedAugmentVis, - VisLayerExpressionFn, - AugmentVisSavedObject, - VisLayerFunctionDefinition, - VisLayer, - VisLayers, -} 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 5ac3a159132..ce168020495 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/types.ts b/src/plugins/vis_augmenter/public/saved_augment_vis/types.ts new file mode 100644 index 00000000000..dee349cb900 --- /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/types.ts b/src/plugins/vis_augmenter/public/types.ts index 78e812ba53f..920a8db0284 100644 --- a/src/plugins/vis_augmenter/public/types.ts +++ b/src/plugins/vis_augmenter/public/types.ts @@ -3,9 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { SavedObject } from '../../saved_objects/public'; -import { ExpressionFunctionDefinition } from '../../expressions'; - export enum VisLayerTypes { PointInTimeEvents = 'PointInTimeEvents', } @@ -48,34 +45,3 @@ export const isPointInTimeEventsVisLayer = (obj: any) => { export const isValidVisLayer = (obj: any) => { return obj?.type in VisLayerTypes; }; - -export interface ISavedAugmentVis { - id?: string; - title: 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 {} - -export interface VisLayerResponseValue { - visLayers: object; -} - -export type VisLayerFunctionDefinition = ExpressionFunctionDefinition< - string, - VisLayerResponseValue, - any, - Promise ->; diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index efac67f6e79..e556f5c2a31 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -19,6 +19,11 @@ 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 @@ -32,6 +37,13 @@ export const getAugmentVisSavedObjs = async ( } }; +/** + * 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 => { const visLayerExpressionFns = [] as Array< ExpressionAstFunctionBuilder diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index d8bb68417dc..580094fd175 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -417,7 +417,7 @@ export class VisualizeEmbeddable timefilter: this.timefilter, timeRange: this.timeRange, abortSignal: this.abortController!.signal, - visLayers: !isEmpty(exprVisLayers) ? exprVisLayers.layers : ([] as VisLayers), + visLayers: isEmpty(exprVisLayers) ? ([] as VisLayers) : exprVisLayers.layers, }); if (this.handler && !abortController.signal.aborted) { From 4de53b3a5983a6e085760eca8d3bddccecbf89e2 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Wed, 8 Mar 2023 14:55:05 -0800 Subject: [PATCH 3/4] Simplify fetchVisLayers() and pipeline helper fns Signed-off-by: Tyler Ohlsen --- .../vis_augmenter/public/utils/utils.ts | 20 ++++-------- .../public/embeddable/visualize_embeddable.ts | 32 ++++++++++--------- .../public/legacy/build_pipeline.ts | 2 +- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index e556f5c2a31..c0d357d517e 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -4,7 +4,7 @@ */ import { get } from 'lodash'; -import { Vis } from '../../../visualizations/public'; +import { Vis } from '../../../../plugins/visualizations/public'; import { formatExpression, buildExpressionFunction, @@ -45,19 +45,13 @@ export const getAugmentVisSavedObjs = async ( * contains the results from each expression function that was ran. */ export const buildPipelineFromAugmentVisSavedObjs = (objs: ISavedAugmentVis[]): string => { - const visLayerExpressionFns = [] as Array< - ExpressionAstFunctionBuilder - >; - try { - objs.forEach((obj: ISavedAugmentVis) => { - visLayerExpressionFns.push( - buildExpressionFunction( - obj.visLayerExpressionFn.name, - obj.visLayerExpressionFn.args - ) - ); - }); + const visLayerExpressionFns = objs.map((obj: ISavedAugmentVis) => + buildExpressionFunction( + obj.visLayerExpressionFn.name, + obj.visLayerExpressionFn.args + ) + ) as Array>; const ast = buildExpression(visLayerExpressionFns).toAst(); return formatExpression(ast); } catch (e) { diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 580094fd175..113ea13f0ff 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -404,20 +404,13 @@ export class VisualizeEmbeddable this.abortController = new AbortController(); const abortController = this.abortController; - let exprVisLayers = {} as ExprVisLayers; - // TODO: final eligibility will be defined as part of a separate effort. - // This includes not fetching any layers / not showing any layers, when in the - // edit context of the vis - // See https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 - if (isEligibleForVisLayers(this.vis)) { - exprVisLayers = await this.fetchVisLayers(expressionParams, 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: isEmpty(exprVisLayers) ? ([] as VisLayers) : exprVisLayers.layers, + visLayers: visLayers, }); if (this.handler && !abortController.signal.aborted) { @@ -490,18 +483,26 @@ export class VisualizeEmbeddable /** * Collects any VisLayers from plugin expressions functions * by fetching all AugmentVisSavedObjects that match the vis - * saved object ID + * 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 => { - let exprVisLayers = {} as ExprVisLayers; + ): Promise => { const augmentVisSavedObjs = await getAugmentVisSavedObjs( this.vis.id, this.savedAugmentVisLoader ); - if (!isEmpty(augmentVisSavedObjs) && !abortController.signal.aborted) { + 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. @@ -511,12 +512,13 @@ export class VisualizeEmbeddable }; // 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. - exprVisLayers = (await getExpressions().run( + const exprVisLayers = (await getExpressions().run( visLayersPipeline, visLayersPipelineInput, expressionParams as Record )) as ExprVisLayers; + return exprVisLayers.layers; } - return exprVisLayers; + return [] as VisLayers; }; } diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index bd0e0d97678..23c939c111c 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 '../../../vis_augmenter/public'; +import { VisLayers } from '../../../../plugins/vis_augmenter/public'; const { isDateHistogramBucketAggConfig } = search.aggs; From f4a6386d114bea4b055b529975dca5fb871318ad Mon Sep 17 00:00:00 2001 From: Josh Romero Date: Wed, 8 Mar 2023 15:19:07 -0800 Subject: [PATCH 4/4] Update src/plugins/visualizations/public/embeddable/visualize_embeddable.ts Signed-off-by: Josh Romero --- .../visualizations/public/embeddable/visualize_embeddable.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 113ea13f0ff..c0d43cd521d 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -410,7 +410,7 @@ export class VisualizeEmbeddable timefilter: this.timefilter, timeRange: this.timeRange, abortSignal: this.abortController!.signal, - visLayers: visLayers, + visLayers, }); if (this.handler && !abortController.signal.aborted) {