Skip to content

Commit

Permalink
Allow embeddables to register a panel config schema
Browse files Browse the repository at this point in the history
  • Loading branch information
nickpeihl committed Nov 22, 2024
1 parent d57e3b0 commit a30830b
Show file tree
Hide file tree
Showing 19 changed files with 754 additions and 248 deletions.
23 changes: 13 additions & 10 deletions src/plugins/dashboard/server/api/register_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { ContentManagementServerSetup } from '@kbn/content-management-plugi
import type { HttpServiceSetup } from '@kbn/core/server';
import type { UsageCounter } from '@kbn/usage-collection-plugin/server';
import type { Logger } from '@kbn/logging';
import type { EmbeddableStart } from '@kbn/embeddable-plugin/server';

import { CONTENT_ID } from '../../common/content_management';
import {
Expand All @@ -20,10 +21,10 @@ import {
PUBLIC_API_CONTENT_MANAGEMENT_VERSION,
} from './constants';
import {
dashboardAttributesSchema,
dashboardGetResultSchema,
dashboardCreateResultSchema,
dashboardSearchResultsSchema,
getDashboardAttributesSchema,
getDashboardGetResultSchema,
getDashboardCreateResultSchema,
getDashboardSearchResultsSchema,
referenceSchema,
} from '../content_management/v3';

Expand All @@ -32,6 +33,7 @@ interface RegisterAPIRoutesArgs {
contentManagement: ContentManagementServerSetup;
restCounter?: UsageCounter;
logger: Logger;
embeddable: EmbeddableStart;
}

const TECHNICAL_PREVIEW_WARNING =
Expand All @@ -40,6 +42,7 @@ const TECHNICAL_PREVIEW_WARNING =
export function registerAPIRoutes({
http,
contentManagement,
embeddable,
restCounter,
logger,
}: RegisterAPIRoutesArgs) {
Expand All @@ -65,14 +68,14 @@ export function registerAPIRoutes({
id: schema.maybe(schema.string()),
}),
body: schema.object({
attributes: dashboardAttributesSchema,
attributes: getDashboardAttributesSchema(embeddable),
references: schema.maybe(schema.arrayOf(referenceSchema)),
spaces: schema.maybe(schema.arrayOf(schema.string())),
}),
},
response: {
200: {
body: () => dashboardCreateResultSchema,
body: () => getDashboardCreateResultSchema(embeddable),
},
},
},
Expand Down Expand Up @@ -131,13 +134,13 @@ export function registerAPIRoutes({
id: schema.string(),
}),
body: schema.object({
attributes: dashboardAttributesSchema,
attributes: getDashboardAttributesSchema(embeddable),
references: schema.maybe(schema.arrayOf(referenceSchema)),
}),
},
response: {
200: {
body: () => dashboardCreateResultSchema,
body: () => getDashboardCreateResultSchema(embeddable),
},
},
},
Expand Down Expand Up @@ -193,7 +196,7 @@ export function registerAPIRoutes({
200: {
body: () =>
schema.object({
items: schema.arrayOf(dashboardSearchResultsSchema),
items: schema.arrayOf(getDashboardSearchResultsSchema(embeddable)),
total: schema.number(),
}),
},
Expand Down Expand Up @@ -247,7 +250,7 @@ export function registerAPIRoutes({
},
response: {
200: {
body: () => dashboardGetResultSchema,
body: () => getDashboardGetResultSchema(embeddable),
},
},
},
Expand Down
11 changes: 7 additions & 4 deletions src/plugins/dashboard/server/content_management/cm_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ import type {
ContentManagementServicesDefinition as ServicesDefinition,
Version,
} from '@kbn/object-versioning';
import { EmbeddableStart } from '@kbn/embeddable-plugin/server';

// We export the versioned service definition from this file and not the barrel to avoid adding
// the schemas in the "public" js bundle

import { serviceDefinition as v1 } from './v1';
import { serviceDefinition as v2 } from './v2';
import { serviceDefinition as v3 } from './v3';
import { getServiceDefinition as v3 } from './v3';

export const cmServicesDefinition: { [version: Version]: ServicesDefinition } = {
export const getCmServicesDefinition = (
embeddable: EmbeddableStart
): { [version: Version]: ServicesDefinition } => ({
1: v1,
2: v2,
3: v3,
};
3: v3(embeddable),
});
109 changes: 59 additions & 50 deletions src/plugins/dashboard/server/content_management/dashboard_storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import Boom from '@hapi/boom';
import { tagsToFindOptions } from '@kbn/content-management-utils';
import type { CoreSetup } from '@kbn/core-lifecycle-browser';
import {
SavedObjectsFindOptions,
SavedObjectsFindResult,
Expand All @@ -18,7 +19,7 @@ import type { Logger } from '@kbn/logging';
import { CreateResult, DeleteResult, SearchQuery } from '@kbn/content-management-plugin/common';
import { StorageContext } from '@kbn/content-management-plugin/server';
import { DASHBOARD_SAVED_OBJECT_TYPE } from '../dashboard_saved_object';
import { cmServicesDefinition } from './cm_services';
import { getCmServicesDefinition } from './cm_services';
import { DashboardSavedObjectAttributes } from '../dashboard_saved_object';
import { itemAttrsToSavedObjectAttrs, savedObjectToItem } from './latest';
import type {
Expand Down Expand Up @@ -60,67 +61,72 @@ const savedObjectClientFromRequest = async (ctx: StorageContext) => {

export class DashboardStorage {
constructor({
core,
logger,
throwOnResultValidationError,
}: {
core: CoreSetup;
logger: Logger;
throwOnResultValidationError: boolean;
}) {
this.core = core;
this.logger = logger;
this.throwOnResultValidationError = throwOnResultValidationError ?? false;
this.mSearch = {
savedObjectType: DASHBOARD_SAVED_OBJECT_TYPE,
additionalSearchFields: [],
toItemResult: (ctx: StorageContext, savedObject: SavedObjectsFindResult): DashboardItem => {
const transforms = ctx.utils.getTransforms(cmServicesDefinition);

const { item, error: itemError } = savedObjectToItem(
savedObject as SavedObjectsFindResult<DashboardSavedObjectAttributes>,
false
);
if (itemError) {
throw Boom.badRequest(`Invalid response. ${itemError.message}`);
}

const validationError = transforms.mSearch.out.result.validate(item);
if (validationError) {
if (this.throwOnResultValidationError) {
throw Boom.badRequest(`Invalid response. ${validationError.message}`);
} else {
this.logger.warn(`Invalid response. ${validationError.message}`);
}
}

// Validate DB response and DOWN transform to the request version
const { value, error: resultError } = transforms.mSearch.out.result.down<
DashboardItem,
DashboardItem
>(
item,
undefined, // do not override version
{ validate: false } // validation is done above
);

if (resultError) {
throw Boom.badRequest(`Invalid response. ${resultError.message}`);
}

return value;
},
};
// this.mSearch = {
// savedObjectType: DASHBOARD_SAVED_OBJECT_TYPE,
// additionalSearchFields: [],
// toItemResult: (ctx: StorageContext, savedObject: SavedObjectsFindResult): DashboardItem => {
// const transforms = ctx.utils.getTransforms(getCmServicesDefinition(this.embeddable));

// const { item, error: itemError } = savedObjectToItem(
// savedObject as SavedObjectsFindResult<DashboardSavedObjectAttributes>,
// false
// );
// if (itemError) {
// throw Boom.badRequest(`Invalid response. ${itemError.message}`);
// }

// const validationError = transforms.mSearch.out.result.validate(item);
// if (validationError) {
// if (this.throwOnResultValidationError) {
// throw Boom.badRequest(`Invalid response. ${validationError.message}`);
// } else {
// this.logger.warn(`Invalid response. ${validationError.message}`);
// }
// }

// // Validate DB response and DOWN transform to the request version
// const { value, error: resultError } = transforms.mSearch.out.result.down<
// DashboardItem,
// DashboardItem
// >(
// item,
// undefined, // do not override version
// { validate: false } // validation is done above
// );

// if (resultError) {
// throw Boom.badRequest(`Invalid response. ${resultError.message}`);
// }

// return value;
// },
// };
}

private core: CoreSetup;
private logger: Logger;
private throwOnResultValidationError: boolean;

mSearch: {
savedObjectType: string;
toItemResult: (ctx: StorageContext, savedObject: SavedObjectsFindResult) => DashboardItem;
additionalSearchFields?: string[];
};
// mSearch: {
// savedObjectType: string;
// toItemResult: (ctx: StorageContext, savedObject: SavedObjectsFindResult) => DashboardItem;
// additionalSearchFields?: string[];
// };

async get(ctx: StorageContext, id: string): Promise<DashboardGetOut> {
const transforms = ctx.utils.getTransforms(cmServicesDefinition);
const [_, { embeddable }] = await this.core.getStartServices();
const transforms = ctx.utils.getTransforms(getCmServicesDefinition(embeddable));
const soClient = await savedObjectClientFromRequest(ctx);

// Save data in DB
Expand Down Expand Up @@ -174,7 +180,8 @@ export class DashboardStorage {
data: DashboardAttributes,
options: DashboardCreateOptions
): Promise<DashboardCreateOut> {
const transforms = ctx.utils.getTransforms(cmServicesDefinition);
const [_, { embeddable }] = await this.core.getStartServices();
const transforms = ctx.utils.getTransforms(getCmServicesDefinition(embeddable));
const soClient = await savedObjectClientFromRequest(ctx);

// Validate input (data & options) & UP transform them to the latest version
Expand Down Expand Up @@ -243,7 +250,8 @@ export class DashboardStorage {
data: DashboardAttributes,
options: DashboardUpdateOptions
): Promise<DashboardUpdateOut> {
const transforms = ctx.utils.getTransforms(cmServicesDefinition);
const [_, { embeddable }] = await this.core.getStartServices();
const transforms = ctx.utils.getTransforms(getCmServicesDefinition(embeddable));
const soClient = await savedObjectClientFromRequest(ctx);

// Validate input (data & options) & UP transform them to the latest version
Expand Down Expand Up @@ -324,7 +332,8 @@ export class DashboardStorage {
query: SearchQuery,
options: DashboardSearchOptions
): Promise<DashboardSearchOut> {
const transforms = ctx.utils.getTransforms(cmServicesDefinition);
const [_, { embeddable }] = await this.core.getStartServices();
const transforms = ctx.utils.getTransforms(getCmServicesDefinition(embeddable));
const soClient = await savedObjectClientFromRequest(ctx);

// Validate and UP transform the options
Expand Down
Loading

0 comments on commit a30830b

Please sign in to comment.