Add verification and value stripping
This commit is contained in:
@ -63,6 +63,7 @@ export default function compile(ir: IR, target: CompileTarget) {
|
||||
// Types are verified. They are now ready to be compiled to targets
|
||||
|
||||
// setState("Building for target: " + target.name);
|
||||
target.start();
|
||||
ir.forEach((step) => {
|
||||
const [type, def] = step;
|
||||
if (type == "type") target.generateType(def as TypeDefinition);
|
||||
|
@ -27,7 +27,9 @@ export class TypescriptTarget extends CompileTarget {
|
||||
|
||||
flavour: "esm" | "node" = "node";
|
||||
|
||||
start() {}
|
||||
start() {
|
||||
this.writeFormattedFile("ts_base.ts", this.getTemplate("ts_base.ts"));
|
||||
}
|
||||
|
||||
private generateImport(imports: string, path: string) {
|
||||
return `import ${imports} from "${
|
||||
@ -39,10 +41,20 @@ export class TypescriptTarget extends CompileTarget {
|
||||
a: lineAppender,
|
||||
def: TypeDefinition | ServiceDefinition
|
||||
) {
|
||||
a(
|
||||
0,
|
||||
this.generateImport(
|
||||
`{ VerificationError, verify_number, verify_string, verify_boolean, verify_void, strip_number, strip_string, strip_boolean, strip_void }`,
|
||||
`./ts_base`
|
||||
)
|
||||
);
|
||||
a(
|
||||
0,
|
||||
def.depends.map((dep) =>
|
||||
this.generateImport(`${dep}, { verify_${dep} }`, "./" + dep)
|
||||
this.generateImport(
|
||||
`${dep}, { verify_${dep}, strip_${dep} }`,
|
||||
"./" + dep
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -79,7 +91,9 @@ export class TypescriptTarget extends CompileTarget {
|
||||
if (field.array) {
|
||||
type = toJSType(field.type) + "[]";
|
||||
} else if (field.map) {
|
||||
type = `Map<${toJSType(field.map)}, ${toJSType(field.type)}>`;
|
||||
type = `{ [key: ${toJSType(field.map)}]: ${toJSType(
|
||||
field.type
|
||||
)} }`;
|
||||
} else {
|
||||
type = toJSType(field.type);
|
||||
}
|
||||
@ -101,14 +115,44 @@ export class TypescriptTarget extends CompileTarget {
|
||||
|
||||
a(0, ``);
|
||||
|
||||
a(1, `static verify(data: ${def.name}){`);
|
||||
a(2, `return verify_${def.name}(data);`);
|
||||
a(1, `static verify(data: ${def.name}) {`);
|
||||
a(2, `verify_${def.name}(data);`);
|
||||
a(1, `}`);
|
||||
a(0, `}`);
|
||||
|
||||
a(0, ``);
|
||||
|
||||
a(0, `export function verify_${def.name}(data: ${def.name}): boolean {`);
|
||||
a(
|
||||
0,
|
||||
`export function strip_${def.name}(data: ${def.name}): ${def.name} {`
|
||||
);
|
||||
{
|
||||
a(1, `let res = {} as any;`);
|
||||
def.fields.forEach((field) => {
|
||||
if (field.array) {
|
||||
a(1, `res["${field.name}"] = data["${field.name}"].map(elm=>`);
|
||||
a(2, `strip_${field.type}(elm)`);
|
||||
a(1, `)`);
|
||||
} else if (field.map) {
|
||||
a(1, `res["${field.name}"] = {}`);
|
||||
a(
|
||||
1,
|
||||
`Object.entries(data["${field.name}"]).forEach(([key, val]) => res["${field.name}"][key] = strip_${field.type}(val))`
|
||||
);
|
||||
} else {
|
||||
a(
|
||||
1,
|
||||
`res["${field.name}"] = strip_${field.type}(data["${field.name}"])`
|
||||
);
|
||||
}
|
||||
});
|
||||
a(1, `return res;`);
|
||||
}
|
||||
a(0, `}`);
|
||||
|
||||
a(0, ``);
|
||||
|
||||
a(0, `export function verify_${def.name}(data: ${def.name}) {`);
|
||||
{
|
||||
def.fields.forEach((field) => {
|
||||
a(
|
||||
@ -116,37 +160,49 @@ export class TypescriptTarget extends CompileTarget {
|
||||
`if(data["${field.name}"] !== null && data["${field.name}"] !== undefined ) {`
|
||||
);
|
||||
|
||||
const verifyType = (varName: string) => {
|
||||
const verifyType = (varName: string, off = 0) => {
|
||||
switch (field.type) {
|
||||
case "string":
|
||||
a(2, `if(typeof ${varName} !== "string") return false;`);
|
||||
a(
|
||||
2 + off,
|
||||
`if(typeof ${varName} !== "string") throw new VerificationError("string", "${field.name}", ${varName});`
|
||||
);
|
||||
break;
|
||||
case "number":
|
||||
a(2, `if(typeof ${varName} !== "number") return false;`);
|
||||
a(
|
||||
2 + off,
|
||||
`if(typeof ${varName} !== "number") throw new VerificationError("number", "${field.name}", ${varName});`
|
||||
);
|
||||
break;
|
||||
case "boolean":
|
||||
a(2, `if(typeof ${varName} !== "boolean") return false;`);
|
||||
a(
|
||||
2 + off,
|
||||
`if(typeof ${varName} !== "boolean") throw new VerificationError("boolean", "${field.name}", ${varName});`
|
||||
);
|
||||
break;
|
||||
default:
|
||||
a(
|
||||
2,
|
||||
`if(!verify_${field.type}(${varName})) return false;`
|
||||
2 + off,
|
||||
`if(!verify_${field.type}(${varName})) throw new VerificationError("${field.type}", "${field.name}", ${varName});`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (field.array) {
|
||||
a(2, `if(!Array.isArray(data["${field.name}"])) return false`);
|
||||
a(
|
||||
2,
|
||||
`if(!Array.isArray(data["${field.name}"])) throw new VerificationError("array", "${field.name}", data["${field.name}"]);`
|
||||
);
|
||||
a(2, `for(const elm of data["${field.name}"]) {`);
|
||||
verifyType("elm");
|
||||
verifyType("elm", 1);
|
||||
a(2, `}`);
|
||||
} else if (field.map) {
|
||||
a(
|
||||
2,
|
||||
`if(typeof data["${field.name}"] !== "object") return false`
|
||||
`if(typeof data["${field.name}"] !== "object") throw new VerificationError("object", ${field.name}, data["${field.name}"]);`
|
||||
);
|
||||
a(2, `for(const key in data["${field.name}"]) {`);
|
||||
verifyType(`data["${field.name}"][key]`);
|
||||
verifyType(`data["${field.name}"][key]`, 1);
|
||||
a(2, `}`);
|
||||
} else {
|
||||
verifyType(`data["${field.name}"]`);
|
||||
@ -154,7 +210,7 @@ export class TypescriptTarget extends CompileTarget {
|
||||
a(1, "}");
|
||||
a(0, ``);
|
||||
});
|
||||
a(1, `return true`);
|
||||
a(1, `return data`);
|
||||
}
|
||||
a(0, `}`);
|
||||
|
||||
@ -176,22 +232,35 @@ export class TypescriptTarget extends CompileTarget {
|
||||
}
|
||||
a(0, `}`);
|
||||
|
||||
a(0, ``);
|
||||
|
||||
a(0, `export default ${def.name}`);
|
||||
|
||||
a(0, `export function verify_${def.name} (data: ${def.name}): boolean {`);
|
||||
a(1, `return ${def.name}[data] != undefined`);
|
||||
a(0, "}");
|
||||
a(0, ``);
|
||||
|
||||
a(
|
||||
0,
|
||||
`export function strip_${def.name} (data: ${def.name}): ${def.name} {`
|
||||
);
|
||||
a(1, `return data;`);
|
||||
a(0, `}`);
|
||||
a(0, ``);
|
||||
a(0, `export function verify_${def.name} (data: ${def.name}) {`);
|
||||
a(1, `if(${def.name}[data] == undefined) throw new VerificationError("${def.name}", undefined, data);`);
|
||||
a(1, `return data`);
|
||||
a(0, `}`);
|
||||
|
||||
this.writeFormattedFile(this.getFileName(def.name), lines.join("\n"));
|
||||
}
|
||||
|
||||
generateServiceClient(def: ServiceDefinition) {
|
||||
this.writeFile(
|
||||
this.writeFormattedFile(
|
||||
"service_client.ts",
|
||||
this.generateImport(
|
||||
"{ RequestObject, ResponseObject, ErrorCodes, Logging }",
|
||||
"./service_base"
|
||||
) +
|
||||
this.generateImport(" { VerificationError }", "./ts_base") +
|
||||
"\n\n" +
|
||||
this.getTemplate("ts_service_client.ts")
|
||||
);
|
||||
@ -212,14 +281,6 @@ export class TypescriptTarget extends CompileTarget {
|
||||
});
|
||||
a(0, `}`);
|
||||
|
||||
a(
|
||||
0,
|
||||
this.generateImport(
|
||||
"{ verify_number, verify_string, verify_boolean, verify_void }",
|
||||
"./service_base"
|
||||
)
|
||||
);
|
||||
|
||||
a(
|
||||
0,
|
||||
this.generateImport(
|
||||
@ -241,6 +302,7 @@ export class TypescriptTarget extends CompileTarget {
|
||||
(e) => `${e.name}: ${toJSType(e.type) + (e.array ? "[]" : "")}`
|
||||
)
|
||||
.join(", ");
|
||||
//TODO: Prio 1 : Verify and strip input parameters
|
||||
//TODO: Prio 2 : Add optional parameters to this and the declaration file
|
||||
if (!fnc.return) {
|
||||
a(1, `${fnc.name}(${params}): void {`);
|
||||
@ -267,16 +329,10 @@ export class TypescriptTarget extends CompileTarget {
|
||||
a(2, `}).then(result => {`);
|
||||
if (fnc.return.array) {
|
||||
a(2, `for(const elm of result) {`);
|
||||
a(
|
||||
3,
|
||||
`if(!verify_${fnc.return.type}(elm)) throw new Error("Invalid result data!");`
|
||||
);
|
||||
a(3, `verify_${fnc.return.type}(elm);`);
|
||||
a(2, `}`);
|
||||
} else {
|
||||
a(
|
||||
3,
|
||||
`if(!verify_${fnc.return.type}(result)) throw new Error("Invalid result data!");`
|
||||
);
|
||||
a(3, `verify_${fnc.return.type}(result);`);
|
||||
}
|
||||
a(3, `return result;`);
|
||||
a(2, `});`);
|
||||
@ -302,12 +358,13 @@ export class TypescriptTarget extends CompileTarget {
|
||||
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
||||
};
|
||||
|
||||
this.writeFile(
|
||||
this.writeFormattedFile(
|
||||
"service_server.ts",
|
||||
this.generateImport(
|
||||
"{ RequestObject, ResponseObject, ErrorCodes, Logging }",
|
||||
"./service_base"
|
||||
) +
|
||||
this.generateImport(" { VerificationError }", "./ts_base") +
|
||||
"\n\n" +
|
||||
this.getTemplate("ts_service_server.ts")
|
||||
);
|
||||
@ -322,14 +379,6 @@ export class TypescriptTarget extends CompileTarget {
|
||||
|
||||
a(0, this.generateImport("{ Service }", "./service_server"));
|
||||
|
||||
a(
|
||||
0,
|
||||
this.generateImport(
|
||||
"{ verify_number, verify_string, verify_boolean, verify_void }",
|
||||
"./service_base"
|
||||
)
|
||||
);
|
||||
|
||||
a(0, ``);
|
||||
|
||||
a(0, `export abstract class ${def.name}<T> extends Service<T> {`);
|
||||
@ -373,27 +422,27 @@ export class TypescriptTarget extends CompileTarget {
|
||||
a(2, `if(p[${i}] !== null && p[${i}] !== undefined) {`);
|
||||
if (fnc.inputs[i].array) {
|
||||
a(2, `for(const elm of p[${i}]) {`);
|
||||
a(
|
||||
3,
|
||||
`if(!verify_${fnc.inputs[i].type}(elm)) throw new Error("Parameter verification failed!")`
|
||||
);
|
||||
a(3, `verify_${fnc.inputs[i].type}(elm)`);
|
||||
a(2, `}`);
|
||||
} else {
|
||||
a(
|
||||
2,
|
||||
`if(!verify_${fnc.inputs[i].type}(p[${i}])) throw new Error("Parameter verification failed!")`
|
||||
);
|
||||
a(2, `verify_${fnc.inputs[i].type}(p[${i}])`);
|
||||
}
|
||||
a(2, `}`);
|
||||
}
|
||||
|
||||
a(2, ``);
|
||||
a(2, `p.push(ctx);`);
|
||||
|
||||
a(
|
||||
2,
|
||||
`return this.${fnc.name}.call(this, ...p)${
|
||||
fnc.return?.type == "void" ? ".then(res => undefined)" : ""
|
||||
};`
|
||||
`return this.${fnc.name}.call(this, ...p)` + //TODO: Refactor. This line is way to compicated for anyone to understand, including me
|
||||
(fnc.return
|
||||
? `.then(${
|
||||
fnc.return?.array
|
||||
? `res => res.map(e => verify_${fnc.return.type}(strip_${fnc.return.type}(e)))`
|
||||
: `res => verify_${fnc.return.type}(strip_${fnc.return.type}(res))`
|
||||
});`
|
||||
: "")
|
||||
);
|
||||
a(1, `}`);
|
||||
a(0, ``);
|
||||
@ -408,7 +457,10 @@ export class TypescriptTarget extends CompileTarget {
|
||||
}
|
||||
|
||||
generateService(def: ServiceDefinition) {
|
||||
this.writeFile("service_base.ts", this.getTemplate("ts_service_base.ts"));
|
||||
this.writeFormattedFile(
|
||||
"service_base.ts",
|
||||
this.getTemplate("ts_service_base.ts")
|
||||
);
|
||||
this.generateServiceClient(def);
|
||||
this.generateServiceServer(def);
|
||||
}
|
||||
@ -446,12 +498,12 @@ export class TypescriptTarget extends CompileTarget {
|
||||
a(
|
||||
0,
|
||||
this.generateImport(
|
||||
`${def.name}, { verify_${def.name} }`,
|
||||
`${def.name}, { verify_${def.name}, strip_${def.name} }`,
|
||||
"./" + def.name
|
||||
)
|
||||
);
|
||||
|
||||
a(0, `export { verify_${def.name} }`);
|
||||
a(0, `export { verify_${def.name}, strip_${def.name} }`);
|
||||
a(0, `export type { ${def.name} }`);
|
||||
a(0, ``);
|
||||
break;
|
||||
@ -459,11 +511,14 @@ export class TypescriptTarget extends CompileTarget {
|
||||
a(
|
||||
0,
|
||||
this.generateImport(
|
||||
`${def.name}, { verify_${def.name} }`,
|
||||
`${def.name}, { verify_${def.name}, strip_${def.name} }`,
|
||||
"./" + def.name
|
||||
)
|
||||
);
|
||||
a(0, `export { ${def.name}, verify_${def.name} }`);
|
||||
a(
|
||||
0,
|
||||
`export { ${def.name}, verify_${def.name}, strip_${def.name} }`
|
||||
);
|
||||
a(0, ``);
|
||||
break;
|
||||
|
||||
|
Reference in New Issue
Block a user