Skip to content

Commit

Permalink
Merge pull request #6 from Dosant/d/2022-04-26-script-based-visualiza…
Browse files Browse the repository at this point in the history
…tions

Add simple search proxy
  • Loading branch information
drewdaemon authored Apr 26, 2022
2 parents 3725767 + 992545b commit 0efafe2
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/plugins/vis_type_script/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"version": "kibana",
"ui": true,
"server": false,
"requiredPlugins": ["expressions", "visualizations"],
"requiredPlugins": ["expressions", "visualizations", "data"],
"requiredBundles": ["expressions", "visualizations", "visDefaultEditor"]
}
10 changes: 8 additions & 2 deletions src/plugins/vis_type_script/public/expression/fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@ import { i18n } from '@kbn/i18n';
import type { ExpressionFunctionDefinition, Render } from '../../../expressions/public';
import type { Arguments } from '../types';
import type { RenderValue } from './renderer';
import { ExpressionValueSearchContext } from '../../../data/common';

type ScriptVisExpressionFunctionDefinition = ExpressionFunctionDefinition<
'scriptVis',
unknown,
ExpressionValueSearchContext | null,
Arguments,
Render<RenderValue>
>;

export const createScriptVisFn = (): ScriptVisExpressionFunctionDefinition => ({
name: 'scriptVis',
type: 'render',
inputTypes: [],
inputTypes: ['kibana_context', 'null'],
help: i18n.translate('visTypeMarkdown.function.help', {
defaultMessage: 'Script-based visualization',
}),
Expand All @@ -44,6 +45,11 @@ export const createScriptVisFn = (): ScriptVisExpressionFunctionDefinition => ({
visParams: {
script: args.script,
},
visSearchContext: {
timeRange: input?.timeRange,
query: input?.query,
filter: input?.filters,
},
},
};
},
Expand Down
18 changes: 13 additions & 5 deletions src/plugins/vis_type_script/public/expression/renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,36 @@ import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import type { ExpressionRenderDefinition } from '../../../expressions';
import { VisualizationContainer } from '../../../visualizations/public';
import { VisParams } from '../types';
import { VisParams, VisSearchContext } from '../types';
import { ScriptRenderer } from '../renderer';
import { VisTypeScriptKibanaApi } from '../kibana_api';
import { DataPublicPluginStart } from '../../../data/public';

export interface RenderValue {
visType: 'script';
visParams: VisParams;
visSearchContext: VisSearchContext;
}

export const scriptVisRenderer: ExpressionRenderDefinition<RenderValue> = {
export const scriptVisRenderer: (
getDeps: () => Promise<{ data: DataPublicPluginStart }> // TODO: not sure if this is correct way of passing deps to vis renderer
) => ExpressionRenderDefinition<RenderValue> = (getDeps) => ({
name: 'script_vis',
displayName: 'script-based visualization',
reuseDomNode: true,
render: async (domNode, { visParams }, handlers) => {
render: async (domNode, { visParams, visSearchContext }, handlers) => {
const deps = await getDeps();
const visTypeScriptKibanaApi = new VisTypeScriptKibanaApi(deps, visSearchContext);

handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});

render(
<VisualizationContainer className="scriptVis" handlers={handlers}>
<ScriptRenderer script={visParams.script} />
<ScriptRenderer script={visParams.script} kibanaApi={visTypeScriptKibanaApi} />
</VisualizationContainer>,
domNode
);
},
};
});
9 changes: 9 additions & 0 deletions src/plugins/vis_type_script/public/kibana_api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export * from './kibana_api';
37 changes: 37 additions & 0 deletions src/plugins/vis_type_script/public/kibana_api/kibana_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type * as estypes from '@elastic/elasticsearch/lib/api/types';
import { lastValueFrom } from 'rxjs';
import { DataPublicPluginStart } from '../../../data/public';
import { VisSearchContext } from '../types';

export interface VisTypeScriptKibanaApiDeps {
data: DataPublicPluginStart;
}

export class VisTypeScriptKibanaApi {
constructor(
private readonly deps: VisTypeScriptKibanaApiDeps,
private readonly visSearchContext: VisSearchContext
) {}

async esSearch(
payload: estypes.SearchRequest,
{ useKibanaContext = true } = { useKibanaContext: true }
): Promise<estypes.SearchResponse> {
if (useKibanaContext) {
// TODO: adjust request based on this.visSearchContext
// eslint-disable-next-line no-console
console.log(this.visSearchContext);
}

const response = await lastValueFrom(this.deps.data.search.search({ params: payload }));
return response.rawResponse;
}
}
23 changes: 19 additions & 4 deletions src/plugins/vis_type_script/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,39 @@ import { scriptVisDefinition } from './vis_definition';
import { ConfigSchema } from '../config';
import { scriptVisRenderer } from './expression/renderer';
import { createScriptVisFn } from './expression/fn';
import { DataPublicPluginStart } from '../../data/public';

