Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VisBuilder] Add Capability to generate dynamic vega #7288

Merged
merged 9 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ const names: Record<string, string> = {
visualizations: i18n.translate('advancedSettings.categoryNames.visualizationsLabel', {
defaultMessage: 'Visualizations',
}),
visbuilder: i18n.translate('advancedSettings.categoryNames.visbuilderLabel', {
defaultMessage: 'VisBuilder',
}),
discover: i18n.translate('advancedSettings.categoryNames.discoverLabel', {
defaultMessage: 'Discover',
}),
Expand Down
1 change: 1 addition & 0 deletions src/plugins/expressions/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,4 @@ export {
UnmappedTypeStrings,
ExpressionValueRender as Render,
} from '../common';
export { getExpressionsService } from './services';
6 changes: 6 additions & 0 deletions src/plugins/vis_builder/common/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const VISBUILDER_ENABLE_VEGA_SETTING = 'visbuilder:enableVega';
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { validateSchemaState, validateAggregations } from '../utils/validations'
import { useTypedDispatch, useTypedSelector, setUIStateState } from '../utils/state_management';
import { useAggs, useVisualizationType } from '../utils/use';
import { PersistedState } from '../../../../visualizations/public';
import { VISBUILDER_ENABLE_VEGA_SETTING } from '../../../common/constants';

import hand_field from '../../assets/hand_field.svg';
import fields_bg from '../../assets/fields_bg.svg';
Expand All @@ -27,6 +28,7 @@ export const WorkspaceUI = () => {
notifications: { toasts },
data,
uiActions,
uiSettings,
},
} = useOpenSearchDashboards<VisBuilderServices>();
const { toExpression, ui } = useVisualizationType();
Expand All @@ -37,6 +39,7 @@ export const WorkspaceUI = () => {
filters: data.query.filterManager.getFilters(),
timeRange: data.query.timefilter.timefilter.getTime(),
});
const useVega = uiSettings.get(VISBUILDER_ENABLE_VEGA_SETTING);
const rootState = useTypedSelector((state) => state);
const dispatch = useTypedDispatch();
// Visualizations require the uiState object to persist even when the expression changes
Expand Down Expand Up @@ -81,12 +84,20 @@ export const WorkspaceUI = () => {
return;
}

const exp = await toExpression(rootState, searchContext);
const exp = await toExpression(rootState, searchContext, useVega);
setExpression(exp);
}

loadExpression();
}, [rootState, toExpression, toasts, ui.containerConfig.data.schemas, searchContext, aggConfigs]);
}, [
rootState,
toExpression,
toasts,
ui.containerConfig.data.schemas,
searchContext,
aggConfigs,
useVega,
]);

