Skip to content

Commit

Permalink
Migrate from Joi to @kbn/config-schema in "home" and "features" plugi…
Browse files Browse the repository at this point in the history
…ns (#100201)

* add a link for issue to remove circular deps

* features: migrate from joi to config-schema

* update tests

* migrate home tutorials to config-schema

* migrate home dataset validation to config schema

* remove unnecessary type. we cannot guarantee this is a valid SO

* address Pierres comments
  • Loading branch information
mshustov authored May 18, 2021
1 parent 15abf24 commit 574f659
Show file tree
Hide file tree
Showing 16 changed files with 440 additions and 415 deletions.
1 change: 0 additions & 1 deletion src/plugins/home/server/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export type { TutorialsRegistrySetup, TutorialsRegistryStart } from './tutorials
export { TutorialsCategory } from './tutorials';

export type {
ParamTypes,
InstructionSetSchema,
ParamsSchema,
InstructionsSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* Side Public License, v 1.
*/

import { SavedObject } from 'src/core/server';
import type { SampleDatasetSchema } from './sample_dataset_schema';
export type { SampleDatasetSchema, AppLinkSchema, DataIndexSchema } from './sample_dataset_schema';

export enum DatasetStatusTypes {
NOT_INSTALLED = 'not_installed',
Expand All @@ -26,57 +27,4 @@ export enum EmbeddableTypes {
SEARCH_EMBEDDABLE_TYPE = 'search',
VISUALIZE_EMBEDDABLE_TYPE = 'visualization',
}
export interface DataIndexSchema {
id: string;

// path to newline delimented JSON file containing data relative to KIBANA_HOME
dataPath: string;

// Object defining Elasticsearch field mappings (contents of index.mappings.type.properties)
fields: object;

// times fields that will be updated relative to now when data is installed
timeFields: string[];

// Reference to now in your test data set.
// When data is installed, timestamps are converted to the present time.
// The distance between a timestamp and currentTimeMarker is preserved but the date and time will change.
// For example:
// sample data set: timestamp: 2018-01-01T00:00:00Z, currentTimeMarker: 2018-01-01T12:00:00Z
// installed data set: timestamp: 2018-04-18T20:33:14Z, currentTimeMarker: 2018-04-19T08:33:14Z
currentTimeMarker: string;

// Set to true to move timestamp to current week, preserving day of week and time of day
// Relative distance from timestamp to currentTimeMarker will not remain the same
preserveDayOfWeekTimeOfDay: boolean;
}

export interface AppLinkSchema {
path: string;
icon: string;
label: string;
}

export interface SampleDatasetSchema<T = unknown> {
id: string;
name: string;
description: string;
previewImagePath: string;
darkPreviewImagePath: string;

// saved object id of main dashboard for sample data set
overviewDashboard: string;
appLinks: AppLinkSchema[];

// saved object id of default index-pattern for sample data set
defaultIndex: string;

// Kibana saved objects (index patter, visualizations, dashboard, ...)
// Should provide a nice demo of Kibana's functionality with the sample data set
savedObjects: Array<SavedObject<T>>;
dataIndices: DataIndexSchema[];
status?: string | undefined;
statusMsg?: unknown;
}

export type SampleDatasetProvider = () => SampleDatasetSchema;
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,94 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { Writable } from '@kbn/utility-types';
import { schema, TypeOf } from '@kbn/config-schema';

import Joi from 'joi';

const dataIndexSchema = Joi.object({
id: Joi.string()
.regex(/^[a-zA-Z0-9-]+$/)
.required(),
const idRegExp = /^[a-zA-Z0-9-]+$/;
const dataIndexSchema = schema.object({
id: schema.string({
validate(value: string) {
if (!idRegExp.test(value)) {
return `Does not satisfy regexp: ${idRegExp.toString()}`;
}
},
}),

// path to newline delimented JSON file containing data relative to KIBANA_HOME
dataPath: Joi.string().required(),
dataPath: schema.string(),

// Object defining Elasticsearch field mappings (contents of index.mappings.type.properties)
fields: Joi.object().required(),
fields: schema.recordOf(schema.string(), schema.any()),

// times fields that will be updated relative to now when data is installed
timeFields: Joi.array().items(Joi.string()).required(),
timeFields: schema.arrayOf(schema.string()),

// Reference to now in your test data set.
// When data is installed, timestamps are converted to the present time.
// The distance between a timestamp and currentTimeMarker is preserved but the date and time will change.
// For example:
// sample data set: timestamp: 2018-01-01T00:00:00Z, currentTimeMarker: 2018-01-01T12:00:00Z
// installed data set: timestamp: 2018-04-18T20:33:14Z, currentTimeMarker: 2018-04-19T08:33:14Z
currentTimeMarker: Joi.string().isoDate().required(),
currentTimeMarker: schema.string({
validate(value: string) {
if (isNaN(Date.parse(value))) {
return 'Expected a valid string in iso format';
}
},
}),

// Set to true to move timestamp to current week, preserving day of week and time of day
// Relative distance from timestamp to currentTimeMarker will not remain the same
preserveDayOfWeekTimeOfDay: Joi.boolean().default(false),
preserveDayOfWeekTimeOfDay: schema.boolean({ defaultValue: false }),
});

const appLinkSchema = Joi.object({
path: Joi.string().required(),
label: Joi.string().required(),
icon: Joi.string().required(),
export type DataIndexSchema = TypeOf<typeof dataIndexSchema>;

const appLinkSchema = schema.object({
path: schema.string(),
label: schema.string(),
icon: schema.string(),
});
export type AppLinkSchema = TypeOf<typeof appLinkSchema>;

export const sampleDataSchema = {
id: Joi.string()
.regex(/^[a-zA-Z0-9-]+$/)
.required(),
name: Joi.string().required(),
description: Joi.string().required(),
previewImagePath: Joi.string().required(),
darkPreviewImagePath: Joi.string(),
export const sampleDataSchema = schema.object({
id: schema.string({
validate(value: string) {
if (!idRegExp.test(value)) {
return `Does not satisfy regexp: ${idRegExp.toString()}`;
}
},
}),
name: schema.string(),
description: schema.string(),
previewImagePath: schema.string(),
darkPreviewImagePath: schema.maybe(schema.string()),

// saved object id of main dashboard for sample data set
overviewDashboard: Joi.string().required(),
appLinks: Joi.array().items(appLinkSchema).default([]),
overviewDashboard: schema.string(),
appLinks: schema.arrayOf(appLinkSchema, { defaultValue: [] }),

// saved object id of default index-pattern for sample data set
defaultIndex: Joi.string().required(),
defaultIndex: schema.string(),

// Kibana saved objects (index patter, visualizations, dashboard, ...)
// Should provide a nice demo of Kibana's functionality with the sample data set
savedObjects: Joi.array().items(Joi.object()).required(),
dataIndices: Joi.array().items(dataIndexSchema).required(),
};
savedObjects: schema.arrayOf(
schema.object(
{
id: schema.string(),
type: schema.string(),
attributes: schema.any(),
references: schema.arrayOf(schema.any()),
version: schema.maybe(schema.any()),
},
{ unknowns: 'allow' }
)
),
dataIndices: schema.arrayOf(dataIndexSchema),

status: schema.maybe(schema.string()),
statusMsg: schema.maybe(schema.string()),
});

export type SampleDatasetSchema = Writable<TypeOf<typeof sampleDataSchema>>;
10 changes: 8 additions & 2 deletions src/plugins/home/server/services/sample_data/routes/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
*/

import { schema } from '@kbn/config-schema';
import { IRouter, Logger, IScopedClusterClient } from 'src/core/server';
import type {
IRouter,
Logger,
IScopedClusterClient,
SavedObjectsBulkCreateObject,
} from 'src/core/server';
import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
import { createIndexName } from '../lib/create_index_name';
import {
Expand Down Expand Up @@ -148,8 +153,9 @@ export function createInstallRoute(

const client = getClient({ includedHiddenTypes });

const savedObjects = sampleDataset.savedObjects as SavedObjectsBulkCreateObject[];
createResults = await client.bulkCreate(
sampleDataset.savedObjects.map(({ version, ...savedObject }) => savedObject),
savedObjects.map(({ version, ...savedObject }) => savedObject),
{ overwrite: true }
);
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* Side Public License, v 1.
*/

import Joi from 'joi';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { SavedObject } from 'src/core/public';
import {
Expand Down Expand Up @@ -55,11 +54,13 @@ export class SampleDataRegistry {

return {
registerSampleDataset: (specProvider: SampleDatasetProvider) => {
const { error, value } = Joi.validate(specProvider(), sampleDataSchema);

if (error) {
let value: SampleDatasetSchema;
try {
value = sampleDataSchema.validate(specProvider());
} catch (error) {
throw new Error(`Unable to register sample dataset spec because it's invalid. ${error}`);
}

const defaultIndexSavedObjectJson = value.savedObjects.find((savedObjectJson: any) => {
return (
savedObjectJson.type === 'index-pattern' && savedObjectJson.id === value.defaultIndex
Expand Down
1 change: 0 additions & 1 deletion src/plugins/home/server/services/tutorials/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export type { TutorialsRegistrySetup, TutorialsRegistryStart } from './tutorials
export { TutorialsCategory } from './lib/tutorials_registry_types';

export type {
ParamTypes,
InstructionSetSchema,
ParamsSchema,
InstructionsSchema,
Expand Down
Loading

0 comments on commit 574f659

Please sign in to comment.