Skip to content

Commit

Permalink
feat: verify when exec custom commands
Browse files Browse the repository at this point in the history
Signed-off-by: zxypro1 <[email protected]>
  • Loading branch information
zxypro1 committed Mar 19, 2024
1 parent 26e43d4 commit 38693f2
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/engine/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@serverless-devs/engine",
"version": "0.1.2-beta.4",
"version": "0.1.2-beta.5",
"description": "a engine lib for serverless-devs",
"main": "lib/index.js",
"scripts": {
Expand Down
49 changes: 45 additions & 4 deletions packages/engine/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Logger, { ILoggerInstance } from '@serverless-devs/logger';
import { DevsError, ETrackerType, emoji, getAbsolutePath, getRootHome, getUserAgent, traceid } from '@serverless-devs/utils';
import { EXIT_CODE } from './constants';
import assert from 'assert';
import Ajv from 'ajv';
export * from './types';
export { verify, preview } from './utils';

Expand Down Expand Up @@ -91,7 +92,7 @@ class Engine {
return this.context;
}
const { steps: _steps, yaml, command, access = yaml.access } = this.spec;
this.logger.write(`${emoji('⌛')} Steps for [${command}] of [${get(this.spec, 'yaml.appName')}]\n${chalk.gray('====================')}`);
this.logger.write(chalk.gray(`${emoji('⌛')} Steps for [${command}] of [${get(this.spec, 'yaml.appName')}]\n${chalk.gray('====================')}`));
// 初始化全局的 action
this.globalActionInstance = new Actions(yaml.actions, {
hookLevel: IActionLevel.GLOBAL,
Expand Down Expand Up @@ -206,14 +207,54 @@ class Engine {
*/
private async validate() {
const { steps, command, projectName } = this.spec;
let errorsList: any[] = [];
const ajv = new Ajv({ allErrors: true });
assert(!isEmpty(steps), 'Step is required');
for (const step of steps) {
const instance = await loadComponent(step.component, { engineLogger: this.logger });
const instance = await loadComponent(step.component, { logger: this.logger, engineLogger: this.logger });
if (projectName && keys(get(instance, 'commands')).includes(projectName)) {
assert(!projectName, `The name of the project [${projectName}] overlaps with a command, please change it's name`);
throw new DevsError(`The name of the project [${projectName}] overlaps with a command, please change it's name.`, {
exitCode: EXIT_CODE.DEVS,
trackerType: ETrackerType.parseException,
prefix: `[${projectName}] failed to [${command}]:`
});
}
// schema validation
if (get(this.spec, 'yaml.use3x') && get(this.spec, 'yaml.content.validation')) {
const schema = await this.getSchemaByInstance(instance);
if (isEmpty(schema)) continue;
const validate = ajv.compile(JSON.parse(schema));
if (!validate(step.props)) {
const errors = validate.errors;
if (!errors) continue;
for (const j of errors) {
j.instancePath = step.projectName + '/props' + j.instancePath;
if (j.keyword === 'enum') {
j.message = j.message + ': ' + j.params.allowedValues.join(', ');
}
}
errorsList = errorsList.concat(errors);
}
}
}
assert(!isEmpty(steps), 'Step is required');
assert(command, 'Command is required');
if (!isEmpty(errorsList)) {
throw new DevsError(`${ajv.errorsText(errorsList, { dataVar: '', separator: '\n' })}`, {
exitCode: EXIT_CODE.DEVS,
trackerType: ETrackerType.parseException,
prefix: 'Function props validation error:'
});
}
}

/**
* Get schema by existing instance, avoid loading components.
* @param instance loadComponent instance
* @param logger Logger
*/
private getSchemaByInstance(instance: any) {
if (!instance || !instance.getSchema) return null;
return instance.getSchema();
}

/**
Expand Down

0 comments on commit 38693f2

Please sign in to comment.