import path from 'node:path'; import chalk from 'chalk'; import { readProductModuleDefinition } from '../helpers/read-product-module-definition'; import { EStepType, generateSchemaFromJoi, getJoiDescribeFromCode, generatePayloadArray } from '../helpers/generate'; import { readAuthAndConfig } from '../helpers/read-auth-and-config'; import { isProductModuleConfig } from '../domain/root-config'; import { checkValidProductModuleDirectory } from '../helpers/check-valid-product-module-directory'; import { CLIAlterationHook } from '../domain/product-module-definition'; import { generateFiles } from '../helpers/generate-files'; import { symbols } from '../helpers/symbols'; import { CLIError, ExitCodes } from '../errors/platform-error'; import { generateApiDocsPrompt } from './apidocs-prompt'; export const getValidStepType = (params: { step: string }): EStepType => { let { step } = params; step = step.toLocaleLowerCase(); // Ensure that this works even if user types QUoTe const validStepType = Object.values(EStepType).includes(step as EStepType) ? (step as EStepType) : undefined; if (!validStepType) { throw new Error(`Unsupported option: '${step}'. Run 'rp generate --help' to view the supported options.`); } return validStepType; }; const generatePayloadFiles = async (params: { directory: string; code: string; payload: string; alterationHooks: CLIAlterationHook[]; }) => { const { payload, code, directory, alterationHooks } = params; const payloadType = getValidStepType({ step: payload }); const generatedPayload = generatePayloadArray({ code, schemaType: payloadType, alterationHooks }); const formattedPayload = JSON.stringify(generatedPayload[0].payload, null, ' '); await generateFiles({ directory: `${directory}/payloads`, fileContent: formattedPayload, outputFilename: `${String(generatedPayload[0].schemaType)}.json`, }); const message = `Payloads successfully generated in the 'payloads' directory`; console.log(`${symbols.success}${chalk.green(message)}`); }; const generateSchemaFiles = async (params: { directory: string; code: string; workflow: string; alterationHooks: CLIAlterationHook[]; }) => { const { workflow, code, directory, alterationHooks } = params; const workflowType = getValidStepType({ step: workflow }); const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: code, schemaType: workflowType, alterationHooks, }); const writers = [...joiDescribeMap.entries()].map(async ([name, description]) => { const results = generateSchemaFromJoi({ joiDescribe: description, }); const formattedJsonSchema = JSON.stringify(results, null, ' '); await generateFiles({ directory: `${directory}/sandbox/workflows`, fileContent: formattedJsonSchema, outputFilename: `${name}-schema.g.json`, }); }); await Promise.all(writers); const message = `Schemas successfully generated in the './sandbox' directory`; console.log(`${symbols.success}${chalk.green(message)}`); }; export const replaceCustomJoiDateOfBirth = (code: string) => { return code.replaceAll('.dateOfBirth()', '.date()'); }; export const generate = async (options: { workflow?: string; payload?: string; apiDocs?: boolean }) => { const { workflow, payload, apiDocs } = options; if (!workflow && !payload && !apiDocs) { throw new CLIError( `rp generate must be called with one or more options. Use 'rp generate --help' to see a list of supported options.`, ExitCodes.GENERAL_ERROR, ); } const { apiKey, config: productModuleConfig } = readAuthAndConfig(); if (!isProductModuleConfig(productModuleConfig)) { throw new Error('.root-config.json is not a valid product module config file.'); } checkValidProductModuleDirectory({ productModuleConfig }); const productModuleDirectory = path.join('./'); // The workflow/payload generators evaluate the local product module code; the // API docs prompt flow is server-driven and needs neither the code nor the definition. if (workflow || payload) { const localDefinition = await readProductModuleDefinition(productModuleDirectory); const code = replaceCustomJoiDateOfBirth(localDefinition.codeFiles.map((cf) => cf.fileContent).join('\n\n')); const { alterationHooks } = localDefinition.workflows; if (workflow) { await generateSchemaFiles({ directory: productModuleDirectory, code, workflow, alterationHooks, }); } if (payload) { await generatePayloadFiles({ directory: productModuleDirectory, code, payload, alterationHooks, }); } } if (apiDocs) { await generateApiDocsPrompt({ apiKey, productModuleConfig }); } };