Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Dosant committed Oct 16, 2020
1 parent b06a04d commit 26be54d
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 { SavedDashboardPanel } from '../../types';
import { SavedObjectReference } from '../../../../../core/types';
import { EmbeddableStart } from '../../../../embeddable/public';
import {
convertSavedDashboardPanelToPanelState,
convertPanelStateToSavedDashboardPanel,
} from './embeddable_saved_object_converters';

export interface InjectDeps {
embeddableStart: EmbeddableStart;
}

export function injectPanelsReferences(
panels: SavedDashboardPanel[],
references: SavedObjectReference[],
deps: InjectDeps
): SavedDashboardPanel[] {
const result: SavedDashboardPanel[] = [];
for (const panel of panels) {
const embeddableState = convertSavedDashboardPanelToPanelState(panel);
embeddableState.explicitInput = deps.embeddableStart.inject(
embeddableState.explicitInput,
references
);
result.push(convertPanelStateToSavedDashboardPanel(embeddableState, panel.version));
}
return result;
}

export interface ExtractDeps {
embeddableStart: EmbeddableStart;
}

export function extractPanelsReferences(
panels: SavedDashboardPanel[],
deps: ExtractDeps
): SavedObjectReference[] {
const references: SavedObjectReference[] = [];

panels.map(convertSavedDashboardPanelToPanelState).forEach(({ explicitInput: input }) => {
references.push(...deps.embeddableStart.extract(input).references);
});

return references;
}
1 change: 1 addition & 0 deletions src/plugins/dashboard/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ export class DashboardPlugin
const savedDashboardLoader = createSavedDashboardLoader({
savedObjectsClient: core.savedObjects.client,
savedObjects: plugins.savedObjects,
embeddableStart: plugins.embeddable,
});
const dashboardContainerFactory = plugins.embeddable.getEmbeddableFactory(
DASHBOARD_CONTAINER_TYPE
Expand Down
13 changes: 10 additions & 3 deletions src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { extractReferences, injectReferences } from './saved_dashboard_reference

import { Filter, ISearchSource, Query, RefreshInterval } from '../../../../plugins/data/public';
import { createDashboardEditUrl } from '../dashboard_constants';
import { EmbeddableStart } from '../../../embeddable/public';
import { SavedObjectAttributes, SavedObjectReference } from '../../../../core/types';

export interface SavedObjectDashboard extends SavedObject {
id?: string;
Expand All @@ -41,7 +43,8 @@ export interface SavedObjectDashboard extends SavedObject {

// Used only by the savedDashboards service, usually no reason to change this
export function createSavedDashboardClass(
savedObjectStart: SavedObjectsStart
savedObjectStart: SavedObjectsStart,
embeddableStart: EmbeddableStart
): new (id: string) => SavedObjectDashboard {
class SavedDashboard extends savedObjectStart.SavedObjectClass {
// save these objects with the 'dashboard' type
Expand Down Expand Up @@ -77,8 +80,12 @@ export function createSavedDashboardClass(
type: SavedDashboard.type,
mapping: SavedDashboard.mapping,
searchSource: SavedDashboard.searchSource,
extractReferences,
injectReferences,
extractReferences: (opts: {
attributes: SavedObjectAttributes;
references: SavedObjectReference[];
}) => extractReferences(opts, { embeddableStart }),
injectReferences: (so: SavedObjectDashboard, reference: SavedObjectReference[]) =>
injectReferences(so, reference, { embeddableStart }),

// if this is null/undefined then the SavedObject will be assigned the defaults
id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,30 @@

import { SavedObjectAttributes, SavedObjectReference } from 'kibana/public';
import { SavedObjectDashboard } from './saved_dashboard';
import { EmbeddableStart } from '../../../embeddable/public';
import {
extractPanelsReferences,
injectPanelsReferences,
} from '../application/lib/embeddable_references';
import { SavedDashboardPanel730ToLatest } from '../../common';

export function extractReferences({
attributes,
references = [],
}: {
attributes: SavedObjectAttributes;
references: SavedObjectReference[];
}) {
export interface ExtractDeps {
embeddableStart: EmbeddableStart;
}

export function extractReferences(
{
attributes,
references = [],
}: {
attributes: SavedObjectAttributes;
references: SavedObjectReference[];
},
deps: ExtractDeps
) {
const panelReferences: SavedObjectReference[] = [];
const panels: Array<Record<string, string>> = JSON.parse(String(attributes.panelsJSON));

panels.forEach((panel, i) => {
if (!panel.type) {
throw new Error(`"type" attribute is missing from panel "${i}"`);
Expand All @@ -46,6 +60,13 @@ export function extractReferences({
delete panel.type;
delete panel.id;
});

// TODO: can be it that here a migration from older panels format is needed?
// I guess no?
panelReferences.push(
...extractPanelsReferences((panels as unknown) as SavedDashboardPanel730ToLatest[], deps)
);

return {
references: [...references, ...panelReferences],
attributes: {
Expand All @@ -55,21 +76,27 @@ export function extractReferences({
};
}

export interface InjectDeps {
embeddableStart: EmbeddableStart;
}

export function injectReferences(
savedObject: SavedObjectDashboard,
references: SavedObjectReference[]
references: SavedObjectReference[],
deps: InjectDeps
) {
// Skip if panelsJSON is missing otherwise this will cause saved object import to fail when
// importing objects without panelsJSON. At development time of this, there is no guarantee each saved
// object has panelsJSON in all previous versions of kibana.
if (typeof savedObject.panelsJSON !== 'string') {
return;
}
const panels = JSON.parse(savedObject.panelsJSON);
let panels = JSON.parse(savedObject.panelsJSON);
// Same here, prevent failing saved object import if ever panels aren't an array.
if (!Array.isArray(panels)) {
return;
}

panels.forEach((panel) => {
if (!panel.panelRefName) {
return;
Expand All @@ -84,5 +111,6 @@ export function injectReferences(
panel.type = reference.type;
delete panel.panelRefName;
});
panels = injectPanelsReferences(panels, references, deps);
savedObject.panelsJSON = JSON.stringify(panels);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@
import { SavedObjectsClientContract } from 'kibana/public';
import { SavedObjectLoader, SavedObjectsStart } from '../../../../plugins/saved_objects/public';
import { createSavedDashboardClass } from './saved_dashboard';
import { EmbeddableStart } from '../../../embeddable/public';

interface Services {
savedObjectsClient: SavedObjectsClientContract;
savedObjects: SavedObjectsStart;
embeddableStart: EmbeddableStart;
}

/**
* @param services
*/
export function createSavedDashboardLoader({ savedObjects, savedObjectsClient }: Services) {
const SavedDashboard = createSavedDashboardClass(savedObjects);
export function createSavedDashboardLoader({
savedObjects,
savedObjectsClient,
embeddableStart,
}: Services) {
const SavedDashboard = createSavedDashboardClass(savedObjects, embeddableStart);
return new SavedObjectLoader(SavedDashboard, savedObjectsClient);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
UiActionsEnhancedDrilldownDefinition as Drilldown,
UiActionsEnhancedBaseActionFactoryContext as BaseActionFactoryContext,
AdvancedUiActionsStart,
UiActionsEnhancedSerializedEvent as SerializedEvent,
} from '../../../../../ui_actions_enhanced/public';
import { txtGoToDashboard } from './i18n';
import {
Expand All @@ -25,6 +26,8 @@ import {
} from '../../../../../../../src/plugins/kibana_utils/public';
import { KibanaURL } from '../../../../../../../src/plugins/share/public';
import { Config } from './types';
import { SavedObjectReference } from '../../../../../../../src/core/types';
import { SerializedAction } from '../../../../../ui_actions_enhanced/common/types';

export interface Params {
start: StartServicesGetter<{
Expand Down Expand Up @@ -86,4 +89,43 @@ export abstract class AbstractDashboardDrilldown<T extends TriggerId>
throw new Error('Dashboard URL generator is required for dashboard drilldown.');
return urlGenerator;
}

public readonly inject = (
state: SerializedEvent,
references: SavedObjectReference[]
): SerializedEvent => {
const action = state.action as SerializedAction<Config>;
const refName = this.generateRefName(state);
const ref = references.find((r) => r.name === refName);
if (!ref) return state;
if (ref.id && ref.id === action.config.dashboardId) return state;
return this.injectDashboardId(state, ref.id);
};

public readonly extract = (
state: SerializedEvent
): { state: SerializedEvent; references: SavedObjectReference[] } => {
const action = state.action as SerializedAction<Config>;
const references: SavedObjectReference[] = action.config.dashboardId
? [{ name: this.generateRefName(state), type: 'dashboard', id: action.config.dashboardId }]
: [];
delete action.config.dashboardId;
return { state, references };
};

private generateRefName = (state: SerializedEvent) =>
`drilldown:${this.id}:${state.eventId}:dashboardId`;

private injectDashboardId(state: SerializedEvent, dashboardId: string): SerializedEvent {
return {
...state,
action: {
...state.action,
config: {
...state.action.config,
dashboardId,
},
},
};
}
}

0 comments on commit 26be54d

Please sign in to comment.