useLayoutEffect(() => {
const subscription = data.query.state$.subscribe(({ state }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ import {
getIndexPatterns,
getTypeService,
getUIActions,
getUISettings,
} from '../plugin_services';
import { PersistedState, prepareJson } from '../../../visualizations/public';
import { VisBuilderSavedVis } from '../saved_visualizations/transforms';
import { handleVisEvent } from '../application/utils/handle_vis_event';
import { VisBuilderEmbeddableFactoryDeps } from './vis_builder_embeddable_factory';
import { VISBUILDER_ENABLE_VEGA_SETTING } from '../../common/constants';

// Apparently this needs to match the saved object type for the clone and replace panel actions to work
export const VISBUILDER_EMBEDDABLE = VISBUILDER_SAVED_OBJECT;
Expand Down Expand Up @@ -150,11 +152,16 @@ export class VisBuilderEmbeddable extends Embeddable<VisBuilderInput, VisBuilder

if (!valid && errorMsg) throw new Error(errorMsg);

const exp = await toExpression(renderState, {
filters: this.filters,
query: this.query,
timeRange: this.timeRange,
});
const useVega = getUISettings().get(VISBUILDER_ENABLE_VEGA_SETTING);
const exp = await toExpression(
renderState,
{
filters: this.filters,
query: this.query,
timeRange: this.timeRange,
},
useVega
);
return exp;
} catch (error) {
this.onContainerError(error as Error);
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/vis_builder/public/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { dataPluginMock } from '../../data/public/mocks';
import { embeddablePluginMock } from '../../embeddable/public/mocks';
import { navigationPluginMock } from '../../navigation/public/mocks';
import { visualizationsPluginMock } from '../../visualizations/public/mocks';
import { expressionsPluginMock } from '../../expressions/public/mocks';
import { PLUGIN_ID, PLUGIN_NAME } from '../common';
import { VisBuilderPlugin } from './plugin';

Expand All @@ -29,6 +30,7 @@ describe('VisBuilderPlugin', () => {
visualizations: visualizationsPluginMock.createSetupContract(),
embeddable: embeddablePluginMock.createSetupContract(),
data: dataPluginMock.createSetupContract(),
expressions: expressionsPluginMock.createSetupContract(), // Add this line
};

const setup = plugin.setup(coreSetup, setupDeps);
Expand All @@ -41,6 +43,7 @@ describe('VisBuilderPlugin', () => {
aliasApp: PLUGIN_ID,
})
);
expect(setupDeps.expressions.registerFunction).toHaveBeenCalled(); // Add this expectation
});
});
});
4 changes: 3 additions & 1 deletion src/plugins/vis_builder/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
withNotifyOnErrors,
} from '../../opensearch_dashboards_utils/public';
import { opensearchFilters } from '../../data/public';
import { createRawDataVisFn } from './visualizations/vega/utils/expression_helper';

export class VisBuilderPlugin
implements
Expand All @@ -74,7 +75,7 @@ export class VisBuilderPlugin

