refactor validator

This commit is contained in:
Bowen Tan 2021-12-14 15:44:07 +08:00
parent 15f0d704aa
commit 29c5054b6e
6 changed files with 81 additions and 45 deletions

View File

@ -93,12 +93,12 @@ const PropsSchema = Type.Object({
customerIncrement: Type.Object({
bg: Type.Optional(Type.String()),
children: Type.Optional(Type.String()),
_active: Type.Object(Type.Object({ bg: Type.String() })),
_active: Type.Object({ bg: Type.String() }),
}),
customerDecrement: Type.Object({
bg: Type.Optional(Type.String()),
children: Type.Optional(Type.String()),
_active: Type.Object(Type.Object({ bg: Type.String() })),
_active: Type.Object({ bg: Type.String() }),
}),
});

View File

@ -25,7 +25,7 @@ class EditorStore {
};
appStorage = new AppStorage();
schemaValidator = new SchemaValidator();
schemaValidator = new SchemaValidator(registry);
get app() {
return this.appStorage.app;

View File

@ -1,13 +1,14 @@
import { ApplicationComponent } from '@sunmao-ui/core';
import { Registry } from '@sunmao-ui/runtime';
import Ajv from 'ajv';
import { registry } from '../setup';
import {
ISchemaValidator,
ComponentValidatorRule,
AllComponentsValidatorRule,
TraitValidatorRule,
ValidatorRule,
ValidateErrorResult
ValidateErrorResult,
ValidatorMap,
} from './interfaces';
import { rules } from './rules';
@ -16,13 +17,12 @@ export class SchemaValidator implements ISchemaValidator {
private traitRules: TraitValidatorRule[] = [];
private componentRules: ComponentValidatorRule[] = [];
private allComponentsRules: AllComponentsValidatorRule[] = [];
private ajv: Ajv;
private ajv!: Ajv;
private validatorMap!: ValidatorMap;
constructor() {
this.addRules(rules)
this.ajv = new Ajv({})
.addKeyword('kind')
.addKeyword('modifier');
constructor(registry: Registry) {
this.initAjv(registry);
this.addRules(rules);
}
addRules(rules: ValidatorRule[]) {
@ -43,8 +43,9 @@ export class SchemaValidator implements ISchemaValidator {
validate(components: ApplicationComponent[]) {
this.result = [];
const t1 = performance.now();
this.allComponentsRules.forEach(rule => {
const r = rule.validate({ components: components, ajv: this.ajv });
const r = rule.validate({ components: components, validators: this.validatorMap });
if (r.length > 0) {
this.result = this.result.concat(r);
}
@ -54,8 +55,7 @@ export class SchemaValidator implements ISchemaValidator {
const r = rule.validate({
component,
components: components,
registry,
ajv: this.ajv,
validators: this.validatorMap,
});
if (r.length > 0) {
this.result = this.result.concat(r);
@ -69,8 +69,7 @@ export class SchemaValidator implements ISchemaValidator {
trait,
component,
components: components,
registry,
ajv: this.ajv,
validators: this.validatorMap,
});
if (r.length > 0) {
this.result = this.result.concat(r);
@ -78,6 +77,8 @@ export class SchemaValidator implements ISchemaValidator {
});
});
});
const t2 = performance.now();
console.log('validate time:', t2 - t1, 'ms');
return this.result;
}
@ -87,4 +88,23 @@ export class SchemaValidator implements ISchemaValidator {
// });
// return components;
}
private initAjv(registry: Registry) {
this.ajv = new Ajv({}).addKeyword('kind').addKeyword('modifier');
this.validatorMap = {
components: {},
traits: {},
};
registry.getAllComponents().forEach(c => {
this.validatorMap.components[`${c.version}/${c.metadata.name}`] = this.ajv.compile(
c.spec.properties
);
});
registry.getAllTraits().forEach(t => {
this.validatorMap.traits[`${t.version}/${t.metadata.name}`] = this.ajv.compile(
t.spec.properties
);
});
}
}

View File

@ -1,24 +1,27 @@
import { ApplicationComponent, ComponentTrait } from '@sunmao-ui/core';
import { Registry } from '@sunmao-ui/runtime';
import Ajv from 'ajv';
import { ValidateFunction } from 'ajv';
export interface ComponentValidateContext {
component: ApplicationComponent;
components: ApplicationComponent[];
registry: Registry;
ajv: Ajv;
export interface ValidatorMap {
components: Record<string, ValidateFunction>;
traits: Record<string, ValidateFunction>;
}
export interface TraitValidateContext {
interface BaseValidateContext {
validators: ValidatorMap;
}
export interface ComponentValidateContext extends BaseValidateContext {
component: ApplicationComponent;
components: ApplicationComponent[];
}
export interface TraitValidateContext extends BaseValidateContext {
trait: ComponentTrait;
component: ApplicationComponent;
components: ApplicationComponent[];
registry: Registry;
ajv: Ajv;
}
export interface AllComponentsValidateContext {
export interface AllComponentsValidateContext extends BaseValidateContext {
components: ApplicationComponent[];
ajv: Ajv;
}
export type ValidateContext =

View File

@ -66,12 +66,11 @@ export class ComponentPropertyValidatorRule implements ComponentValidatorRule {
validate({
component,
registry,
ajv,
validators,
}: ComponentValidateContext): ValidateErrorResult[] {
const results: ValidateErrorResult[] = [];
const spec = registry.getComponentByType(component.type);
if (!spec) {
const validate = validators.components[component.type];
if (!validate) {
results.push({
message: `Cannot find component spec: ${component.type}.`,
componentId: component.id,
@ -79,10 +78,8 @@ export class ComponentPropertyValidatorRule implements ComponentValidatorRule {
return results;
}
const propertySchema = spec.spec.properties;
const regExp = new RegExp('.*{{.*}}.*');
const validate = ajv.compile(propertySchema);
const valid = validate(component.properties);
if (!valid) {
validate.errors!.forEach(error => {
@ -112,12 +109,11 @@ export class TraitPropertyValidatorRule implements TraitValidatorRule {
validate({
trait,
component,
registry,
ajv,
validators,
}: TraitValidateContext): ValidateErrorResult[] {
const results: ValidateErrorResult[] = [];
const spec = registry.getTraitByType(trait.type);
if (!spec) {
const validate = validators.traits[trait.type];
if (!validate) {
results.push({
message: `Cannot find trait spec: ${trait.type}.`,
componentId: component.id,
@ -126,10 +122,6 @@ export class TraitPropertyValidatorRule implements TraitValidatorRule {
return results;
}
const propertySchema = spec.spec.properties;
const regExp = new RegExp('.*{{.*}}.*');
const validate = ajv.compile(propertySchema);
const valid = validate(trait.properties);
if (!valid) {
validate.errors!.forEach(error => {
@ -139,6 +131,7 @@ export class TraitPropertyValidatorRule implements TraitValidatorRule {
const value = trait.properties[path];
// if value is an expression, skip it
const regExp = new RegExp('.*{{.*}}.*');
if (typeof value === 'string' && regExp.test(value)) {
return;
}

View File

@ -51,9 +51,9 @@ export type SunmaoLib = {
};
export class Registry {
components: Map<string, Map<string, ImplementedRuntimeComponent>> = new Map();
traits: Map<string, Map<string, ImplementedRuntimeTrait>> = new Map();
modules: Map<string, Map<string, ImplementedRuntimeModule>> = new Map();
components = new Map<string, Map<string, ImplementedRuntimeComponent>>()
traits = new Map<string, Map<string, ImplementedRuntimeTrait>>()
modules = new Map<string, Map<string, ImplementedRuntimeModule>>()
registerComponent(c: ImplementedRuntimeComponent) {
if (this.components.get(c.version)?.has(c.metadata.name)) {
@ -80,6 +80,16 @@ export class Registry {
return this.getComponent(version, name);
}
getAllComponents(): ImplementedRuntimeComponent[] {
const res: ImplementedRuntimeComponent[] = [];
for (const version of this.components.values()) {
for (const component of version.values()) {
res.push(component);
}
}
return res
}
registerTrait(t: ImplementedRuntimeTrait) {
if (this.traits.get(t.version)?.has(t.metadata.name)) {
throw new Error(
@ -115,6 +125,16 @@ export class Registry {
return res;
}
getAllTraits(): ImplementedRuntimeTrait[] {
const res: ImplementedRuntimeTrait[] = [];
for (const version of this.traits.values()) {
for (const trait of version.values()) {
res.push(trait);
}
}
return res
}
registerModule(c: ImplementedRuntimeModule, overWrite = false) {
const parsedModule = parseModuleSchema(cloneDeep(c));
if (!overWrite && this.modules.get(c.version)?.has(c.metadata.name)) {