Skip to content

Commit

Permalink
Add validators only option
Browse files Browse the repository at this point in the history
  • Loading branch information
skonves committed Oct 21, 2024
1 parent 525f51d commit e19c380
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 103 deletions.
16 changes: 10 additions & 6 deletions src/sanitizer-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ export class SanitizerFactory {
) {}

build(): File[] {
return [
{
path: buildFilePath(['sanitizers.ts'], this.service, this.options),
contents: format(from(this.buildFile()), this.options),
},
];
if (!this.options?.typescriptValidators?.validatorsOnly) {
return [
{
path: buildFilePath(['sanitizers.ts'], this.service, this.options),
contents: format(from(this.buildFile()), this.options),
},
];
}

return [];
}

private buildMethodName(member: Type | Union): string {
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NamespacedTypescriptOptions } from '@basketry/typescript/lib/types';

export declare type TypescriptValidatorsOptions = {
typesImportPath?: string;
validatorsOnly?: boolean;
};

export declare type NamespacedTypescriptValidatorsOptions =
Expand Down
197 changes: 100 additions & 97 deletions src/validator-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,11 @@ export class ValidatorFactory {
this.options?.typescriptValidators?.typesImportPath ?? './types'
}"`;

yield `import ${
this.options?.typescript?.typeImports ? ' type ' : ' '
} * as sanitizers from "./sanitizers"`;
if (!this.options?.typescriptValidators?.validatorsOnly) {
yield `import ${
this.options?.typescript?.typeImports ? ' type ' : ' '
} * as sanitizers from "./sanitizers"`;
}
}

private readonly codes = new Set<string>();
Expand Down Expand Up @@ -428,118 +430,119 @@ export class ValidatorFactory {
}