public setup(
core: CoreSetup<VisBuilderPluginStartDependencies, VisBuilderStart>,
{ embeddable, visualizations, data }: VisBuilderPluginSetupDependencies
{ embeddable, visualizations, data, expressions: exp }: VisBuilderPluginSetupDependencies
) {
const { appMounted, appUnMounted, stop: stopUrlTracker } = createOsdUrlTracker({
baseUrl: core.http.basePath.prepend(`/app/${PLUGIN_ID}`),
Expand Down Expand Up @@ -107,6 +108,7 @@ export class VisBuilderPlugin
// Register Default Visualizations
const typeService = this.typeService;
registerDefaultTypes(typeService.setup());
exp.registerFunction(createRawDataVisFn());

// Register the plugin to core
core.application.register({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface VisualizationTypeOptions<T = any> {
};
readonly toExpression: (
state: RenderState,
searchContext: IExpressionLoaderParams['searchContext']
searchContext: IExpressionLoaderParams['searchContext'],
useVega: boolean
) => Promise<string | undefined>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export class VisualizationType implements IVisualizationType {
public readonly ui: IVisualizationType['ui'];
public readonly toExpression: (
state: RenderState,
searchContext: IExpressionLoaderParams['searchContext']
searchContext: IExpressionLoaderParams['searchContext'],
useVega: boolean
) => Promise<string | undefined>;

constructor(options: VisualizationTypeOptions) {
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/vis_builder/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { SavedObject, SavedObjectsStart } from '../../saved_objects/public';
import { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public';
import { DashboardStart } from '../../dashboard/public';
import { VisualizationsSetup } from '../../visualizations/public';
import { ExpressionsStart } from '../../expressions/public';
import { ExpressionsStart, ExpressionsPublicPlugin } from '../../expressions/public';
import { NavigationPublicPluginStart } from '../../navigation/public';
import { DataPublicPluginStart } from '../../data/public';
import { TypeServiceSetup, TypeServiceStart } from './services/type_service';
Expand All @@ -18,6 +18,7 @@ import { IOsdUrlStateStorage } from '../../opensearch_dashboards_utils/public';
import { DataPublicPluginSetup } from '../../data/public';
import { UiActionsStart } from '../../ui_actions/public';
import { Capabilities } from '../../../core/public';
import { IUiSettingsClient } from '../../../core/public';

export type VisBuilderSetup = TypeServiceSetup;
export interface VisBuilderStart extends TypeServiceStart {
Expand All @@ -28,6 +29,7 @@ export interface VisBuilderPluginSetupDependencies {
embeddable: EmbeddableSetup;
visualizations: VisualizationsSetup;
data: DataPublicPluginSetup;
expressions: ReturnType<ExpressionsPublicPlugin['setup']>;
}
export interface VisBuilderPluginStartDependencies {
embeddable: EmbeddableStart;
Expand All @@ -37,6 +39,7 @@ export interface VisBuilderPluginStartDependencies {
dashboard: DashboardStart;
expressions: ExpressionsStart;
uiActions: UiActionsStart;
uiSettings: IUiSettingsClient;
}

export interface VisBuilderServices extends CoreStart {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ import { cloneDeep } from 'lodash';
import { OpenSearchaggsExpressionFunctionDefinition } from '../../../../data/public';
import { ExpressionFunctionOpenSearchDashboards } from '../../../../expressions';
import { buildExpressionFunction } from '../../../../expressions/public';
import { VisualizationState } from '../../application/utils/state_management';
import { VisualizationState, StyleState } from '../../application/utils/state_management';
import { getSearchService, getIndexPatterns } from '../../plugin_services';
import { StyleState } from '../../application/utils/state_management';
import { IExpressionLoaderParams } from '../../../../expressions/public';

export const getAggExpressionFunctions = async (
visualization: VisualizationState,
style?: StyleState
style?: StyleState,
useVega: boolean = false,
searchContext?: IExpressionLoaderParams['searchContext']
) => {
const { activeVisualization, indexPattern: indexId = '' } = visualization;
const { aggConfigParams } = activeVisualization || {};

const indexPatternsService = getIndexPatterns();
const indexPattern = await indexPatternsService.get(indexId);
// aggConfigParams is the serealizeable aggConfigs that need to be reconstructed here using the agg servce
const aggConfigs = getSearchService().aggs.createAggConfigs(
indexPattern,
cloneDeep(aggConfigParams)
Expand All @@ -31,7 +32,6 @@ export const getAggExpressionFunctions = async (
{}
);

// soon this becomes: const opensearchaggs = vis.data.aggs!.toExpressionAst();
const opensearchaggs = buildExpressionFunction<OpenSearchaggsExpressionFunctionDefinition>(
'opensearchaggs',
{
Expand All @@ -43,9 +43,20 @@ export const getAggExpressionFunctions = async (
}
);

let expressionFns = [opensearchDashboards, opensearchaggs];

if (useVega === true && searchContext) {
const opensearchDashboardsContext = buildExpressionFunction('opensearch_dashboards_context', {
timeRange: JSON.stringify(searchContext.timeRange || {}),
filters: JSON.stringify(searchContext.filters || []),
query: JSON.stringify(searchContext.query || []),
});
expressionFns = [opensearchDashboards, opensearchDashboardsContext, opensearchaggs];
}

return {
aggConfigs,
indexPattern,
expressionFns: [opensearchDashboards, opensearchaggs],
expressionFns,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { buildVegaLiteEncoding } from './encoding';

describe('encoding.ts', () => {
describe('buildVegaLiteEncoding', () => {
it('should build correct encoding for x and y axes', () => {
const dimensions = { x: [{}], y: [{}] };
const formats = { xAxisLabel: 'X Label', yAxisLabel: 'Y Label' };
const result = buildVegaLiteEncoding(dimensions, formats);

expect(result.x).toBeDefined();
expect(result.y).toBeDefined();
expect(result.x!.axis!.title).toBe('X Label');
expect(result.y!.axis!.title).toBe('Y Label');
});

it('should include color encoding when y dimension is present', () => {
const dimensions = { x: [{}], y: [{}] };
const formats = {};
const result = buildVegaLiteEncoding(dimensions, formats);

expect(result.color).toBeDefined();
expect(result.color!.field).toBe('series');
expect(result.color!.type).toBe('nominal');
});
});
});
Loading
Loading