/** @internal */
export interface ScriptVisPluginDependencies {
export interface ScriptVisPluginSetupDependencies {
expressions: ReturnType<ExpressionsPublicPlugin['setup']>;
visualizations: VisualizationsSetup;
}

/** @internal */
export class ScriptVisPlugin implements Plugin<void, void> {
export interface ScriptVisPluginStartDependencies {
data: DataPublicPluginStart;
}

/** @internal */
export class ScriptVisPlugin
implements Plugin<void, void, ScriptVisPluginSetupDependencies, ScriptVisPluginStartDependencies>
{
initializerContext: PluginInitializerContext<ConfigSchema>;

constructor(initializerContext: PluginInitializerContext<ConfigSchema>) {
this.initializerContext = initializerContext;
}

public setup(core: CoreSetup, { expressions, visualizations }: ScriptVisPluginDependencies) {
public setup(
core: CoreSetup<ScriptVisPluginStartDependencies>,
{ expressions, visualizations }: ScriptVisPluginSetupDependencies
) {
visualizations.createBaseVisualization(scriptVisDefinition);
expressions.registerRenderer(scriptVisRenderer);
expressions.registerRenderer(
scriptVisRenderer(() =>
core.getStartServices().then(([coreStart, plugins]) => ({ data: plugins.data }))
)
);
expressions.registerFunction(createScriptVisFn);
}

Expand Down
34 changes: 16 additions & 18 deletions src/plugins/vis_type_script/public/renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React from 'react';
import { createEndpoint, fromIframe } from '@remote-ui/rpc';

import './index.scss';
import { VisTypeScriptKibanaApi } from '../kibana_api';

const getSandboxDocument = (script: string) => {
// may be possible to remove this iframe-level nonce once we can use the top-level CSP
Expand All @@ -30,19 +31,12 @@ const getSandboxDocument = (script: string) => {
const endpoint = createEndpoint(fromInsideIframe());
const KIBANA_API = {
searchEs: () => {
esSearch: (payload, options) => {
console.log('Calling search inside an iframe')
endpoint.call.searchEs();
return endpoint.call.esSearch(payload, options);
}
}
endpoint.expose({
onUpdate: () => {
console.log('Running an update from an iframe')
}
})
window.KIBANA_API = KIBANA_API;
</script>
Expand All @@ -53,29 +47,33 @@ const getSandboxDocument = (script: string) => {
`;
};

export const ScriptRenderer: React.FunctionComponent<{ script: string }> = ({
export const ScriptRenderer: React.FunctionComponent<{
script: string;
kibanaApi: VisTypeScriptKibanaApi;
}> = ({
script: visualizationScript,
kibanaApi,
}: {
script: string;
kibanaApi: VisTypeScriptKibanaApi;
}) => {
const iframeRef = React.useRef<HTMLIFrameElement | null>(null);

React.useEffect(() => {
if (!iframeRef.current) throw new Error('Iframe init error');
const iframeEl = iframeRef.current;
const endpoint = createEndpoint<{ onUpdate: () => void }>(fromIframe(iframeEl));
const endpoint = createEndpoint(fromIframe(iframeEl, { terminate: false }));

endpoint.expose({
searchEs: () => {
// eslint-disable-next-line no-console
console.log('Running search from parent');
esSearch: (payload, options) => {
return kibanaApi.esSearch(payload, options);
},
});

// eslint-disable-next-line no-console
console.log('Calling on update from parent');
endpoint.call.onUpdate();
}, []);
return () => {
endpoint.terminate();
};
}, [kibanaApi]);

return (
<iframe
Expand Down
9 changes: 9 additions & 0 deletions src/plugins/vis_type_script/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@
* Side Public License, v 1.
*/

import { Filter, Query } from '@kbn/es-query';
import { TimeRange } from '../../data/common';

export interface Arguments {
script: string;
}

export interface VisParams {
script: Arguments['script'];
}

export interface VisSearchContext {
filters?: Filter[];
query?: Query | Query[];
timeRange?: TimeRange;
}

0 comments on commit 0efafe2

Please sign in to comment.