private *buildValidatedServiceWrappers(): Iterable<string> {
yield `export type ResponseBuilder<T> = (validationErrors: ValidationError[], err: any) => T`;
for (const int of sort(this.service.interfaces)) {
const returnTypes = sort(
Array.from(
new Set(
int.methods
.map((m) =>
getTypeByName(this.service, m.returnType?.typeName.value),
)
.filter((t): t is Type => !!t),
if (!this.options?.typescriptValidators?.validatorsOnly) {
yield `export type ResponseBuilder<T> = (validationErrors: ValidationError[], err: any) => T`;
for (const int of sort(this.service.interfaces)) {
const returnTypes = sort(
Array.from(
new Set(
int.methods
.map((m) =>
getTypeByName(this.service, m.returnType?.typeName.value),
)
.filter((t): t is Type => !!t),
),
),
),
);

const hasVoid = int.methods.some((m) => !m.returnType);

const handlers = returnTypes.map(
(type) =>
`${camel(
`build_${type.name.value}`,
)}: ResponseBuilder<${buildTypeName(type, 'types')}>`,
);
if (hasVoid) {
handlers.push('buildVoid: ResponseBuilder<void>');
}

const handlersType = `{${handlers.join(',')}}`;

const intName = buildInterfaceName(int, 'types');
yield `export class ${pascal(
`validated_${int.name.value}_service`,
)} implements ${buildInterfaceName(int, 'types')} {`;
yield `constructor(private readonly service: ${intName}, private readonly handlers: ${handlersType}){}`;
yield '';
for (const method of sort(int.methods)) {
const methodName = buildMethodName(method);
const returnType = getTypeByName(
this.service,
method.returnType?.typeName.value,
);

const sanitize = (call: string, isAsync: boolean): string => {
if (returnType) {
return `sanitizers.${camel(`sanitize_${returnType.name.value}`)}(${
isAsync ? 'await' : ''
} ${call})`;
} else {
return call;
}
};
const hasVoid = int.methods.some((m) => !m.returnType);

const sanitizeAsync = (call: string): string => {
return sanitize(call, true);
};
const handlers = returnTypes.map(
(type) =>
`${camel(
`build_${type.name.value}`,
)}: ResponseBuilder<${buildTypeName(type, 'types')}>`,
);
if (hasVoid) {
handlers.push('buildVoid: ResponseBuilder<void>');
}

const sanitizeSync = (call: string): string => {
return sanitize(call, false);
};
const handlersType = `{${handlers.join(',')}}`;

const handlerName = returnType
? `this.handlers.${camel(`build_${returnType.name.value}`)}`
: 'this.handlers.buildVoid';
const intName = buildInterfaceName(int, 'types');
yield `export class ${pascal(
`validated_${int.name.value}_service`,
)} implements ${buildInterfaceName(int, 'types')} {`;
yield `constructor(private readonly service: ${intName}, private readonly handlers: ${handlersType}){}`;
yield '';
for (const method of sort(int.methods)) {
const methodName = buildMethodName(method);
const returnType = getTypeByName(
this.service,
method.returnType?.typeName.value,
);

const hasParams = !!method.parameters.length;
const hasRequiredParams = method.parameters.some(isRequired);
const paramDef = method.parameters.length
? `params${hasRequiredParams ? '' : '?'}: ${buildMethodParamsTypeName(
const sanitize = (call: string, isAsync: boolean): string => {
if (returnType) {
return `sanitizers.${camel(
`sanitize_${returnType.name.value}`,
)}(${isAsync ? 'await' : ''} ${call})`;
} else {
return call;
}
};

const sanitizeAsync = (call: string): string => {
return sanitize(call, true);
};

const sanitizeSync = (call: string): string => {
return sanitize(call, false);
};

const handlerName = returnType
? `this.handlers.${camel(`build_${returnType.name.value}`)}`
: 'this.handlers.buildVoid';

const hasParams = !!method.parameters.length;
const hasRequiredParams = method.parameters.some(isRequired);
const paramDef = method.parameters.length
? `params${
hasRequiredParams ? '' : '?'
}: ${buildMethodParamsTypeName(method, 'types')}`
: '';
yield `async ${methodName}(${paramDef}) {`;
yield `${
hasParams ? 'let' : 'const'
} validationErrors: ValidationError[] = [];`;
yield 'try {';
if (hasParams) {
yield `validationErrors = ${buildParamsValidatorName(
method,
'types',
)}`
: '';
yield `async ${methodName}(${paramDef}) {`;
yield `${
hasParams ? 'let' : 'const'
} validationErrors: ValidationError[] = [];`;
yield 'try {';
if (hasParams) {
yield `validationErrors = ${buildParamsValidatorName(
method,
)}(params)`;
yield `if(validationErrors.length) {`;
)}(params)`;
yield `if(validationErrors.length) {`;
if (returnType) {
yield `return ${sanitizeSync(
`${handlerName}(validationErrors, undefined)`,
)}`;
} else {
yield `return ${handlerName}(validationErrors, undefined)`;
}
yield '}';
}
if (hasParams) {
yield `const sanitizedParams = sanitizers.${camel(
`sanitize_${method.name.value}_params`,
)}(params)`;
}
yield `return ${sanitizeAsync(
`this.service.${methodName}(${hasParams ? 'sanitizedParams' : ''})`,
)}`;
yield '} catch (err) {';
if (returnType) {
yield `return ${sanitizeSync(
`${handlerName}(validationErrors, undefined)`,
`${handlerName}(validationErrors, err)`,
)}`;
} else {
yield `return ${handlerName}(validationErrors, undefined)`;
yield `return ${handlerName}(validationErrors, err)`;
}
yield '}';
}
if (hasParams) {
yield `const sanitizedParams = sanitizers.${camel(
`sanitize_${method.name.value}_params`,
)}(params)`;
}
yield `return ${sanitizeAsync(
`this.service.${methodName}(${hasParams ? 'sanitizedParams' : ''})`,
)}`;
yield '} catch (err) {';
if (returnType) {
yield `return ${sanitizeSync(
`${handlerName}(validationErrors, err)`,
)}`;
} else {
yield `return ${handlerName}(validationErrors, err)`;
yield '}';
yield '';
}
yield '}';
yield '}';
yield '';
}
yield '}';
yield '';
}
}
}
Expand Down

0 comments on commit e19c380

Please sign in to comment.