import { Command } from 'commander'; import path from 'path'; import fs from 'fs'; import { parse, stringify } from 'yaml'; import { v4 } from 'uuid'; import { normalizeName, searchTask, } from '../../../../helpers/profileGeneratorHelper'; import { validationTypesTemplate, validationYaml, } from '../../../../components/validationTemplate'; import inquirer from 'inquirer'; import Fuse from 'fuse.js'; import { getProjectPath } from '../../../../helpers/cluserHelper'; import { mergeVariables, readVariableValue, } from '../../../../helpers/variablesHelper'; import { findEntityByIdOrName } from '../../../../helpers/entityHelper'; export function GenerateValidationCommand(): Command { const cmd = new Command('generate') .alias('g') .description('Generates a HexaSync Validation') .option( '-t, --task-name ', 'Search for name of the task, e.g.: product', ) .option( '-c, --category ', "Define the category of the validation, e.g.: product, 'sales order'", ) .option( '-n, --validation-name ', "Define the name of the validation, e.g.: 'product price should greater than 0', 'product stock should be unsigned integer'", ) .action(async ({ taskName, category, validationName }) => { const normalizedCategoryName = normalizeName(category); const componentPath = getProjectPath(); const categoryPath = path.join( componentPath, 'validations', normalizedCategoryName.dashedSlug, ); if (!fs.existsSync(categoryPath)) { fs.mkdirSync(categoryPath, { recursive: true }); } const taskYml = await findEntityByIdOrName('', taskName, 'task'); let taskId = taskYml?.entityId || ''; if (!taskYml) { console.error( `There is no such task ${taskName}. Generating empty validation.`, ); } let realTaskName = taskYml?.entityName ? readVariableValue(taskYml.entityName) : ''; const taskHeader = realTaskName?.toString().replace(/\[([^\]]+)\]/g, '$1') || ''; const normalizedTaskHeader = taskYml?.entityName ? normalizeName(taskHeader) : null; const normalizedValidationName = normalizeName(validationName); const variableKey = normalizedTaskHeader ? `${normalizedTaskHeader.camelCaseName}_${normalizedValidationName.camelCaseName}_Validation` : `${normalizedValidationName.camelCaseName}_Validation`; var variables = [ { field: 'id', key: `**${variableKey}_Id**`, value: v4(), }, { field: 'index', key: `**${variableKey}_Index**`, value: 0, }, { field: 'name', key: `**${variableKey}_Name**`, value: normalizedTaskHeader ? `[${taskHeader}] ${normalizedValidationName.displayName} Validation` : `${normalizedValidationName.displayName} Validation`, }, ]; let yml = validationYaml(); for (var i = 0; i < variables.length; i++) { var variable = variables[i]; yml = yml.replaceAll( `||VARIABLE_KEY_${variable.field}||`, variable.key, ); } const validationTypes = validationTypesTemplate(); const fuse = new Fuse(validationTypes, { includeScore: true, keys: [ 'tags', { name: 'type', weight: 2, }, ], threshold: 0.8, isCaseSensitive: false, ignoreLocation: true, distance: 100, }); let searchedValidationTypes = fuse .search(validationName) .sort((a: any, b: any) => parseFloat(a.score) - parseFloat(b.score)) .map((x) => x.item); if (!searchedValidationTypes.length) { searchedValidationTypes = [...validationTypes]; } const answer = await inquirer.prompt<{ type: string }>([ { type: 'list', name: 'type', message: 'Please choose one type of validation:', choices: searchedValidationTypes.map((t) => t.type), }, ]); const validationType = searchedValidationTypes.find( (t) => t.type === answer.type, ); const metadata = validationType?.metadataSample as any; const config = parse(yml); config.metadata = metadata; config.type = validationType?.type; const filePath = path.join(categoryPath, `${variableKey}.yaml`); fs.writeFileSync( filePath, stringify( { validations: { ...{ [taskId]: [config] }, }, }, { keepSourceTokens: true, lineWidth: 0 }, ), ); mergeVariables(variables); }); return cmd; }