import { Description } from 'joi'; import moment from 'moment'; import _ from 'lodash'; import { createSchemaComponent } from './schema-component-factory'; import { IComponentParent, IComponentSchema } from './components'; import { CLIAlterationHook } from '../../domain/product-module-definition'; // Using import result in errors due to the fact that joi.ts uses module.export, so we need to use Require const Joi = require('../joi'); export enum EStepType { Quote = 'quote', Application = 'application', Alterations = 'alterations', } /** * This retrieves Joi schema for the given the schema name - the variable in the code of type Joi.object(...). * @param params The product module code and the name of the schema to retrieve */ const getJoiSchemaFromCodeUsingSchemaName = (params: { localDefinitionCode: string; name: string }): any => { const { localDefinitionCode, name } = params; const returnJoiDescriptionFunctionBody = ` ${localDefinitionCode} return ${name}; `; // Suppressing "Do not use the Function constructor to create functions" // eslint-disable-next-line @typescript-eslint/no-implied-eval const fn = new Function('Joi', 'moment', returnJoiDescriptionFunctionBody); const schema = fn(Joi, moment); return schema; }; /** * This invokes the describe() function of the Joi object of the given alterations. * @param params The product module code and the type of the schema to generate */ export const getAllJoiAlterationsSchemaFromCode = (params: { localDefinitionCode: string; alterationHooks: CLIAlterationHook[]; }): Map => { const { localDefinitionCode, alterationHooks } = params; return new Map( alterationHooks.map((alterationHook) => { return [ _.kebabCase(alterationHook.key), getJoiSchemaFromCodeUsingSchemaName({ name: `${_.camelCase(alterationHook.key)}Schema`, localDefinitionCode, }), ]; }), ); }; /** * This retrieves Joi schema for the given schema type. * @param params The product module code and the type of the schema to retrieve */ export const getJoiSchemaFromCode = (params: { localDefinitionCode: string; schemaType: EStepType; alterationHooks: CLIAlterationHook[]; }): Map => { const { localDefinitionCode, schemaType, alterationHooks } = params; switch (schemaType) { case EStepType.Quote: // fall through case EStepType.Application: { return new Map([ [ schemaType, getJoiSchemaFromCodeUsingSchemaName({ name: `${schemaType}Schema`, localDefinitionCode, }), ], ]); } case EStepType.Alterations: { return getAllJoiAlterationsSchemaFromCode({ localDefinitionCode, alterationHooks }); } default: { throw new Error('Error: Unsupported schema type. See docs for schema types that are supported by the generator.'); } } }; /** * This invokes the describe() function of the Joi object of the given schema type. * @param params The product module code and the type of the schema to generate */ export const getJoiDescribeFromCode = (params: { localDefinitionCode: string; schemaType: EStepType; alterationHooks: CLIAlterationHook[]; }): Map => { const schemaMap = getJoiSchemaFromCode(params); return new Map([...schemaMap.entries()].map(([name, schema]) => [name, schema.describe()])); }; /** * This generates the schema of the given type from the given product module local code. * @param params The product module code and the type of the schema to generate */ export const generateSchemaFromJoi = (params: { joiDescribe: Description; parent?: IComponentParent; }): IComponentSchema[] => { const { joiDescribe, parent } = params; return Object.keys(joiDescribe.keys).flatMap((fieldName) => { const properties = joiDescribe?.keys[fieldName]; if (properties.type === 'object') { return generateSchemaFromJoi({ joiDescribe: properties, parent: { key: fieldName, properties, }, }); } const component = createSchemaComponent({ name: fieldName, parent, properties, }); return component.schema(); }); };