Add verification and value stripping
This commit is contained in:
parent
db21c1c42e
commit
cf49fca928
@ -4,6 +4,7 @@ import { AddValueRequest, AddValueResponse, Logging } from "./out/index";
|
|||||||
|
|
||||||
import * as Client from "./out/index_client";
|
import * as Client from "./out/index_client";
|
||||||
import * as Server from "./out/index_server";
|
import * as Server from "./out/index_server";
|
||||||
|
import { VerificationError } from "./out/ts_base";
|
||||||
|
|
||||||
const client = new Client.ServiceProvider((msg) => {
|
const client = new Client.ServiceProvider((msg) => {
|
||||||
session.onMessage(msg);
|
session.onMessage(msg);
|
||||||
@ -72,13 +73,27 @@ async function run() {
|
|||||||
console.log("!!!!This should have failed!!!!");
|
console.log("!!!!This should have failed!!!!");
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log("Found expected error!", err.message);
|
if (err instanceof VerificationError)
|
||||||
|
console.log(
|
||||||
|
"Found expected error!",
|
||||||
|
{
|
||||||
|
type: err.type,
|
||||||
|
field: err.field,
|
||||||
|
value: err.value
|
||||||
|
}
|
||||||
|
);
|
||||||
|
else {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error("Unexpected Error");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Test with arrays:");
|
console.log("Test with arrays:");
|
||||||
console.log(await test
|
console.log(
|
||||||
|
await test
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
.FunctionWithArrayAsParamAndReturn([1,2,3], [4,5,6]));
|
.FunctionWithArrayAsParamAndReturn([1, 2, 3], [4, 5, 6])
|
||||||
|
);
|
||||||
|
|
||||||
console.log("Let with Array fail!");
|
console.log("Let with Array fail!");
|
||||||
await test
|
await test
|
||||||
@ -88,7 +103,19 @@ async function run() {
|
|||||||
console.log("!!!!This should have failed!!!!");
|
console.log("!!!!This should have failed!!!!");
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log("Found expected error!", err.message);
|
if (err instanceof VerificationError)
|
||||||
|
console.log(
|
||||||
|
"Found expected error!",
|
||||||
|
{
|
||||||
|
type: err.type,
|
||||||
|
field: err.field,
|
||||||
|
value: err.value
|
||||||
|
}
|
||||||
|
);
|
||||||
|
else {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error("Unexpected Error");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
89
lib/jrpc.js
89
lib/jrpc.js
@ -7496,6 +7496,7 @@ var CompileTarget = class {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
function compile(ir, target) {
|
function compile(ir, target) {
|
||||||
|
target.start();
|
||||||
ir.forEach((step) => {
|
ir.forEach((step) => {
|
||||||
const [type, def] = step;
|
const [type, def] = step;
|
||||||
if (type == "type")
|
if (type == "type")
|
||||||
@ -7523,13 +7524,15 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
name = "Typescript";
|
name = "Typescript";
|
||||||
flavour = "node";
|
flavour = "node";
|
||||||
start() {
|
start() {
|
||||||
|
this.writeFormattedFile("ts_base.ts", this.getTemplate("ts_base.ts"));
|
||||||
}
|
}
|
||||||
generateImport(imports, path) {
|
generateImport(imports, path) {
|
||||||
return `import ${imports} from "${path + (this.flavour === "esm" ? ".ts" : "")}";
|
return `import ${imports} from "${path + (this.flavour === "esm" ? ".ts" : "")}";
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
generateImports(a, def) {
|
generateImports(a, def) {
|
||||||
a(0, def.depends.map((dep) => this.generateImport(`${dep}, { verify_${dep} }`, "./" + dep)));
|
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}, strip_${dep} }`, "./" + dep)));
|
||||||
}
|
}
|
||||||
getFileName(typename) {
|
getFileName(typename) {
|
||||||
return typename + ".ts";
|
return typename + ".ts";
|
||||||
@ -7552,7 +7555,7 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
if (field.array) {
|
if (field.array) {
|
||||||
type = toJSType(field.type) + "[]";
|
type = toJSType(field.type) + "[]";
|
||||||
} else if (field.map) {
|
} else if (field.map) {
|
||||||
type = `Map<${toJSType(field.map)}, ${toJSType(field.type)}>`;
|
type = `{ [key: ${toJSType(field.map)}]: ${toJSType(field.type)} }`;
|
||||||
} else {
|
} else {
|
||||||
type = toJSType(field.type);
|
type = toJSType(field.type);
|
||||||
}
|
}
|
||||||
@ -7570,38 +7573,57 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
a(0, ``);
|
a(0, ``);
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
a(1, `static verify(data: ${def.name}) {`);
|
a(1, `static verify(data: ${def.name}) {`);
|
||||||
a(2, `return verify_${def.name}(data);`);
|
a(2, `verify_${def.name}(data);`);
|
||||||
a(1, `}`);
|
a(1, `}`);
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
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) => {
|
def.fields.forEach((field) => {
|
||||||
a(1, `if(data["${field.name}"] !== null && data["${field.name}"] !== undefined ) {`);
|
a(1, `if(data["${field.name}"] !== null && data["${field.name}"] !== undefined ) {`);
|
||||||
const verifyType = (varName) => {
|
const verifyType = (varName, off = 0) => {
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case "string":
|
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;
|
break;
|
||||||
case "number":
|
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;
|
break;
|
||||||
case "boolean":
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
a(2, `if(!verify_${field.type}(${varName})) return false;`);
|
a(2 + off, `if(!verify_${field.type}(${varName})) throw new VerificationError("${field.type}", "${field.name}", ${varName});`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (field.array) {
|
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}"]) {`);
|
a(2, `for(const elm of data["${field.name}"]) {`);
|
||||||
verifyType("elm");
|
verifyType("elm", 1);
|
||||||
a(2, `}`);
|
a(2, `}`);
|
||||||
} else if (field.map) {
|
} else if (field.map) {
|
||||||
a(2, `if(typeof data["${field.name}"] !== "object") return false`);
|
a(2, `if(typeof data["${field.name}"] !== "object") throw new VerificationError("object", ${field.name}, data["${field.name}"]);`);
|
||||||
a(2, `for(const key in 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, `}`);
|
a(2, `}`);
|
||||||
} else {
|
} else {
|
||||||
verifyType(`data["${field.name}"]`);
|
verifyType(`data["${field.name}"]`);
|
||||||
@ -7609,7 +7631,7 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
a(1, "}");
|
a(1, "}");
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
});
|
});
|
||||||
a(1, `return true`);
|
a(1, `return data`);
|
||||||
}
|
}
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
this.writeFormattedFile(this.getFileName(def.name), lines.join("\n"));
|
this.writeFormattedFile(this.getFileName(def.name), lines.join("\n"));
|
||||||
@ -7627,14 +7649,21 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
a(1, `${value.name}=${value.value},`);
|
a(1, `${value.name}=${value.value},`);
|
||||||
}
|
}
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
|
a(0, ``);
|
||||||
a(0, `export default ${def.name}`);
|
a(0, `export default ${def.name}`);
|
||||||
a(0, `export function verify_${def.name} (data: ${def.name}): boolean {`);
|
a(0, ``);
|
||||||
a(1, `return ${def.name}[data] != undefined`);
|
a(0, `export function strip_${def.name} (data: ${def.name}): ${def.name} {`);
|
||||||
a(0, "}");
|
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"));
|
this.writeFormattedFile(this.getFileName(def.name), lines.join("\n"));
|
||||||
}
|
}
|
||||||
generateServiceClient(def) {
|
generateServiceClient(def) {
|
||||||
this.writeFile("service_client.ts", this.generateImport("{ RequestObject, ResponseObject, ErrorCodes, Logging }", "./service_base") + "\n\n" + this.getTemplate("ts_service_client.ts"));
|
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"));
|
||||||
let lines = [];
|
let lines = [];
|
||||||
const a = (i, t) => {
|
const a = (i, t) => {
|
||||||
if (!Array.isArray(t)) {
|
if (!Array.isArray(t)) {
|
||||||
@ -7648,7 +7677,6 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
a(1, `${dep},`);
|
a(1, `${dep},`);
|
||||||
});
|
});
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
a(0, this.generateImport("{ verify_number, verify_string, verify_boolean, verify_void }", "./service_base"));
|
|
||||||
a(0, this.generateImport("{ Service, ServiceProvider, getRandomID }", "./service_client"));
|
a(0, this.generateImport("{ Service, ServiceProvider, getRandomID }", "./service_client"));
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
a(0, `export class ${def.name} extends Service {`);
|
a(0, `export class ${def.name} extends Service {`);
|
||||||
@ -7680,10 +7708,10 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
a(2, `}).then(result => {`);
|
a(2, `}).then(result => {`);
|
||||||
if (fnc.return.array) {
|
if (fnc.return.array) {
|
||||||
a(2, `for(const elm of result) {`);
|
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, `}`);
|
a(2, `}`);
|
||||||
} else {
|
} 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(3, `return result;`);
|
||||||
a(2, `});`);
|
a(2, `});`);
|
||||||
@ -7703,7 +7731,7 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
}
|
}
|
||||||
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
||||||
};
|
};
|
||||||
this.writeFile("service_server.ts", this.generateImport("{ RequestObject, ResponseObject, ErrorCodes, Logging }", "./service_base") + "\n\n" + this.getTemplate("ts_service_server.ts"));
|
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"));
|
||||||
this.generateImports(a, def);
|
this.generateImports(a, def);
|
||||||
a(0, `export type {`);
|
a(0, `export type {`);
|
||||||
def.depends.forEach((dep) => {
|
def.depends.forEach((dep) => {
|
||||||
@ -7711,7 +7739,6 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
});
|
});
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
a(0, this.generateImport("{ Service }", "./service_server"));
|
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, ``);
|
||||||
a(0, `export abstract class ${def.name}<T> extends Service<T> {`);
|
a(0, `export abstract class ${def.name}<T> extends Service<T> {`);
|
||||||
a(1, `public name = "${def.name}";`);
|
a(1, `public name = "${def.name}";`);
|
||||||
@ -7743,16 +7770,16 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
a(2, `if(p[${i}] !== null && p[${i}] !== undefined) {`);
|
a(2, `if(p[${i}] !== null && p[${i}] !== undefined) {`);
|
||||||
if (fnc.inputs[i].array) {
|
if (fnc.inputs[i].array) {
|
||||||
a(2, `for(const elm of p[${i}]) {`);
|
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, `}`);
|
a(2, `}`);
|
||||||
} else {
|
} 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, ``);
|
a(2, ``);
|
||||||
a(2, `p.push(ctx);`);
|
a(2, `p.push(ctx);`);
|
||||||
a(2, `return this.${fnc.name}.call(this, ...p)${((_a = fnc.return) == null ? void 0 : _a.type) == "void" ? ".then(res => undefined)" : ""};`);
|
a(2, `return this.${fnc.name}.call(this, ...p)` + (fnc.return ? `.then(${((_a = fnc.return) == null ? void 0 : _a.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(1, `}`);
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
}
|
}
|
||||||
@ -7760,7 +7787,7 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
this.writeFormattedFile(this.getFileName(def.name + "_server"), lines.join("\n"));
|
this.writeFormattedFile(this.getFileName(def.name + "_server"), lines.join("\n"));
|
||||||
}
|
}
|
||||||
generateService(def) {
|
generateService(def) {
|
||||||
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.generateServiceClient(def);
|
||||||
this.generateServiceServer(def);
|
this.generateServiceServer(def);
|
||||||
}
|
}
|
||||||
@ -7790,14 +7817,14 @@ var TypescriptTarget = class extends CompileTarget {
|
|||||||
steps.forEach(([type, def]) => {
|
steps.forEach(([type, def]) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "type":
|
case "type":
|
||||||
a(0, this.generateImport(`${def.name}, { verify_${def.name} }`, "./" + def.name));
|
a(0, this.generateImport(`${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, `export type { ${def.name} }`);
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
break;
|
break;
|
||||||
case "enum":
|
case "enum":
|
||||||
a(0, this.generateImport(`${def.name}, { verify_${def.name} }`, "./" + def.name));
|
a(0, this.generateImport(`${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, ``);
|
a(0, ``);
|
||||||
break;
|
break;
|
||||||
case "service":
|
case "service":
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hibas123/jrpcgen",
|
"name": "@hibas123/jrpcgen",
|
||||||
"version": "1.0.5",
|
"version": "1.0.6",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"packageManager": "yarn@3.1.1",
|
"packageManager": "yarn@3.1.1",
|
||||||
|
@ -63,6 +63,7 @@ export default function compile(ir: IR, target: CompileTarget) {
|
|||||||
// Types are verified. They are now ready to be compiled to targets
|
// Types are verified. They are now ready to be compiled to targets
|
||||||
|
|
||||||
// setState("Building for target: " + target.name);
|
// setState("Building for target: " + target.name);
|
||||||
|
target.start();
|
||||||
ir.forEach((step) => {
|
ir.forEach((step) => {
|
||||||
const [type, def] = step;
|
const [type, def] = step;
|
||||||
if (type == "type") target.generateType(def as TypeDefinition);
|
if (type == "type") target.generateType(def as TypeDefinition);
|
||||||
|
@ -27,7 +27,9 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
|
|
||||||
flavour: "esm" | "node" = "node";
|
flavour: "esm" | "node" = "node";
|
||||||
|
|
||||||
start() {}
|
start() {
|
||||||
|
this.writeFormattedFile("ts_base.ts", this.getTemplate("ts_base.ts"));
|
||||||
|
}
|
||||||
|
|
||||||
private generateImport(imports: string, path: string) {
|
private generateImport(imports: string, path: string) {
|
||||||
return `import ${imports} from "${
|
return `import ${imports} from "${
|
||||||
@ -39,10 +41,20 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
a: lineAppender,
|
a: lineAppender,
|
||||||
def: TypeDefinition | ServiceDefinition
|
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(
|
a(
|
||||||
0,
|
0,
|
||||||
def.depends.map((dep) =>
|
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) {
|
if (field.array) {
|
||||||
type = toJSType(field.type) + "[]";
|
type = toJSType(field.type) + "[]";
|
||||||
} else if (field.map) {
|
} else if (field.map) {
|
||||||
type = `Map<${toJSType(field.map)}, ${toJSType(field.type)}>`;
|
type = `{ [key: ${toJSType(field.map)}]: ${toJSType(
|
||||||
|
field.type
|
||||||
|
)} }`;
|
||||||
} else {
|
} else {
|
||||||
type = toJSType(field.type);
|
type = toJSType(field.type);
|
||||||
}
|
}
|
||||||
@ -102,13 +116,43 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
a(0, ``);
|
a(0, ``);
|
||||||
|
|
||||||
a(1, `static verify(data: ${def.name}) {`);
|
a(1, `static verify(data: ${def.name}) {`);
|
||||||
a(2, `return verify_${def.name}(data);`);
|
a(2, `verify_${def.name}(data);`);
|
||||||
a(1, `}`);
|
a(1, `}`);
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
|
|
||||||
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) => {
|
def.fields.forEach((field) => {
|
||||||
a(
|
a(
|
||||||
@ -116,37 +160,49 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
`if(data["${field.name}"] !== null && data["${field.name}"] !== undefined ) {`
|
`if(data["${field.name}"] !== null && data["${field.name}"] !== undefined ) {`
|
||||||
);
|
);
|
||||||
|
|
||||||
const verifyType = (varName: string) => {
|
const verifyType = (varName: string, off = 0) => {
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case "string":
|
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;
|
break;
|
||||||
case "number":
|
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;
|
break;
|
||||||
case "boolean":
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
a(
|
a(
|
||||||
2,
|
2 + off,
|
||||||
`if(!verify_${field.type}(${varName})) return false;`
|
`if(!verify_${field.type}(${varName})) throw new VerificationError("${field.type}", "${field.name}", ${varName});`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (field.array) {
|
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}"]) {`);
|
a(2, `for(const elm of data["${field.name}"]) {`);
|
||||||
verifyType("elm");
|
verifyType("elm", 1);
|
||||||
a(2, `}`);
|
a(2, `}`);
|
||||||
} else if (field.map) {
|
} else if (field.map) {
|
||||||
a(
|
a(
|
||||||
2,
|
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}"]) {`);
|
a(2, `for(const key in data["${field.name}"]) {`);
|
||||||
verifyType(`data["${field.name}"][key]`);
|
verifyType(`data["${field.name}"][key]`, 1);
|
||||||
a(2, `}`);
|
a(2, `}`);
|
||||||
} else {
|
} else {
|
||||||
verifyType(`data["${field.name}"]`);
|
verifyType(`data["${field.name}"]`);
|
||||||
@ -154,7 +210,7 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
a(1, "}");
|
a(1, "}");
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
});
|
});
|
||||||
a(1, `return true`);
|
a(1, `return data`);
|
||||||
}
|
}
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
|
|
||||||
@ -176,22 +232,35 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
}
|
}
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
|
|
||||||
|
a(0, ``);
|
||||||
|
|
||||||
a(0, `export default ${def.name}`);
|
a(0, `export default ${def.name}`);
|
||||||
|
|
||||||
a(0, `export function verify_${def.name} (data: ${def.name}): boolean {`);
|
a(0, ``);
|
||||||
a(1, `return ${def.name}[data] != undefined`);
|
|
||||||
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"));
|
this.writeFormattedFile(this.getFileName(def.name), lines.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
generateServiceClient(def: ServiceDefinition) {
|
generateServiceClient(def: ServiceDefinition) {
|
||||||
this.writeFile(
|
this.writeFormattedFile(
|
||||||
"service_client.ts",
|
"service_client.ts",
|
||||||
this.generateImport(
|
this.generateImport(
|
||||||
"{ RequestObject, ResponseObject, ErrorCodes, Logging }",
|
"{ RequestObject, ResponseObject, ErrorCodes, Logging }",
|
||||||
"./service_base"
|
"./service_base"
|
||||||
) +
|
) +
|
||||||
|
this.generateImport(" { VerificationError }", "./ts_base") +
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
this.getTemplate("ts_service_client.ts")
|
this.getTemplate("ts_service_client.ts")
|
||||||
);
|
);
|
||||||
@ -212,14 +281,6 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
});
|
});
|
||||||
a(0, `}`);
|
a(0, `}`);
|
||||||
|
|
||||||
a(
|
|
||||||
0,
|
|
||||||
this.generateImport(
|
|
||||||
"{ verify_number, verify_string, verify_boolean, verify_void }",
|
|
||||||
"./service_base"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
a(
|
a(
|
||||||
0,
|
0,
|
||||||
this.generateImport(
|
this.generateImport(
|
||||||
@ -241,6 +302,7 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
(e) => `${e.name}: ${toJSType(e.type) + (e.array ? "[]" : "")}`
|
(e) => `${e.name}: ${toJSType(e.type) + (e.array ? "[]" : "")}`
|
||||||
)
|
)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
//TODO: Prio 1 : Verify and strip input parameters
|
||||||
//TODO: Prio 2 : Add optional parameters to this and the declaration file
|
//TODO: Prio 2 : Add optional parameters to this and the declaration file
|
||||||
if (!fnc.return) {
|
if (!fnc.return) {
|
||||||
a(1, `${fnc.name}(${params}): void {`);
|
a(1, `${fnc.name}(${params}): void {`);
|
||||||
@ -267,16 +329,10 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
a(2, `}).then(result => {`);
|
a(2, `}).then(result => {`);
|
||||||
if (fnc.return.array) {
|
if (fnc.return.array) {
|
||||||
a(2, `for(const elm of result) {`);
|
a(2, `for(const elm of result) {`);
|
||||||
a(
|
a(3, `verify_${fnc.return.type}(elm);`);
|
||||||
3,
|
|
||||||
`if(!verify_${fnc.return.type}(elm)) throw new Error("Invalid result data!");`
|
|
||||||
);
|
|
||||||
a(2, `}`);
|
a(2, `}`);
|
||||||
} else {
|
} else {
|
||||||
a(
|
a(3, `verify_${fnc.return.type}(result);`);
|
||||||
3,
|
|
||||||
`if(!verify_${fnc.return.type}(result)) throw new Error("Invalid result data!");`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
a(3, `return result;`);
|
a(3, `return result;`);
|
||||||
a(2, `});`);
|
a(2, `});`);
|
||||||
@ -302,12 +358,13 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.writeFile(
|
this.writeFormattedFile(
|
||||||
"service_server.ts",
|
"service_server.ts",
|
||||||
this.generateImport(
|
this.generateImport(
|
||||||
"{ RequestObject, ResponseObject, ErrorCodes, Logging }",
|
"{ RequestObject, ResponseObject, ErrorCodes, Logging }",
|
||||||
"./service_base"
|
"./service_base"
|
||||||
) +
|
) +
|
||||||
|
this.generateImport(" { VerificationError }", "./ts_base") +
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
this.getTemplate("ts_service_server.ts")
|
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("{ Service }", "./service_server"));
|
||||||
|
|
||||||
a(
|
|
||||||
0,
|
|
||||||
this.generateImport(
|
|
||||||
"{ verify_number, verify_string, verify_boolean, verify_void }",
|
|
||||||
"./service_base"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
|
|
||||||
a(0, `export abstract class ${def.name}<T> extends Service<T> {`);
|
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) {`);
|
a(2, `if(p[${i}] !== null && p[${i}] !== undefined) {`);
|
||||||
if (fnc.inputs[i].array) {
|
if (fnc.inputs[i].array) {
|
||||||
a(2, `for(const elm of p[${i}]) {`);
|
a(2, `for(const elm of p[${i}]) {`);
|
||||||
a(
|
a(3, `verify_${fnc.inputs[i].type}(elm)`);
|
||||||
3,
|
|
||||||
`if(!verify_${fnc.inputs[i].type}(elm)) throw new Error("Parameter verification failed!")`
|
|
||||||
);
|
|
||||||
a(2, `}`);
|
a(2, `}`);
|
||||||
} else {
|
} else {
|
||||||
a(
|
a(2, `verify_${fnc.inputs[i].type}(p[${i}])`);
|
||||||
2,
|
|
||||||
`if(!verify_${fnc.inputs[i].type}(p[${i}])) throw new Error("Parameter verification failed!")`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
a(2, `}`);
|
a(2, `}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
a(2, ``);
|
a(2, ``);
|
||||||
a(2, `p.push(ctx);`);
|
a(2, `p.push(ctx);`);
|
||||||
|
|
||||||
a(
|
a(
|
||||||
2,
|
2,
|
||||||
`return this.${fnc.name}.call(this, ...p)${
|
`return this.${fnc.name}.call(this, ...p)` + //TODO: Refactor. This line is way to compicated for anyone to understand, including me
|
||||||
fnc.return?.type == "void" ? ".then(res => undefined)" : ""
|
(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(1, `}`);
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
@ -408,7 +457,10 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generateService(def: ServiceDefinition) {
|
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.generateServiceClient(def);
|
||||||
this.generateServiceServer(def);
|
this.generateServiceServer(def);
|
||||||
}
|
}
|
||||||
@ -446,12 +498,12 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
a(
|
a(
|
||||||
0,
|
0,
|
||||||
this.generateImport(
|
this.generateImport(
|
||||||
`${def.name}, { verify_${def.name} }`,
|
`${def.name}, { verify_${def.name}, strip_${def.name} }`,
|
||||||
"./" + 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, `export type { ${def.name} }`);
|
||||||
a(0, ``);
|
a(0, ``);
|
||||||
break;
|
break;
|
||||||
@ -459,11 +511,14 @@ export class TypescriptTarget extends CompileTarget {
|
|||||||
a(
|
a(
|
||||||
0,
|
0,
|
||||||
this.generateImport(
|
this.generateImport(
|
||||||
`${def.name}, { verify_${def.name} }`,
|
`${def.name}, { verify_${def.name}, strip_${def.name} }`,
|
||||||
"./" + 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, ``);
|
a(0, ``);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
43
templates/ts_base.ts
Normal file
43
templates/ts_base.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
export class VerificationError extends Error {
|
||||||
|
constructor(
|
||||||
|
public readonly type?: string,
|
||||||
|
public readonly field?: string,
|
||||||
|
public readonly value?: any
|
||||||
|
) {
|
||||||
|
super("Parameter verification failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verify_number(data: any) {
|
||||||
|
if (typeof data !== "number") throw new VerificationError("number", undefined, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function strip_number(data: any) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verify_string(data: any) {
|
||||||
|
if (typeof data !== "string") throw new VerificationError("string", undefined, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function strip_string(data: any) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verify_boolean(data: any) {
|
||||||
|
if (typeof data !== "boolean") throw new VerificationError("boolean", undefined, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function strip_boolean(data: any) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Should it return data? it is kinda undefined actually...
|
||||||
|
export function verify_void(data: any) {}
|
||||||
|
|
||||||
|
export function strip_void(data: any) {
|
||||||
|
return undefined;
|
||||||
|
}
|
@ -28,25 +28,3 @@ export interface ResponseObject {
|
|||||||
error?: { code: ErrorCodes; message: string; data?: any };
|
error?: { code: ErrorCodes; message: string; data?: any };
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function verify_number(data: any) {
|
|
||||||
if (typeof data !== "number") return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function verify_string(data: any) {
|
|
||||||
if (typeof data !== "string") return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function verify_boolean(data: any) {
|
|
||||||
if (typeof data !== "boolean") return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function verify_void(data: any) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
//@template-ignore
|
//@template-ignore
|
||||||
|
import { VerificationError } from "./ts_base";
|
||||||
|
//@template-ignore
|
||||||
import { RequestObject, ResponseObject, ErrorCodes, Logging } from "./ts_service_base";
|
import { RequestObject, ResponseObject, ErrorCodes, Logging } from "./ts_service_base";
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +50,11 @@ export class ServiceProvider {
|
|||||||
let resListener = this.requests.get(msg.id);
|
let resListener = this.requests.get(msg.id);
|
||||||
if(!resListener) return; // Ignore wrong responses
|
if(!resListener) return; // Ignore wrong responses
|
||||||
if(msg.error) {
|
if(msg.error) {
|
||||||
|
if(msg.error.data && msg.error.data.$ == "verification_error") {
|
||||||
|
resListener.err(new VerificationError(msg.error.data.type, msg.error.data.field, msg.error.data.value))
|
||||||
|
} else {
|
||||||
resListener.err(new Error(msg.error.message));
|
resListener.err(new Error(msg.error.message));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
resListener.ok(msg.result);
|
resListener.ok(msg.result);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
//@template-ignore
|
//@template-ignore
|
||||||
|
import { VerificationError } from "./ts_base";
|
||||||
|
//@template-ignore
|
||||||
import { RequestObject, ResponseObject, ErrorCodes, Logging } from "./ts_service_base";
|
import { RequestObject, ResponseObject, ErrorCodes, Logging } from "./ts_service_base";
|
||||||
|
|
||||||
|
|
||||||
@ -109,6 +111,14 @@ class Session<T> {
|
|||||||
error: {
|
error: {
|
||||||
code: ErrorCodes.InternalError,
|
code: ErrorCodes.InternalError,
|
||||||
message: err.message,
|
message: err.message,
|
||||||
|
data: err instanceof VerificationError ? {
|
||||||
|
$: "verification_error",
|
||||||
|
type: err.type,
|
||||||
|
field: err.field,
|
||||||
|
value: err.value
|
||||||
|
} : {
|
||||||
|
$: "unknown_error"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as ResponseObject,
|
} as ResponseObject,
|
||||||
err
|
err
|
||||||
|
Loading…
Reference in New Issue
Block a user