@restura/core 0.1.0-alpha.8 → 0.1.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +133 -13
- package/dist/index.d.ts +133 -13
- package/dist/index.js +711 -546
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +700 -543
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -5
- package/dist/acorn-SW5GI5G7.mjs +0 -3016
- package/dist/acorn-SW5GI5G7.mjs.map +0 -1
- package/dist/angular-FYEH6QOL.mjs +0 -1547
- package/dist/angular-FYEH6QOL.mjs.map +0 -1
- package/dist/babel-V6GZHMYX.mjs +0 -6911
- package/dist/babel-V6GZHMYX.mjs.map +0 -1
- package/dist/chunk-TL4KRYOF.mjs +0 -58
- package/dist/chunk-TL4KRYOF.mjs.map +0 -1
- package/dist/estree-67ZCSSSI.mjs +0 -4396
- package/dist/estree-67ZCSSSI.mjs.map +0 -1
- package/dist/flow-SJW7PRXX.mjs +0 -26365
- package/dist/flow-SJW7PRXX.mjs.map +0 -1
- package/dist/glimmer-PF2X22V2.mjs +0 -2995
- package/dist/glimmer-PF2X22V2.mjs.map +0 -1
- package/dist/graphql-NOJ5HX7K.mjs +0 -1253
- package/dist/graphql-NOJ5HX7K.mjs.map +0 -1
- package/dist/html-ROPIWVPQ.mjs +0 -2780
- package/dist/html-ROPIWVPQ.mjs.map +0 -1
- package/dist/index.cjs +0 -34
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -3
- package/dist/markdown-PFYT4MSP.mjs +0 -3423
- package/dist/markdown-PFYT4MSP.mjs.map +0 -1
- package/dist/meriyah-5NLZXLMA.mjs +0 -2356
- package/dist/meriyah-5NLZXLMA.mjs.map +0 -1
- package/dist/postcss-ITO6IEN5.mjs +0 -5027
- package/dist/postcss-ITO6IEN5.mjs.map +0 -1
- package/dist/typescript-6IE7K56Q.mjs +0 -13392
- package/dist/typescript-6IE7K56Q.mjs.map +0 -1
- package/dist/yaml-DB2OVPLH.mjs +0 -4230
- package/dist/yaml-DB2OVPLH.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -47,12 +47,13 @@ import { format } from "logform";
|
|
|
47
47
|
// src/config.schema.ts
|
|
48
48
|
import { z } from "zod";
|
|
49
49
|
var loggerConfigSchema = z.object({
|
|
50
|
-
level: z.enum(["info", "warn", "error", "debug"]).default("info")
|
|
50
|
+
level: z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
|
|
51
51
|
});
|
|
52
52
|
var resturaConfigSchema = z.object({
|
|
53
53
|
authToken: z.string().min(1, "Missing Restura Auth Token"),
|
|
54
54
|
sendErrorStackTrace: z.boolean().default(false),
|
|
55
55
|
schemaFilePath: z.string().default(process.cwd() + "/restura.schema.json"),
|
|
56
|
+
customApiFolderPath: z.string().default(process.cwd() + "/dist/api"),
|
|
56
57
|
generatedTypesPath: z.string().default(process.cwd() + "/src/@types")
|
|
57
58
|
});
|
|
58
59
|
|
|
@@ -91,8 +92,74 @@ var logger = winston.createLogger({
|
|
|
91
92
|
]
|
|
92
93
|
});
|
|
93
94
|
|
|
95
|
+
// src/restura/errors.ts
|
|
96
|
+
var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
|
|
97
|
+
HtmlStatusCodes2[HtmlStatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
|
|
98
|
+
HtmlStatusCodes2[HtmlStatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
|
|
99
|
+
HtmlStatusCodes2[HtmlStatusCodes2["FORBIDDEN"] = 403] = "FORBIDDEN";
|
|
100
|
+
HtmlStatusCodes2[HtmlStatusCodes2["NOT_FOUND"] = 404] = "NOT_FOUND";
|
|
101
|
+
HtmlStatusCodes2[HtmlStatusCodes2["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
|
|
102
|
+
HtmlStatusCodes2[HtmlStatusCodes2["CONFLICT"] = 409] = "CONFLICT";
|
|
103
|
+
HtmlStatusCodes2[HtmlStatusCodes2["VERSION_OUT_OF_DATE"] = 418] = "VERSION_OUT_OF_DATE";
|
|
104
|
+
HtmlStatusCodes2[HtmlStatusCodes2["SERVER_ERROR"] = 500] = "SERVER_ERROR";
|
|
105
|
+
HtmlStatusCodes2[HtmlStatusCodes2["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
|
|
106
|
+
HtmlStatusCodes2[HtmlStatusCodes2["NETWORK_CONNECT_TIMEOUT"] = 599] = "NETWORK_CONNECT_TIMEOUT";
|
|
107
|
+
return HtmlStatusCodes2;
|
|
108
|
+
})(HtmlStatusCodes || {});
|
|
109
|
+
var RsError = class _RsError {
|
|
110
|
+
constructor(errCode, message) {
|
|
111
|
+
this.err = errCode;
|
|
112
|
+
this.msg = message || "";
|
|
113
|
+
this.status = _RsError.htmlStatus(errCode);
|
|
114
|
+
this.stack = new Error().stack || "";
|
|
115
|
+
}
|
|
116
|
+
static htmlStatus(code) {
|
|
117
|
+
return htmlStatusMap[code];
|
|
118
|
+
}
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
+
static isRsError(error) {
|
|
121
|
+
return error instanceof _RsError;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var htmlStatusMap = {
|
|
125
|
+
UNKNOWN_ERROR: 500 /* SERVER_ERROR */,
|
|
126
|
+
NOT_FOUND: 404 /* NOT_FOUND */,
|
|
127
|
+
EMAIL_TAKEN: 409 /* CONFLICT */,
|
|
128
|
+
FORBIDDEN: 403 /* FORBIDDEN */,
|
|
129
|
+
CONFLICT: 409 /* CONFLICT */,
|
|
130
|
+
UNAUTHORIZED: 401 /* UNAUTHORIZED */,
|
|
131
|
+
UPDATE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
132
|
+
CREATE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
133
|
+
DELETE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
134
|
+
DELETE_FAILURE: 500 /* SERVER_ERROR */,
|
|
135
|
+
BAD_REQUEST: 400 /* BAD_REQUEST */,
|
|
136
|
+
INVALID_TOKEN: 401 /* UNAUTHORIZED */,
|
|
137
|
+
INCORRECT_EMAIL_OR_PASSWORD: 401 /* UNAUTHORIZED */,
|
|
138
|
+
DUPLICATE_TOKEN: 409 /* CONFLICT */,
|
|
139
|
+
DUPLICATE_USERNAME: 409 /* CONFLICT */,
|
|
140
|
+
DUPLICATE_EMAIL: 409 /* CONFLICT */,
|
|
141
|
+
DUPLICATE: 409 /* CONFLICT */,
|
|
142
|
+
EMAIL_NOT_VERIFIED: 400 /* BAD_REQUEST */,
|
|
143
|
+
UPDATE_WITHOUT_ID: 400 /* BAD_REQUEST */,
|
|
144
|
+
CONNECTION_ERROR: 599 /* NETWORK_CONNECT_TIMEOUT */,
|
|
145
|
+
INVALID_PAYMENT: 403 /* FORBIDDEN */,
|
|
146
|
+
DECLINED_PAYMENT: 403 /* FORBIDDEN */,
|
|
147
|
+
INTEGRATION_ERROR: 500 /* SERVER_ERROR */,
|
|
148
|
+
CANNOT_RESERVE: 403 /* FORBIDDEN */,
|
|
149
|
+
REFUND_FAILURE: 403 /* FORBIDDEN */,
|
|
150
|
+
INVALID_INVOICE: 403 /* FORBIDDEN */,
|
|
151
|
+
INVALID_COUPON: 403 /* FORBIDDEN */,
|
|
152
|
+
SERVICE_UNAVAILABLE: 503 /* SERVICE_UNAVAILABLE */,
|
|
153
|
+
METHOD_UNALLOWED: 405 /* METHOD_NOT_ALLOWED */,
|
|
154
|
+
LOGIN_EXPIRED: 401 /* UNAUTHORIZED */,
|
|
155
|
+
THIRD_PARTY_ERROR: 400 /* BAD_REQUEST */,
|
|
156
|
+
ACCESS_DENIED: 403 /* FORBIDDEN */,
|
|
157
|
+
DATABASE_ERROR: 500 /* SERVER_ERROR */,
|
|
158
|
+
SCHEMA_ERROR: 500 /* SERVER_ERROR */
|
|
159
|
+
};
|
|
160
|
+
|
|
94
161
|
// src/restura/restura.ts
|
|
95
|
-
import { ObjectUtils as ObjectUtils5 } from "@redskytech/core-utils";
|
|
162
|
+
import { ObjectUtils as ObjectUtils5, StringUtils as StringUtils3 } from "@redskytech/core-utils";
|
|
96
163
|
import { config as config2 } from "@restura/internal";
|
|
97
164
|
|
|
98
165
|
// ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
|
|
@@ -147,59 +214,11 @@ import compression from "compression";
|
|
|
147
214
|
import cookieParser from "cookie-parser";
|
|
148
215
|
import { createHash } from "crypto";
|
|
149
216
|
import * as express from "express";
|
|
150
|
-
import
|
|
151
|
-
import
|
|
217
|
+
import fs3 from "fs";
|
|
218
|
+
import path3 from "path";
|
|
219
|
+
import pg2 from "pg";
|
|
152
220
|
import * as prettier3 from "prettier";
|
|
153
221
|
|
|
154
|
-
// src/restura/errors.ts
|
|
155
|
-
var RsError = class _RsError {
|
|
156
|
-
constructor(errCode, message) {
|
|
157
|
-
this.err = errCode;
|
|
158
|
-
this.msg = message || "";
|
|
159
|
-
this.status = _RsError.htmlStatus(errCode);
|
|
160
|
-
this.stack = new Error().stack || "";
|
|
161
|
-
}
|
|
162
|
-
static htmlStatus(code) {
|
|
163
|
-
return htmlStatusMap[code];
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
var htmlStatusMap = {
|
|
167
|
-
UNKNOWN_ERROR: 500 /* SERVER_ERROR */,
|
|
168
|
-
NOT_FOUND: 404 /* NOT_FOUND */,
|
|
169
|
-
EMAIL_TAKEN: 409 /* CONFLICT */,
|
|
170
|
-
FORBIDDEN: 403 /* FORBIDDEN */,
|
|
171
|
-
CONFLICT: 409 /* CONFLICT */,
|
|
172
|
-
UNAUTHORIZED: 401 /* UNAUTHORIZED */,
|
|
173
|
-
UPDATE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
174
|
-
CREATE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
175
|
-
DELETE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
176
|
-
DELETE_FAILURE: 500 /* SERVER_ERROR */,
|
|
177
|
-
BAD_REQUEST: 400 /* BAD_REQUEST */,
|
|
178
|
-
INVALID_TOKEN: 401 /* UNAUTHORIZED */,
|
|
179
|
-
INCORRECT_EMAIL_OR_PASSWORD: 401 /* UNAUTHORIZED */,
|
|
180
|
-
DUPLICATE_TOKEN: 409 /* CONFLICT */,
|
|
181
|
-
DUPLICATE_USERNAME: 409 /* CONFLICT */,
|
|
182
|
-
DUPLICATE_EMAIL: 409 /* CONFLICT */,
|
|
183
|
-
DUPLICATE: 409 /* CONFLICT */,
|
|
184
|
-
EMAIL_NOT_VERIFIED: 400 /* BAD_REQUEST */,
|
|
185
|
-
UPDATE_WITHOUT_ID: 400 /* BAD_REQUEST */,
|
|
186
|
-
CONNECTION_ERROR: 599 /* NETWORK_CONNECT_TIMEOUT */,
|
|
187
|
-
INVALID_PAYMENT: 403 /* FORBIDDEN */,
|
|
188
|
-
DECLINED_PAYMENT: 403 /* FORBIDDEN */,
|
|
189
|
-
INTEGRATION_ERROR: 500 /* SERVER_ERROR */,
|
|
190
|
-
CANNOT_RESERVE: 403 /* FORBIDDEN */,
|
|
191
|
-
REFUND_FAILURE: 403 /* FORBIDDEN */,
|
|
192
|
-
INVALID_INVOICE: 403 /* FORBIDDEN */,
|
|
193
|
-
INVALID_COUPON: 403 /* FORBIDDEN */,
|
|
194
|
-
SERVICE_UNAVAILABLE: 503 /* SERVICE_UNAVAILABLE */,
|
|
195
|
-
METHOD_UNALLOWED: 405 /* METHOD_NOT_ALLOWED */,
|
|
196
|
-
LOGIN_EXPIRED: 401 /* UNAUTHORIZED */,
|
|
197
|
-
THIRD_PARTY_ERROR: 400 /* BAD_REQUEST */,
|
|
198
|
-
ACCESS_DENIED: 403 /* FORBIDDEN */,
|
|
199
|
-
DATABASE_ERROR: 500 /* SERVER_ERROR */,
|
|
200
|
-
SCHEMA_ERROR: 500 /* SERVER_ERROR */
|
|
201
|
-
};
|
|
202
|
-
|
|
203
222
|
// src/restura/sql/SqlUtils.ts
|
|
204
223
|
var SqlUtils = class _SqlUtils {
|
|
205
224
|
static convertDatabaseTypeToTypescript(type, value) {
|
|
@@ -291,9 +310,9 @@ var ResponseValidator = class _ResponseValidator {
|
|
|
291
310
|
return { validator: "any" };
|
|
292
311
|
}
|
|
293
312
|
getTypeFromTable(selector, name) {
|
|
294
|
-
const
|
|
295
|
-
if (
|
|
296
|
-
const tableName =
|
|
313
|
+
const path4 = selector.split(".");
|
|
314
|
+
if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { validator: "any", isOptional: false };
|
|
315
|
+
const tableName = path4.length == 2 ? path4[0] : name, columnName = path4.length == 2 ? path4[1] : path4[0];
|
|
297
316
|
const table = this.database.find((t) => t.name == tableName);
|
|
298
317
|
const column = table == null ? void 0 : table.columns.find((c) => c.name == columnName);
|
|
299
318
|
if (!table || !column) return { validator: "any", isOptional: false };
|
|
@@ -509,10 +528,10 @@ var ApiTree = class _ApiTree {
|
|
|
509
528
|
return `${p.name}${optional ? "?" : ""}:${responseType}${array ? "[]" : ""}`;
|
|
510
529
|
}
|
|
511
530
|
getTypeFromTable(selector, name) {
|
|
512
|
-
const
|
|
513
|
-
if (
|
|
514
|
-
let tableName =
|
|
515
|
-
const columnName =
|
|
531
|
+
const path4 = selector.split(".");
|
|
532
|
+
if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { responseType: "any", optional: false };
|
|
533
|
+
let tableName = path4.length == 2 ? path4[0] : name;
|
|
534
|
+
const columnName = path4.length == 2 ? path4[1] : path4[0];
|
|
516
535
|
let table = this.database.find((t) => t.name == tableName);
|
|
517
536
|
if (!table && tableName.includes("_")) {
|
|
518
537
|
const tableAliasSplit = tableName.split("_");
|
|
@@ -527,8 +546,8 @@ var ApiTree = class _ApiTree {
|
|
|
527
546
|
};
|
|
528
547
|
}
|
|
529
548
|
};
|
|
530
|
-
function pathToNamespaces(
|
|
531
|
-
return
|
|
549
|
+
function pathToNamespaces(path4) {
|
|
550
|
+
return path4.split("/").map((e) => StringUtils.toPascalCasing(e)).filter((e) => e);
|
|
532
551
|
}
|
|
533
552
|
function apiGenerator(schema, schemaHash) {
|
|
534
553
|
let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
|
|
@@ -561,6 +580,92 @@ function apiGenerator(schema, schemaHash) {
|
|
|
561
580
|
}));
|
|
562
581
|
}
|
|
563
582
|
|
|
583
|
+
// src/restura/customApiFactory.ts
|
|
584
|
+
import fs from "fs";
|
|
585
|
+
import path from "path";
|
|
586
|
+
var CustomApiFactory = class {
|
|
587
|
+
constructor() {
|
|
588
|
+
this.customApis = {};
|
|
589
|
+
}
|
|
590
|
+
async loadApiFiles(baseFolderPath) {
|
|
591
|
+
const apiVersions = ["v1"];
|
|
592
|
+
for (const apiVersion of apiVersions) {
|
|
593
|
+
const apiVersionFolderPath = path.join(baseFolderPath, apiVersion);
|
|
594
|
+
if (!fs.existsSync(apiVersionFolderPath)) continue;
|
|
595
|
+
await this.addDirectory(apiVersionFolderPath, apiVersion);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
getCustomApi(customApiName) {
|
|
599
|
+
return this.customApis[customApiName];
|
|
600
|
+
}
|
|
601
|
+
async addDirectory(directoryPath, apiVersion) {
|
|
602
|
+
const entries = fs.readdirSync(directoryPath, {
|
|
603
|
+
withFileTypes: true
|
|
604
|
+
});
|
|
605
|
+
for (const entry of entries) {
|
|
606
|
+
if (entry.isFile()) {
|
|
607
|
+
if (entry.name.endsWith(`.api.${apiVersion}.js`) === false) continue;
|
|
608
|
+
try {
|
|
609
|
+
const importPath = `${path.join(directoryPath, entry.name)}`;
|
|
610
|
+
const ApiImport = await import(importPath);
|
|
611
|
+
const customApiClass = new ApiImport.default();
|
|
612
|
+
logger.info(`Registering custom API: ${ApiImport.default.name}`);
|
|
613
|
+
this.bindMethodsToInstance(customApiClass);
|
|
614
|
+
this.customApis[ApiImport.default.name] = customApiClass;
|
|
615
|
+
} catch (e) {
|
|
616
|
+
console.error(e);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
bindMethodsToInstance(instance) {
|
|
622
|
+
const proto = Object.getPrototypeOf(instance);
|
|
623
|
+
Object.getOwnPropertyNames(proto).forEach((key) => {
|
|
624
|
+
const property = instance[key];
|
|
625
|
+
if (typeof property === "function" && key !== "constructor") {
|
|
626
|
+
instance[key] = property.bind(instance);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
var customApiFactory = new CustomApiFactory();
|
|
632
|
+
var customApiFactory_default = customApiFactory;
|
|
633
|
+
|
|
634
|
+
// src/restura/customTypeValidationGenerator.ts
|
|
635
|
+
import fs2 from "fs";
|
|
636
|
+
import * as TJS from "typescript-json-schema";
|
|
637
|
+
import path2, { resolve } from "path";
|
|
638
|
+
import tmp from "tmp";
|
|
639
|
+
import * as process2 from "process";
|
|
640
|
+
function customTypeValidationGenerator(currentSchema) {
|
|
641
|
+
const schemaObject = {};
|
|
642
|
+
const customInterfaceNames = currentSchema.customTypes.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
|
|
643
|
+
if (!customInterfaceNames) return {};
|
|
644
|
+
const temporaryFile = tmp.fileSync({ mode: 420, prefix: "prefix-", postfix: ".ts" });
|
|
645
|
+
fs2.writeFileSync(temporaryFile.name, currentSchema.customTypes);
|
|
646
|
+
const compilerOptions = {
|
|
647
|
+
strictNullChecks: true,
|
|
648
|
+
skipLibCheck: true
|
|
649
|
+
};
|
|
650
|
+
const program = TJS.getProgramFromFiles(
|
|
651
|
+
[
|
|
652
|
+
resolve(temporaryFile.name),
|
|
653
|
+
// find a way to remove
|
|
654
|
+
path2.join(process2.cwd(), "src/@types/models.d.ts"),
|
|
655
|
+
path2.join(process2.cwd(), "src/@types/api.d.ts")
|
|
656
|
+
],
|
|
657
|
+
compilerOptions
|
|
658
|
+
);
|
|
659
|
+
customInterfaceNames.forEach((item) => {
|
|
660
|
+
const ddlSchema = TJS.generateSchema(program, item, {
|
|
661
|
+
required: true
|
|
662
|
+
});
|
|
663
|
+
schemaObject[item] = ddlSchema || {};
|
|
664
|
+
});
|
|
665
|
+
temporaryFile.removeCallback();
|
|
666
|
+
return schemaObject;
|
|
667
|
+
}
|
|
668
|
+
|
|
564
669
|
// src/restura/middleware/addApiResponseFunctions.ts
|
|
565
670
|
function addApiResponseFunctions(req, res, next) {
|
|
566
671
|
res.sendData = function(data, statusCode = 200) {
|
|
@@ -589,9 +694,17 @@ function addApiResponseFunctions(req, res, next) {
|
|
|
589
694
|
next();
|
|
590
695
|
}
|
|
591
696
|
|
|
592
|
-
// src/restura/
|
|
593
|
-
|
|
594
|
-
|
|
697
|
+
// src/restura/middleware/authenticateUser.ts
|
|
698
|
+
function authenticateUser(applicationAuthenticateHandler) {
|
|
699
|
+
return (req, res, next) => {
|
|
700
|
+
applicationAuthenticateHandler(req, res, (userDetails) => {
|
|
701
|
+
req.requesterDetails = __spreadValues(__spreadValues({}, req.requesterDetails), userDetails);
|
|
702
|
+
next();
|
|
703
|
+
});
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/restura/restura.schema.ts
|
|
595
708
|
import { z as z3 } from "zod";
|
|
596
709
|
|
|
597
710
|
// src/restura/types/validation.types.ts
|
|
@@ -602,264 +715,83 @@ var validatorDataSchema = z2.object({
|
|
|
602
715
|
value: validatorDataSchemeValue
|
|
603
716
|
}).strict();
|
|
604
717
|
|
|
605
|
-
// src/restura/utils/addQuotesToStrings.ts
|
|
606
|
-
function addQuotesToStrings(variable) {
|
|
607
|
-
if (typeof variable === "string") {
|
|
608
|
-
return `'${variable}'`;
|
|
609
|
-
} else if (Array.isArray(variable)) {
|
|
610
|
-
const arrayWithQuotes = variable.map(addQuotesToStrings);
|
|
611
|
-
return arrayWithQuotes;
|
|
612
|
-
} else {
|
|
613
|
-
return variable;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// src/restura/validateRequestParams.ts
|
|
618
|
-
function validateRequestParams(req, routeData, validationSchema) {
|
|
619
|
-
const requestData = getRequestData(req);
|
|
620
|
-
req.data = requestData;
|
|
621
|
-
if (routeData.request === void 0) {
|
|
622
|
-
if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
|
|
623
|
-
throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
|
|
624
|
-
if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
|
|
625
|
-
if (!routeData.requestType) throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
|
|
626
|
-
const currentInterface = validationSchema[routeData.requestType];
|
|
627
|
-
const validator = new jsonschema.Validator();
|
|
628
|
-
const executeValidation = validator.validate(req.data, currentInterface);
|
|
629
|
-
if (!executeValidation.valid) {
|
|
630
|
-
throw new RsError(
|
|
631
|
-
"BAD_REQUEST",
|
|
632
|
-
`Request custom setup has failed the following check: (${executeValidation.errors})`
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
Object.keys(req.data).forEach((requestParamName) => {
|
|
638
|
-
const requestParam = routeData.request.find((param) => param.name === requestParamName);
|
|
639
|
-
if (!requestParam) {
|
|
640
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
routeData.request.forEach((requestParam) => {
|
|
644
|
-
const requestValue = requestData[requestParam.name];
|
|
645
|
-
if (requestParam.required && requestValue === void 0)
|
|
646
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
|
|
647
|
-
else if (!requestParam.required && requestValue === void 0) return;
|
|
648
|
-
validateRequestSingleParam(requestValue, requestParam);
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
function validateRequestSingleParam(requestValue, requestParam) {
|
|
652
|
-
requestParam.validator.forEach((validator) => {
|
|
653
|
-
switch (validator.type) {
|
|
654
|
-
case "TYPE_CHECK":
|
|
655
|
-
performTypeCheck(requestValue, validator, requestParam.name);
|
|
656
|
-
break;
|
|
657
|
-
case "MIN":
|
|
658
|
-
performMinCheck(requestValue, validator, requestParam.name);
|
|
659
|
-
break;
|
|
660
|
-
case "MAX":
|
|
661
|
-
performMaxCheck(requestValue, validator, requestParam.name);
|
|
662
|
-
break;
|
|
663
|
-
case "ONE_OF":
|
|
664
|
-
performOneOfCheck(requestValue, validator, requestParam.name);
|
|
665
|
-
break;
|
|
666
|
-
}
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
function isValidType(type, requestValue) {
|
|
670
|
-
try {
|
|
671
|
-
expectValidType(type, requestValue);
|
|
672
|
-
return true;
|
|
673
|
-
} catch (e) {
|
|
674
|
-
return false;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
function expectValidType(type, requestValue) {
|
|
678
|
-
if (type === "number") {
|
|
679
|
-
return z3.number().parse(requestValue);
|
|
680
|
-
}
|
|
681
|
-
if (type === "string") {
|
|
682
|
-
return z3.string().parse(requestValue);
|
|
683
|
-
}
|
|
684
|
-
if (type === "boolean") {
|
|
685
|
-
return z3.boolean().parse(requestValue);
|
|
686
|
-
}
|
|
687
|
-
if (type === "string[]") {
|
|
688
|
-
return z3.array(z3.string()).parse(requestValue);
|
|
689
|
-
}
|
|
690
|
-
if (type === "number[]") {
|
|
691
|
-
return z3.array(z3.number()).parse(requestValue);
|
|
692
|
-
}
|
|
693
|
-
if (type === "any[]") {
|
|
694
|
-
return z3.array(z3.any()).parse(requestValue);
|
|
695
|
-
}
|
|
696
|
-
if (type === "object") {
|
|
697
|
-
return z3.object({}).strict().parse(requestValue);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
function performTypeCheck(requestValue, validator, requestParamName) {
|
|
701
|
-
if (!isValidType(validator.value, requestValue)) {
|
|
702
|
-
throw new RsError(
|
|
703
|
-
"BAD_REQUEST",
|
|
704
|
-
`Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
|
-
try {
|
|
708
|
-
validatorDataSchemeValue.parse(validator.value);
|
|
709
|
-
} catch (e) {
|
|
710
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
function expectOnlyNumbers(requestValue, validator, requestParamName) {
|
|
714
|
-
if (!isValueNumber(requestValue))
|
|
715
|
-
throw new RsError(
|
|
716
|
-
"BAD_REQUEST",
|
|
717
|
-
`Request param (${requestParamName}) with value (${requestValue}) is not of type number`
|
|
718
|
-
);
|
|
719
|
-
if (!isValueNumber(validator.value))
|
|
720
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
|
|
721
|
-
}
|
|
722
|
-
function performMinCheck(requestValue, validator, requestParamName) {
|
|
723
|
-
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
724
|
-
if (requestValue < validator.value)
|
|
725
|
-
throw new RsError(
|
|
726
|
-
"BAD_REQUEST",
|
|
727
|
-
`Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
function performMaxCheck(requestValue, validator, requestParamName) {
|
|
731
|
-
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
732
|
-
if (requestValue > validator.value)
|
|
733
|
-
throw new RsError(
|
|
734
|
-
"BAD_REQUEST",
|
|
735
|
-
`Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
|
|
736
|
-
);
|
|
737
|
-
}
|
|
738
|
-
function performOneOfCheck(requestValue, validator, requestParamName) {
|
|
739
|
-
if (!ObjectUtils2.isArrayWithData(validator.value))
|
|
740
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
|
|
741
|
-
if (typeof requestValue === "object")
|
|
742
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
|
|
743
|
-
if (!validator.value.includes(requestValue))
|
|
744
|
-
throw new RsError(
|
|
745
|
-
"BAD_REQUEST",
|
|
746
|
-
`Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
function isValueNumber(value) {
|
|
750
|
-
return !isNaN(Number(value));
|
|
751
|
-
}
|
|
752
|
-
function getRequestData(req) {
|
|
753
|
-
let body = "";
|
|
754
|
-
if (req.method === "GET" || req.method === "DELETE") {
|
|
755
|
-
body = "query";
|
|
756
|
-
} else {
|
|
757
|
-
body = "body";
|
|
758
|
-
}
|
|
759
|
-
const bodyData = req[body];
|
|
760
|
-
if (bodyData) {
|
|
761
|
-
for (const attr in bodyData) {
|
|
762
|
-
if (attr === "token") {
|
|
763
|
-
delete bodyData[attr];
|
|
764
|
-
continue;
|
|
765
|
-
}
|
|
766
|
-
if (bodyData[attr] instanceof Array) {
|
|
767
|
-
const attrList = [];
|
|
768
|
-
for (const value of bodyData[attr]) {
|
|
769
|
-
if (isNaN(Number(value))) continue;
|
|
770
|
-
attrList.push(Number(value));
|
|
771
|
-
}
|
|
772
|
-
if (ObjectUtils2.isArrayWithData(attrList)) {
|
|
773
|
-
bodyData[attr] = attrList;
|
|
774
|
-
}
|
|
775
|
-
} else {
|
|
776
|
-
bodyData[attr] = ObjectUtils2.safeParse(bodyData[attr]);
|
|
777
|
-
if (isNaN(Number(bodyData[attr]))) continue;
|
|
778
|
-
bodyData[attr] = Number(bodyData[attr]);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
return bodyData;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
718
|
// src/restura/restura.schema.ts
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
tableName: z4.string()
|
|
719
|
+
var orderBySchema = z3.object({
|
|
720
|
+
columnName: z3.string(),
|
|
721
|
+
order: z3.enum(["ASC", "DESC"]),
|
|
722
|
+
tableName: z3.string()
|
|
791
723
|
}).strict();
|
|
792
|
-
var groupBySchema =
|
|
793
|
-
columnName:
|
|
794
|
-
tableName:
|
|
724
|
+
var groupBySchema = z3.object({
|
|
725
|
+
columnName: z3.string(),
|
|
726
|
+
tableName: z3.string()
|
|
795
727
|
}).strict();
|
|
796
|
-
var whereDataSchema =
|
|
797
|
-
tableName:
|
|
798
|
-
columnName:
|
|
799
|
-
operator:
|
|
800
|
-
value:
|
|
801
|
-
custom:
|
|
802
|
-
conjunction:
|
|
728
|
+
var whereDataSchema = z3.object({
|
|
729
|
+
tableName: z3.string().optional(),
|
|
730
|
+
columnName: z3.string().optional(),
|
|
731
|
+
operator: z3.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH"]).optional(),
|
|
732
|
+
value: z3.string().or(z3.number()).optional(),
|
|
733
|
+
custom: z3.string().optional(),
|
|
734
|
+
conjunction: z3.enum(["AND", "OR"]).optional()
|
|
803
735
|
}).strict();
|
|
804
|
-
var assignmentDataSchema =
|
|
805
|
-
name:
|
|
806
|
-
value:
|
|
736
|
+
var assignmentDataSchema = z3.object({
|
|
737
|
+
name: z3.string(),
|
|
738
|
+
value: z3.string()
|
|
807
739
|
}).strict();
|
|
808
|
-
var joinDataSchema =
|
|
809
|
-
table:
|
|
810
|
-
localColumnName:
|
|
811
|
-
foreignColumnName:
|
|
812
|
-
custom:
|
|
813
|
-
type:
|
|
814
|
-
alias:
|
|
740
|
+
var joinDataSchema = z3.object({
|
|
741
|
+
table: z3.string(),
|
|
742
|
+
localColumnName: z3.string().optional(),
|
|
743
|
+
foreignColumnName: z3.string().optional(),
|
|
744
|
+
custom: z3.string().optional(),
|
|
745
|
+
type: z3.enum(["LEFT", "INNER"]),
|
|
746
|
+
alias: z3.string().optional()
|
|
815
747
|
}).strict();
|
|
816
|
-
var requestDataSchema =
|
|
817
|
-
name:
|
|
818
|
-
required:
|
|
819
|
-
validator:
|
|
748
|
+
var requestDataSchema = z3.object({
|
|
749
|
+
name: z3.string(),
|
|
750
|
+
required: z3.boolean(),
|
|
751
|
+
validator: z3.array(validatorDataSchema)
|
|
820
752
|
}).strict();
|
|
821
|
-
var responseDataSchema =
|
|
822
|
-
name:
|
|
823
|
-
selector:
|
|
824
|
-
subquery:
|
|
825
|
-
table:
|
|
826
|
-
joins:
|
|
827
|
-
where:
|
|
828
|
-
properties:
|
|
753
|
+
var responseDataSchema = z3.object({
|
|
754
|
+
name: z3.string(),
|
|
755
|
+
selector: z3.string().optional(),
|
|
756
|
+
subquery: z3.object({
|
|
757
|
+
table: z3.string(),
|
|
758
|
+
joins: z3.array(joinDataSchema),
|
|
759
|
+
where: z3.array(whereDataSchema),
|
|
760
|
+
properties: z3.array(z3.lazy(() => responseDataSchema)),
|
|
829
761
|
// Explicit type for the lazy schema
|
|
830
762
|
groupBy: groupBySchema.optional(),
|
|
831
763
|
orderBy: orderBySchema.optional()
|
|
832
764
|
}).optional()
|
|
833
765
|
}).strict();
|
|
834
|
-
var routeDataBaseSchema =
|
|
835
|
-
method:
|
|
836
|
-
name:
|
|
837
|
-
description:
|
|
838
|
-
path:
|
|
839
|
-
roles:
|
|
766
|
+
var routeDataBaseSchema = z3.object({
|
|
767
|
+
method: z3.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
768
|
+
name: z3.string(),
|
|
769
|
+
description: z3.string(),
|
|
770
|
+
path: z3.string(),
|
|
771
|
+
roles: z3.array(z3.string())
|
|
840
772
|
}).strict();
|
|
841
773
|
var standardRouteSchema = routeDataBaseSchema.extend({
|
|
842
|
-
type:
|
|
843
|
-
table:
|
|
844
|
-
joins:
|
|
845
|
-
assignments:
|
|
846
|
-
where:
|
|
847
|
-
request:
|
|
848
|
-
response:
|
|
774
|
+
type: z3.enum(["ONE", "ARRAY", "PAGED"]),
|
|
775
|
+
table: z3.string(),
|
|
776
|
+
joins: z3.array(joinDataSchema),
|
|
777
|
+
assignments: z3.array(assignmentDataSchema),
|
|
778
|
+
where: z3.array(whereDataSchema),
|
|
779
|
+
request: z3.array(requestDataSchema),
|
|
780
|
+
response: z3.array(responseDataSchema),
|
|
849
781
|
groupBy: groupBySchema.optional(),
|
|
850
782
|
orderBy: orderBySchema.optional()
|
|
851
783
|
}).strict();
|
|
852
784
|
var customRouteSchema = routeDataBaseSchema.extend({
|
|
853
|
-
type:
|
|
854
|
-
responseType:
|
|
855
|
-
requestType:
|
|
856
|
-
request:
|
|
857
|
-
table:
|
|
858
|
-
joins:
|
|
859
|
-
assignments:
|
|
860
|
-
fileUploadType:
|
|
785
|
+
type: z3.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
|
|
786
|
+
responseType: z3.union([z3.string(), z3.enum(["string", "number", "boolean"])]),
|
|
787
|
+
requestType: z3.string().optional(),
|
|
788
|
+
request: z3.array(requestDataSchema).optional(),
|
|
789
|
+
table: z3.undefined(),
|
|
790
|
+
joins: z3.undefined(),
|
|
791
|
+
assignments: z3.undefined(),
|
|
792
|
+
fileUploadType: z3.enum(["SINGLE", "MULTIPLE"]).optional()
|
|
861
793
|
}).strict();
|
|
862
|
-
var postgresColumnNumericTypesSchema =
|
|
794
|
+
var postgresColumnNumericTypesSchema = z3.enum([
|
|
863
795
|
"SMALLINT",
|
|
864
796
|
// 2 bytes, -32,768 to 32,767
|
|
865
797
|
"INTEGER",
|
|
@@ -879,7 +811,7 @@ var postgresColumnNumericTypesSchema = z4.enum([
|
|
|
879
811
|
"BIGSERIAL"
|
|
880
812
|
// auto-incrementing big integer
|
|
881
813
|
]);
|
|
882
|
-
var postgresColumnStringTypesSchema =
|
|
814
|
+
var postgresColumnStringTypesSchema = z3.enum([
|
|
883
815
|
"CHAR",
|
|
884
816
|
// fixed-length, blank-padded
|
|
885
817
|
"VARCHAR",
|
|
@@ -889,7 +821,7 @@ var postgresColumnStringTypesSchema = z4.enum([
|
|
|
889
821
|
"BYTEA"
|
|
890
822
|
// binary data
|
|
891
823
|
]);
|
|
892
|
-
var postgresColumnDateTypesSchema =
|
|
824
|
+
var postgresColumnDateTypesSchema = z3.enum([
|
|
893
825
|
"DATE",
|
|
894
826
|
// calendar date (year, month, day)
|
|
895
827
|
"TIMESTAMP",
|
|
@@ -901,7 +833,7 @@ var postgresColumnDateTypesSchema = z4.enum([
|
|
|
901
833
|
"INTERVAL"
|
|
902
834
|
// time span
|
|
903
835
|
]);
|
|
904
|
-
var mariaDbColumnNumericTypesSchema =
|
|
836
|
+
var mariaDbColumnNumericTypesSchema = z3.enum([
|
|
905
837
|
"BOOLEAN",
|
|
906
838
|
// 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
|
|
907
839
|
"TINYINT",
|
|
@@ -921,7 +853,7 @@ var mariaDbColumnNumericTypesSchema = z4.enum([
|
|
|
921
853
|
"DOUBLE"
|
|
922
854
|
// 8 bytes Stored in 64-bit IEEE-754 floating point format. As such, the number of significant digits is about 15 and the range of values is approximately +/-1e308.
|
|
923
855
|
]);
|
|
924
|
-
var mariaDbColumnStringTypesSchema =
|
|
856
|
+
var mariaDbColumnStringTypesSchema = z3.enum([
|
|
925
857
|
"CHAR",
|
|
926
858
|
// 1, 2, 4, or 8 bytes Holds letters and special characters of fixed length. Max length is 255. Default and minimum size is 1 byte.
|
|
927
859
|
"VARCHAR",
|
|
@@ -947,7 +879,7 @@ var mariaDbColumnStringTypesSchema = z4.enum([
|
|
|
947
879
|
"ENUM"
|
|
948
880
|
// Enum type
|
|
949
881
|
]);
|
|
950
|
-
var mariaDbColumnDateTypesSchema =
|
|
882
|
+
var mariaDbColumnDateTypesSchema = z3.enum([
|
|
951
883
|
"DATE",
|
|
952
884
|
// 4-bytes Date has year, month, and day.
|
|
953
885
|
"DATETIME",
|
|
@@ -957,9 +889,9 @@ var mariaDbColumnDateTypesSchema = z4.enum([
|
|
|
957
889
|
"TIMESTAMP"
|
|
958
890
|
// 4-bytes Values are stored as the number of seconds since 1970-01-01 00:00:00 UTC, and optionally microseconds.
|
|
959
891
|
]);
|
|
960
|
-
var columnDataSchema =
|
|
961
|
-
name:
|
|
962
|
-
type:
|
|
892
|
+
var columnDataSchema = z3.object({
|
|
893
|
+
name: z3.string(),
|
|
894
|
+
type: z3.union([
|
|
963
895
|
postgresColumnNumericTypesSchema,
|
|
964
896
|
postgresColumnStringTypesSchema,
|
|
965
897
|
postgresColumnDateTypesSchema,
|
|
@@ -967,24 +899,24 @@ var columnDataSchema = z4.object({
|
|
|
967
899
|
mariaDbColumnStringTypesSchema,
|
|
968
900
|
mariaDbColumnDateTypesSchema
|
|
969
901
|
]),
|
|
970
|
-
isNullable:
|
|
971
|
-
roles:
|
|
972
|
-
comment:
|
|
973
|
-
default:
|
|
974
|
-
value:
|
|
975
|
-
isPrimary:
|
|
976
|
-
isUnique:
|
|
977
|
-
hasAutoIncrement:
|
|
978
|
-
length:
|
|
902
|
+
isNullable: z3.boolean(),
|
|
903
|
+
roles: z3.array(z3.string()),
|
|
904
|
+
comment: z3.string().optional(),
|
|
905
|
+
default: z3.string().optional(),
|
|
906
|
+
value: z3.string().optional(),
|
|
907
|
+
isPrimary: z3.boolean().optional(),
|
|
908
|
+
isUnique: z3.boolean().optional(),
|
|
909
|
+
hasAutoIncrement: z3.boolean().optional(),
|
|
910
|
+
length: z3.number().optional()
|
|
979
911
|
}).strict();
|
|
980
|
-
var indexDataSchema =
|
|
981
|
-
name:
|
|
982
|
-
columns:
|
|
983
|
-
isUnique:
|
|
984
|
-
isPrimaryKey:
|
|
985
|
-
order:
|
|
912
|
+
var indexDataSchema = z3.object({
|
|
913
|
+
name: z3.string(),
|
|
914
|
+
columns: z3.array(z3.string()),
|
|
915
|
+
isUnique: z3.boolean(),
|
|
916
|
+
isPrimaryKey: z3.boolean(),
|
|
917
|
+
order: z3.enum(["ASC", "DESC"])
|
|
986
918
|
}).strict();
|
|
987
|
-
var foreignKeyActionsSchema =
|
|
919
|
+
var foreignKeyActionsSchema = z3.enum([
|
|
988
920
|
"CASCADE",
|
|
989
921
|
// CASCADE action for foreign keys
|
|
990
922
|
"SET NULL",
|
|
@@ -996,48 +928,229 @@ var foreignKeyActionsSchema = z4.enum([
|
|
|
996
928
|
"SET DEFAULT"
|
|
997
929
|
// SET DEFAULT action for foreign keys
|
|
998
930
|
]);
|
|
999
|
-
var foreignKeyDataSchema =
|
|
1000
|
-
name:
|
|
1001
|
-
column:
|
|
1002
|
-
refTable:
|
|
1003
|
-
refColumn:
|
|
931
|
+
var foreignKeyDataSchema = z3.object({
|
|
932
|
+
name: z3.string(),
|
|
933
|
+
column: z3.string(),
|
|
934
|
+
refTable: z3.string(),
|
|
935
|
+
refColumn: z3.string(),
|
|
1004
936
|
onDelete: foreignKeyActionsSchema,
|
|
1005
937
|
onUpdate: foreignKeyActionsSchema
|
|
1006
938
|
}).strict();
|
|
1007
|
-
var checkConstraintDataSchema =
|
|
1008
|
-
name:
|
|
1009
|
-
check:
|
|
939
|
+
var checkConstraintDataSchema = z3.object({
|
|
940
|
+
name: z3.string(),
|
|
941
|
+
check: z3.string()
|
|
1010
942
|
}).strict();
|
|
1011
|
-
var tableDataSchema =
|
|
1012
|
-
name:
|
|
1013
|
-
columns:
|
|
1014
|
-
indexes:
|
|
1015
|
-
foreignKeys:
|
|
1016
|
-
checkConstraints:
|
|
1017
|
-
roles:
|
|
943
|
+
var tableDataSchema = z3.object({
|
|
944
|
+
name: z3.string(),
|
|
945
|
+
columns: z3.array(columnDataSchema),
|
|
946
|
+
indexes: z3.array(indexDataSchema),
|
|
947
|
+
foreignKeys: z3.array(foreignKeyDataSchema),
|
|
948
|
+
checkConstraints: z3.array(checkConstraintDataSchema),
|
|
949
|
+
roles: z3.array(z3.string())
|
|
1018
950
|
}).strict();
|
|
1019
|
-
var endpointDataSchema =
|
|
1020
|
-
name:
|
|
1021
|
-
description:
|
|
1022
|
-
baseUrl:
|
|
1023
|
-
routes:
|
|
951
|
+
var endpointDataSchema = z3.object({
|
|
952
|
+
name: z3.string(),
|
|
953
|
+
description: z3.string(),
|
|
954
|
+
baseUrl: z3.string(),
|
|
955
|
+
routes: z3.array(z3.union([standardRouteSchema, customRouteSchema]))
|
|
1024
956
|
}).strict();
|
|
1025
|
-
var resturaZodSchema =
|
|
1026
|
-
database:
|
|
1027
|
-
endpoints:
|
|
1028
|
-
globalParams:
|
|
1029
|
-
roles:
|
|
1030
|
-
customTypes:
|
|
957
|
+
var resturaZodSchema = z3.object({
|
|
958
|
+
database: z3.array(tableDataSchema),
|
|
959
|
+
endpoints: z3.array(endpointDataSchema),
|
|
960
|
+
globalParams: z3.array(z3.string()),
|
|
961
|
+
roles: z3.array(z3.string()),
|
|
962
|
+
customTypes: z3.string()
|
|
1031
963
|
}).strict();
|
|
1032
964
|
async function isSchemaValid(schemaToCheck) {
|
|
1033
965
|
try {
|
|
1034
966
|
resturaZodSchema.parse(schemaToCheck);
|
|
1035
967
|
return true;
|
|
1036
|
-
} catch (error) {
|
|
1037
|
-
logger.error(error);
|
|
968
|
+
} catch (error) {
|
|
969
|
+
logger.error(error);
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// src/restura/validateRequestParams.ts
|
|
975
|
+
import { ObjectUtils as ObjectUtils2 } from "@redskytech/core-utils";
|
|
976
|
+
import jsonschema from "jsonschema";
|
|
977
|
+
import { z as z4 } from "zod";
|
|
978
|
+
|
|
979
|
+
// src/restura/utils/addQuotesToStrings.ts
|
|
980
|
+
function addQuotesToStrings(variable) {
|
|
981
|
+
if (typeof variable === "string") {
|
|
982
|
+
return `'${variable}'`;
|
|
983
|
+
} else if (Array.isArray(variable)) {
|
|
984
|
+
const arrayWithQuotes = variable.map(addQuotesToStrings);
|
|
985
|
+
return arrayWithQuotes;
|
|
986
|
+
} else {
|
|
987
|
+
return variable;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// src/restura/validateRequestParams.ts
|
|
992
|
+
function validateRequestParams(req, routeData, validationSchema) {
|
|
993
|
+
const requestData = getRequestData(req);
|
|
994
|
+
req.data = requestData;
|
|
995
|
+
if (routeData.request === void 0) {
|
|
996
|
+
if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
|
|
997
|
+
throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
|
|
998
|
+
if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
|
|
999
|
+
if (!routeData.requestType) throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
|
|
1000
|
+
const currentInterface = validationSchema[routeData.requestType];
|
|
1001
|
+
const validator = new jsonschema.Validator();
|
|
1002
|
+
const executeValidation = validator.validate(req.data, currentInterface);
|
|
1003
|
+
if (!executeValidation.valid) {
|
|
1004
|
+
throw new RsError(
|
|
1005
|
+
"BAD_REQUEST",
|
|
1006
|
+
`Request custom setup has failed the following check: (${executeValidation.errors})`
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
Object.keys(req.data).forEach((requestParamName) => {
|
|
1012
|
+
const requestParam = routeData.request.find((param) => param.name === requestParamName);
|
|
1013
|
+
if (!requestParam) {
|
|
1014
|
+
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
routeData.request.forEach((requestParam) => {
|
|
1018
|
+
const requestValue = requestData[requestParam.name];
|
|
1019
|
+
if (requestParam.required && requestValue === void 0)
|
|
1020
|
+
throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
|
|
1021
|
+
else if (!requestParam.required && requestValue === void 0) return;
|
|
1022
|
+
validateRequestSingleParam(requestValue, requestParam);
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
function validateRequestSingleParam(requestValue, requestParam) {
|
|
1026
|
+
requestParam.validator.forEach((validator) => {
|
|
1027
|
+
switch (validator.type) {
|
|
1028
|
+
case "TYPE_CHECK":
|
|
1029
|
+
performTypeCheck(requestValue, validator, requestParam.name);
|
|
1030
|
+
break;
|
|
1031
|
+
case "MIN":
|
|
1032
|
+
performMinCheck(requestValue, validator, requestParam.name);
|
|
1033
|
+
break;
|
|
1034
|
+
case "MAX":
|
|
1035
|
+
performMaxCheck(requestValue, validator, requestParam.name);
|
|
1036
|
+
break;
|
|
1037
|
+
case "ONE_OF":
|
|
1038
|
+
performOneOfCheck(requestValue, validator, requestParam.name);
|
|
1039
|
+
break;
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
function isValidType(type, requestValue) {
|
|
1044
|
+
try {
|
|
1045
|
+
expectValidType(type, requestValue);
|
|
1046
|
+
return true;
|
|
1047
|
+
} catch (e) {
|
|
1038
1048
|
return false;
|
|
1039
1049
|
}
|
|
1040
1050
|
}
|
|
1051
|
+
function expectValidType(type, requestValue) {
|
|
1052
|
+
if (type === "number") {
|
|
1053
|
+
return z4.number().parse(requestValue);
|
|
1054
|
+
}
|
|
1055
|
+
if (type === "string") {
|
|
1056
|
+
return z4.string().parse(requestValue);
|
|
1057
|
+
}
|
|
1058
|
+
if (type === "boolean") {
|
|
1059
|
+
return z4.boolean().parse(requestValue);
|
|
1060
|
+
}
|
|
1061
|
+
if (type === "string[]") {
|
|
1062
|
+
return z4.array(z4.string()).parse(requestValue);
|
|
1063
|
+
}
|
|
1064
|
+
if (type === "number[]") {
|
|
1065
|
+
return z4.array(z4.number()).parse(requestValue);
|
|
1066
|
+
}
|
|
1067
|
+
if (type === "any[]") {
|
|
1068
|
+
return z4.array(z4.any()).parse(requestValue);
|
|
1069
|
+
}
|
|
1070
|
+
if (type === "object") {
|
|
1071
|
+
return z4.object({}).strict().parse(requestValue);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
function performTypeCheck(requestValue, validator, requestParamName) {
|
|
1075
|
+
if (!isValidType(validator.value, requestValue)) {
|
|
1076
|
+
throw new RsError(
|
|
1077
|
+
"BAD_REQUEST",
|
|
1078
|
+
`Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
try {
|
|
1082
|
+
validatorDataSchemeValue.parse(validator.value);
|
|
1083
|
+
} catch (e) {
|
|
1084
|
+
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function expectOnlyNumbers(requestValue, validator, requestParamName) {
|
|
1088
|
+
if (!isValueNumber(requestValue))
|
|
1089
|
+
throw new RsError(
|
|
1090
|
+
"BAD_REQUEST",
|
|
1091
|
+
`Request param (${requestParamName}) with value (${requestValue}) is not of type number`
|
|
1092
|
+
);
|
|
1093
|
+
if (!isValueNumber(validator.value))
|
|
1094
|
+
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
|
|
1095
|
+
}
|
|
1096
|
+
function performMinCheck(requestValue, validator, requestParamName) {
|
|
1097
|
+
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
1098
|
+
if (requestValue < validator.value)
|
|
1099
|
+
throw new RsError(
|
|
1100
|
+
"BAD_REQUEST",
|
|
1101
|
+
`Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
function performMaxCheck(requestValue, validator, requestParamName) {
|
|
1105
|
+
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
1106
|
+
if (requestValue > validator.value)
|
|
1107
|
+
throw new RsError(
|
|
1108
|
+
"BAD_REQUEST",
|
|
1109
|
+
`Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
function performOneOfCheck(requestValue, validator, requestParamName) {
|
|
1113
|
+
if (!ObjectUtils2.isArrayWithData(validator.value))
|
|
1114
|
+
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
|
|
1115
|
+
if (typeof requestValue === "object")
|
|
1116
|
+
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
|
|
1117
|
+
if (!validator.value.includes(requestValue))
|
|
1118
|
+
throw new RsError(
|
|
1119
|
+
"BAD_REQUEST",
|
|
1120
|
+
`Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
function isValueNumber(value) {
|
|
1124
|
+
return !isNaN(Number(value));
|
|
1125
|
+
}
|
|
1126
|
+
function getRequestData(req) {
|
|
1127
|
+
let body = "";
|
|
1128
|
+
if (req.method === "GET" || req.method === "DELETE") {
|
|
1129
|
+
body = "query";
|
|
1130
|
+
} else {
|
|
1131
|
+
body = "body";
|
|
1132
|
+
}
|
|
1133
|
+
const bodyData = req[body];
|
|
1134
|
+
if (bodyData && body === "query") {
|
|
1135
|
+
for (const attr in bodyData) {
|
|
1136
|
+
if (bodyData[attr] instanceof Array) {
|
|
1137
|
+
const attrList = [];
|
|
1138
|
+
for (const value of bodyData[attr]) {
|
|
1139
|
+
if (isNaN(Number(value))) continue;
|
|
1140
|
+
attrList.push(Number(value));
|
|
1141
|
+
}
|
|
1142
|
+
if (ObjectUtils2.isArrayWithData(attrList)) {
|
|
1143
|
+
bodyData[attr] = attrList;
|
|
1144
|
+
}
|
|
1145
|
+
} else {
|
|
1146
|
+
bodyData[attr] = ObjectUtils2.safeParse(bodyData[attr]);
|
|
1147
|
+
if (isNaN(Number(bodyData[attr]))) continue;
|
|
1148
|
+
bodyData[attr] = Number(bodyData[attr]);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return bodyData;
|
|
1153
|
+
}
|
|
1041
1154
|
|
|
1042
1155
|
// src/restura/middleware/schemaValidation.ts
|
|
1043
1156
|
async function schemaValidation(req, res, next) {
|
|
@@ -1086,55 +1199,52 @@ function convertTable(table) {
|
|
|
1086
1199
|
return modelString;
|
|
1087
1200
|
}
|
|
1088
1201
|
|
|
1089
|
-
// src/restura/
|
|
1090
|
-
|
|
1091
|
-
return (req, res, next) => {
|
|
1092
|
-
applicationAuthenticateHandler(
|
|
1093
|
-
req,
|
|
1094
|
-
(userDetails) => {
|
|
1095
|
-
req.requesterDetails = __spreadValues(__spreadValues({}, req.requesterDetails), userDetails);
|
|
1096
|
-
next();
|
|
1097
|
-
},
|
|
1098
|
-
(errorMessage) => {
|
|
1099
|
-
res.sendError("UNAUTHORIZED", errorMessage);
|
|
1100
|
-
}
|
|
1101
|
-
);
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1202
|
+
// src/restura/sql/PsqlEngine.ts
|
|
1203
|
+
import { ObjectUtils as ObjectUtils4 } from "@redskytech/core-utils";
|
|
1104
1204
|
|
|
1105
|
-
// src/restura/
|
|
1106
|
-
import
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
function
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
const
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1205
|
+
// src/restura/sql/PsqlUtils.ts
|
|
1206
|
+
import format2 from "pg-format";
|
|
1207
|
+
function escapeColumnName(columnName) {
|
|
1208
|
+
if (columnName === void 0) return "";
|
|
1209
|
+
return `"${columnName.replace(/"/g, "")}"`.replace(".", '"."');
|
|
1210
|
+
}
|
|
1211
|
+
function questionMarksToOrderedParams(query) {
|
|
1212
|
+
let count = 1;
|
|
1213
|
+
return query.replace(/'\?'|\?/g, () => `$${count++}`);
|
|
1214
|
+
}
|
|
1215
|
+
function insertObjectQuery(table, obj) {
|
|
1216
|
+
const keys = Object.keys(obj);
|
|
1217
|
+
const params = Object.values(obj);
|
|
1218
|
+
const columns = keys.map((column) => escapeColumnName(column)).join(", ");
|
|
1219
|
+
const values = params.map((value) => SQL`${value}`).join(", ");
|
|
1220
|
+
const query = `INSERT INTO "${table}" (${columns})
|
|
1221
|
+
VALUES (${values})
|
|
1222
|
+
RETURNING *`;
|
|
1223
|
+
return query;
|
|
1224
|
+
}
|
|
1225
|
+
function updateObjectQuery(table, obj, whereStatement) {
|
|
1226
|
+
const setArray = [];
|
|
1227
|
+
for (const i in obj) {
|
|
1228
|
+
setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
|
|
1229
|
+
}
|
|
1230
|
+
return `UPDATE ${escapeColumnName(table)}
|
|
1231
|
+
SET ${setArray.join(", ")} ${whereStatement}
|
|
1232
|
+
RETURNING *`;
|
|
1233
|
+
}
|
|
1234
|
+
function isValueNumber2(value) {
|
|
1235
|
+
return !isNaN(Number(value));
|
|
1236
|
+
}
|
|
1237
|
+
function SQL(strings, ...values) {
|
|
1238
|
+
let query = strings[0];
|
|
1239
|
+
values.forEach((value, index) => {
|
|
1240
|
+
if (isValueNumber2(value)) {
|
|
1241
|
+
query += value;
|
|
1242
|
+
} else {
|
|
1243
|
+
query += format2.literal(value);
|
|
1244
|
+
}
|
|
1245
|
+
query += strings[index + 1];
|
|
1135
1246
|
});
|
|
1136
|
-
|
|
1137
|
-
return schemaObject;
|
|
1247
|
+
return query;
|
|
1138
1248
|
}
|
|
1139
1249
|
|
|
1140
1250
|
// src/restura/sql/SqlEngine.ts
|
|
@@ -1226,7 +1336,10 @@ var SqlEngine = class {
|
|
|
1226
1336
|
param = param.replace("#", "");
|
|
1227
1337
|
const globalParamValue = req.requesterDetails[param];
|
|
1228
1338
|
if (!globalParamValue)
|
|
1229
|
-
throw new RsError(
|
|
1339
|
+
throw new RsError(
|
|
1340
|
+
"SCHEMA_ERROR",
|
|
1341
|
+
`Invalid global keyword clause in route (${routeData.path}) when looking for (#${param})`
|
|
1342
|
+
);
|
|
1230
1343
|
sqlParams.push(globalParamValue);
|
|
1231
1344
|
});
|
|
1232
1345
|
return value.replace(new RegExp(/#[a-zA-Z][a-zA-Z0-9_]+/g), "?");
|
|
@@ -1235,7 +1348,7 @@ var SqlEngine = class {
|
|
|
1235
1348
|
}
|
|
1236
1349
|
};
|
|
1237
1350
|
|
|
1238
|
-
// src/restura/sql/
|
|
1351
|
+
// src/restura/sql/filterPsqlParser.ts
|
|
1239
1352
|
import peg from "pegjs";
|
|
1240
1353
|
var filterSqlGrammar = `
|
|
1241
1354
|
start = expressionList
|
|
@@ -1247,7 +1360,7 @@ expressionList =
|
|
|
1247
1360
|
|
|
1248
1361
|
expression =
|
|
1249
1362
|
negate:negate?"(" "column:" column:column ","? value:value? ","? type:type? ")"
|
|
1250
|
-
{return \`\${negate? "!" : ""}(\${type? type(column, value) : \`\${column} = \${
|
|
1363
|
+
{return \`\${negate? "!" : ""}(\${type? type(column, value) : \`\${column} = \${format.literal(value)}\`})\`;}
|
|
1251
1364
|
/
|
|
1252
1365
|
negate:negate?"("expression:expressionList")" { return \`\${negate? "!" : ""}(\${expression})\`; }
|
|
1253
1366
|
|
|
@@ -1255,80 +1368,33 @@ negate = "!"
|
|
|
1255
1368
|
|
|
1256
1369
|
operator = "and"i / "or"i
|
|
1257
1370
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1371
|
+
|
|
1372
|
+
column = left:text "." right:text { return \`\${format.ident(left)}.\${format.ident(right)}\`; }
|
|
1373
|
+
/
|
|
1374
|
+
text:text { return format.ident(text); }
|
|
1375
|
+
|
|
1262
1376
|
|
|
1263
|
-
text = text:[
|
|
1377
|
+
text = text:[a-z0-9-_:@]i+ { return text.join("");}
|
|
1264
1378
|
|
|
1265
1379
|
type = "type:" type:typeString { return type; }
|
|
1266
|
-
typeString = text:"startsWith" { return function(column, value) { return \`\${column}
|
|
1267
|
-
text:"endsWith" { return function(column, value) { return \`\${column}
|
|
1268
|
-
text:"contains" { return function(column, value) { return \`\${column}
|
|
1269
|
-
text:"exact" { return function(column, value) { return \`\${column} = '\${
|
|
1270
|
-
text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${
|
|
1271
|
-
text:"greaterThan" { return function(column, value) { return \`\${column} > '\${
|
|
1272
|
-
text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${
|
|
1273
|
-
text:"lessThan" { return function(column, value) { return \`\${column} < '\${
|
|
1380
|
+
typeString = text:"startsWith" { return function(column, value) { return \`\${column} ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } } /
|
|
1381
|
+
text:"endsWith" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1382
|
+
text:"contains" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } } /
|
|
1383
|
+
text:"exact" { return function(column, value) { return \`\${column} = '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1384
|
+
text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1385
|
+
text:"greaterThan" { return function(column, value) { return \`\${column} > '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1386
|
+
text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1387
|
+
text:"lessThan" { return function(column, value) { return \`\${column} < '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1274
1388
|
text:"isNull" { return function(column, value) { return \`isNull(\${column})\`; } }
|
|
1275
1389
|
|
|
1276
1390
|
value = "value:" value:text { return value; }
|
|
1277
1391
|
|
|
1278
1392
|
`;
|
|
1279
|
-
var
|
|
1280
|
-
format: "commonjs"
|
|
1281
|
-
|
|
1393
|
+
var filterPsqlParser = peg.generate(filterSqlGrammar, {
|
|
1394
|
+
format: "commonjs",
|
|
1395
|
+
dependencies: { format: "pg-format" }
|
|
1282
1396
|
});
|
|
1283
|
-
var
|
|
1284
|
-
|
|
1285
|
-
// src/restura/sql/PsqlEngine.ts
|
|
1286
|
-
import { ObjectUtils as ObjectUtils4 } from "@redskytech/core-utils";
|
|
1287
|
-
|
|
1288
|
-
// src/restura/sql/PsqlUtils.ts
|
|
1289
|
-
import format2 from "pg-format";
|
|
1290
|
-
function escapeColumnName(columnName) {
|
|
1291
|
-
if (columnName === void 0) return "";
|
|
1292
|
-
return `"${columnName.replace(/"/g, "")}"`.replace(".", '"."');
|
|
1293
|
-
}
|
|
1294
|
-
function questionMarksToOrderedParams(query) {
|
|
1295
|
-
let count = 1;
|
|
1296
|
-
return query.replace(/'\?'/g, () => `$${count++}`);
|
|
1297
|
-
}
|
|
1298
|
-
function insertObjectQuery(table, obj) {
|
|
1299
|
-
const keys = Object.keys(obj);
|
|
1300
|
-
const params = Object.values(obj);
|
|
1301
|
-
const columns = keys.map((column) => escapeColumnName(column)).join(", ");
|
|
1302
|
-
const values = params.map((value) => SQL`${value}`).join(", ");
|
|
1303
|
-
const query = `INSERT INTO "${table}" (${columns})
|
|
1304
|
-
VALUES (${values})
|
|
1305
|
-
RETURNING *`;
|
|
1306
|
-
return query;
|
|
1307
|
-
}
|
|
1308
|
-
function updateObjectQuery(table, obj, whereStatement) {
|
|
1309
|
-
const setArray = [];
|
|
1310
|
-
for (const i in obj) {
|
|
1311
|
-
setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
|
|
1312
|
-
}
|
|
1313
|
-
return `UPDATE ${escapeColumnName(table)}
|
|
1314
|
-
SET ${setArray.join(", ")} ${whereStatement}
|
|
1315
|
-
RETURNING *`;
|
|
1316
|
-
}
|
|
1317
|
-
function isValueNumber2(value) {
|
|
1318
|
-
return !isNaN(Number(value));
|
|
1319
|
-
}
|
|
1320
|
-
function SQL(strings, ...values) {
|
|
1321
|
-
let query = strings[0];
|
|
1322
|
-
values.forEach((value, index) => {
|
|
1323
|
-
if (isValueNumber2(value)) {
|
|
1324
|
-
query += value;
|
|
1325
|
-
} else {
|
|
1326
|
-
query += format2.literal(value);
|
|
1327
|
-
}
|
|
1328
|
-
query += strings[index + 1];
|
|
1329
|
-
});
|
|
1330
|
-
return query;
|
|
1331
|
-
}
|
|
1397
|
+
var filterPsqlParser_default = filterPsqlParser;
|
|
1332
1398
|
|
|
1333
1399
|
// src/restura/sql/PsqlEngine.ts
|
|
1334
1400
|
var PsqlEngine = class extends SqlEngine {
|
|
@@ -1345,8 +1411,45 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1345
1411
|
return "";
|
|
1346
1412
|
}
|
|
1347
1413
|
createNestedSelect(req, schema, item, routeData, userRole, sqlParams) {
|
|
1348
|
-
|
|
1349
|
-
|
|
1414
|
+
if (!item.subquery) return "";
|
|
1415
|
+
if (!ObjectUtils4.isArrayWithData(
|
|
1416
|
+
item.subquery.properties.filter((nestedItem) => {
|
|
1417
|
+
return this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
|
|
1418
|
+
...routeData.joins,
|
|
1419
|
+
...item.subquery.joins
|
|
1420
|
+
]);
|
|
1421
|
+
})
|
|
1422
|
+
)) {
|
|
1423
|
+
return "'[]'";
|
|
1424
|
+
}
|
|
1425
|
+
return `COALESCE((
|
|
1426
|
+
SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
1427
|
+
${item.subquery.properties.map((nestedItem) => {
|
|
1428
|
+
if (!this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
|
|
1429
|
+
...routeData.joins,
|
|
1430
|
+
...item.subquery.joins
|
|
1431
|
+
])) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
if (nestedItem.subquery) {
|
|
1435
|
+
return `"${nestedItem.name}", ${this.createNestedSelect(
|
|
1436
|
+
// recursion
|
|
1437
|
+
req,
|
|
1438
|
+
schema,
|
|
1439
|
+
nestedItem,
|
|
1440
|
+
routeData,
|
|
1441
|
+
userRole,
|
|
1442
|
+
sqlParams
|
|
1443
|
+
)}`;
|
|
1444
|
+
}
|
|
1445
|
+
return `'${nestedItem.name}', ${escapeColumnName(nestedItem.selector)}`;
|
|
1446
|
+
}).filter(Boolean).join(",")}
|
|
1447
|
+
))
|
|
1448
|
+
FROM
|
|
1449
|
+
"${item.subquery.table}"
|
|
1450
|
+
${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, userRole, sqlParams)}
|
|
1451
|
+
${this.generateWhereClause(req, item.subquery.where, routeData, sqlParams)}
|
|
1452
|
+
), '[]')`;
|
|
1350
1453
|
}
|
|
1351
1454
|
async executeCreateRequest(req, routeData, schema) {
|
|
1352
1455
|
const sqlParams = [];
|
|
@@ -1355,7 +1458,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1355
1458
|
parameterObj[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
|
|
1356
1459
|
});
|
|
1357
1460
|
const query = insertObjectQuery(routeData.table, __spreadValues(__spreadValues({}, req.data), parameterObj));
|
|
1358
|
-
const createdItem = await this.psqlConnectionPool.queryOne(query, sqlParams);
|
|
1461
|
+
const createdItem = await this.psqlConnectionPool.queryOne(query, sqlParams, req.requesterDetails);
|
|
1359
1462
|
const insertId = createdItem == null ? void 0 : createdItem.id;
|
|
1360
1463
|
const whereData = [
|
|
1361
1464
|
{
|
|
@@ -1405,12 +1508,14 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1405
1508
|
if (routeData.type === "ONE") {
|
|
1406
1509
|
return this.psqlConnectionPool.queryOne(
|
|
1407
1510
|
`${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
|
|
1408
|
-
sqlParams
|
|
1511
|
+
sqlParams,
|
|
1512
|
+
req.requesterDetails
|
|
1409
1513
|
);
|
|
1410
1514
|
} else if (routeData.type === "ARRAY") {
|
|
1411
1515
|
return this.psqlConnectionPool.runQuery(
|
|
1412
1516
|
`${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
|
|
1413
|
-
sqlParams
|
|
1517
|
+
sqlParams,
|
|
1518
|
+
req.requesterDetails
|
|
1414
1519
|
);
|
|
1415
1520
|
} else if (routeData.type === "PAGED") {
|
|
1416
1521
|
const data = req.data;
|
|
@@ -1422,7 +1527,8 @@ ${sqlStatement};`,
|
|
|
1422
1527
|
data.perPage || DEFAULT_PAGED_PER_PAGE_NUMBER,
|
|
1423
1528
|
(data.page - 1) * data.perPage || DEFAULT_PAGED_PAGE_NUMBER,
|
|
1424
1529
|
...sqlParams
|
|
1425
|
-
]
|
|
1530
|
+
],
|
|
1531
|
+
req.requesterDetails
|
|
1426
1532
|
);
|
|
1427
1533
|
let total = 0;
|
|
1428
1534
|
if (ObjectUtils4.isArrayWithData(pageResults)) {
|
|
@@ -1453,7 +1559,7 @@ ${sqlStatement};`,
|
|
|
1453
1559
|
}
|
|
1454
1560
|
const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
1455
1561
|
const query = updateObjectQuery(routeData.table, bodyNoId, whereClause);
|
|
1456
|
-
await this.psqlConnectionPool.queryOne(query, [...sqlParams]);
|
|
1562
|
+
await this.psqlConnectionPool.queryOne(query, [...sqlParams], req.requesterDetails);
|
|
1457
1563
|
return this.executeGetRequest(req, routeData, schema);
|
|
1458
1564
|
}
|
|
1459
1565
|
async executeDeleteRequest(req, routeData, schema) {
|
|
@@ -1471,16 +1577,26 @@ ${sqlStatement};`,
|
|
|
1471
1577
|
FROM "${routeData.table}" ${joinStatement}`;
|
|
1472
1578
|
deleteStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
1473
1579
|
deleteStatement += ";";
|
|
1474
|
-
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams);
|
|
1580
|
+
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
|
|
1475
1581
|
return true;
|
|
1476
1582
|
}
|
|
1477
1583
|
generateJoinStatements(req, joins, baseTable, routeData, schema, userRole, sqlParams) {
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1584
|
+
let joinStatements = "";
|
|
1585
|
+
joins.forEach((item) => {
|
|
1586
|
+
if (!this.doesRoleHavePermissionToTable(userRole, schema, item.table))
|
|
1587
|
+
throw new RsError("UNAUTHORIZED", "You do not have permission to access this table");
|
|
1588
|
+
if (item.custom) {
|
|
1589
|
+
const customReplaced = this.replaceParamKeywords(item.custom, routeData, req, sqlParams);
|
|
1590
|
+
joinStatements += ` ${item.type} JOIN ${escapeColumnName(item.table)} ON ${customReplaced}
|
|
1591
|
+
`;
|
|
1592
|
+
} else {
|
|
1593
|
+
joinStatements += ` ${item.type} JOIN ${escapeColumnName(item.table)}${item.alias ? `AS "${item.alias}"` : ""} ON "${baseTable}"."${item.localColumnName}" = ${escapeColumnName(item.alias ? item.alias : item.table)}.${escapeColumnName(
|
|
1594
|
+
item.foreignColumnName
|
|
1595
|
+
)}
|
|
1596
|
+
`;
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
return joinStatements;
|
|
1484
1600
|
}
|
|
1485
1601
|
generateGroupBy(routeData) {
|
|
1486
1602
|
let groupBy = "";
|
|
@@ -1539,22 +1655,24 @@ ${sqlStatement};`,
|
|
|
1539
1655
|
const data = req.data;
|
|
1540
1656
|
if (routeData.type === "PAGED" && !!(data == null ? void 0 : data.filter)) {
|
|
1541
1657
|
let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
1658
|
+
var _a;
|
|
1542
1659
|
const requestParam = routeData.request.find((item) => {
|
|
1543
1660
|
return item.name === value.replace("$", "");
|
|
1544
1661
|
});
|
|
1545
1662
|
if (!requestParam)
|
|
1546
1663
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
1547
|
-
return data[requestParam.name];
|
|
1664
|
+
return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
|
|
1548
1665
|
});
|
|
1549
1666
|
statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
1667
|
+
var _a;
|
|
1550
1668
|
const requestParam = routeData.request.find((item) => {
|
|
1551
1669
|
return item.name === value.replace("#", "");
|
|
1552
1670
|
});
|
|
1553
1671
|
if (!requestParam)
|
|
1554
1672
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
1555
|
-
return data[requestParam.name];
|
|
1673
|
+
return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
|
|
1556
1674
|
});
|
|
1557
|
-
statement =
|
|
1675
|
+
statement = filterPsqlParser_default.parse(statement);
|
|
1558
1676
|
if (whereClause.startsWith("WHERE")) {
|
|
1559
1677
|
whereClause += ` AND (${statement})
|
|
1560
1678
|
`;
|
|
@@ -1567,24 +1685,33 @@ ${sqlStatement};`,
|
|
|
1567
1685
|
}
|
|
1568
1686
|
};
|
|
1569
1687
|
|
|
1570
|
-
// src/restura/restura.ts
|
|
1571
|
-
import { types } from "pg";
|
|
1572
|
-
|
|
1573
1688
|
// src/restura/sql/PsqlPool.ts
|
|
1574
|
-
import
|
|
1689
|
+
import pg from "pg";
|
|
1690
|
+
import format3 from "pg-format";
|
|
1691
|
+
var { Pool } = pg;
|
|
1575
1692
|
var PsqlPool = class {
|
|
1576
1693
|
constructor(poolConfig) {
|
|
1577
1694
|
this.poolConfig = poolConfig;
|
|
1578
1695
|
this.pool = new Pool(poolConfig);
|
|
1696
|
+
this.queryOne("SELECT NOW();", [], { isSystemUser: true, role: "", host: "localhost", ipAddress: "" }).then(() => {
|
|
1697
|
+
logger.info("Connected to PostgreSQL database");
|
|
1698
|
+
}).catch((error) => {
|
|
1699
|
+
logger.error("Error connecting to database", error);
|
|
1700
|
+
process.exit(1);
|
|
1701
|
+
});
|
|
1579
1702
|
}
|
|
1580
1703
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1581
|
-
async queryOne(query, options) {
|
|
1704
|
+
async queryOne(query, options, requesterDetails) {
|
|
1582
1705
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1706
|
+
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1583
1707
|
try {
|
|
1584
1708
|
const response = await this.pool.query(formattedQuery, options);
|
|
1709
|
+
if (response.rows.length === 0) throw new RsError("NOT_FOUND", "No results found");
|
|
1710
|
+
else if (response.rows.length > 1) throw new RsError("DUPLICATE", "More than one result found");
|
|
1585
1711
|
return response.rows[0];
|
|
1586
1712
|
} catch (error) {
|
|
1587
1713
|
console.error(error, query, options);
|
|
1714
|
+
if (RsError.isRsError(error)) throw error;
|
|
1588
1715
|
if ((error == null ? void 0 : error.routine) === "_bt_check_unique") {
|
|
1589
1716
|
throw new RsError("DUPLICATE", error.message);
|
|
1590
1717
|
}
|
|
@@ -1592,8 +1719,9 @@ var PsqlPool = class {
|
|
|
1592
1719
|
}
|
|
1593
1720
|
}
|
|
1594
1721
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1595
|
-
async runQuery(query, options) {
|
|
1722
|
+
async runQuery(query, options, requesterDetails) {
|
|
1596
1723
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1724
|
+
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1597
1725
|
const queryUpdated = query.replace(/[\t\n]/g, " ");
|
|
1598
1726
|
console.log(queryUpdated, options);
|
|
1599
1727
|
try {
|
|
@@ -1607,9 +1735,30 @@ var PsqlPool = class {
|
|
|
1607
1735
|
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
1608
1736
|
}
|
|
1609
1737
|
}
|
|
1738
|
+
logSqlStatement(query, options, requesterDetails, prefix = "") {
|
|
1739
|
+
if (logger.level !== "silly") return;
|
|
1740
|
+
let sqlStatement = "";
|
|
1741
|
+
if (options.length === 0) {
|
|
1742
|
+
sqlStatement = query;
|
|
1743
|
+
} else {
|
|
1744
|
+
let stringIndex = 0;
|
|
1745
|
+
sqlStatement = query.replace(/\$\d+/g, () => {
|
|
1746
|
+
const value = options[stringIndex++];
|
|
1747
|
+
if (typeof value === "number") return value.toString();
|
|
1748
|
+
return format3.literal(value);
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
let initiator = "Anonymous";
|
|
1752
|
+
if ("userId" in requesterDetails && requesterDetails.userId)
|
|
1753
|
+
initiator = `User Id (${requesterDetails.userId.toString()})`;
|
|
1754
|
+
if ("isSystemUser" in requesterDetails && requesterDetails.isSystemUser) initiator = "SYSTEM";
|
|
1755
|
+
logger.silly(`${prefix}query by ${initiator}, Query ->
|
|
1756
|
+
${sqlStatement}`);
|
|
1757
|
+
}
|
|
1610
1758
|
};
|
|
1611
1759
|
|
|
1612
1760
|
// src/restura/restura.ts
|
|
1761
|
+
var { types } = pg2;
|
|
1613
1762
|
var ResturaEngine = class {
|
|
1614
1763
|
constructor() {
|
|
1615
1764
|
this.publicEndpoints = {
|
|
@@ -1627,10 +1776,11 @@ var ResturaEngine = class {
|
|
|
1627
1776
|
* @returns A promise that resolves when the initialization is complete.
|
|
1628
1777
|
*/
|
|
1629
1778
|
async init(app, authenticationHandler, psqlConnectionPool) {
|
|
1779
|
+
this.resturaConfig = config2.validate("restura", resturaConfigSchema);
|
|
1630
1780
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
1631
1781
|
this.psqlEngine = new PsqlEngine(this.psqlConnectionPool);
|
|
1632
1782
|
setupPgReturnTypes();
|
|
1633
|
-
this.resturaConfig
|
|
1783
|
+
await customApiFactory_default.loadApiFiles(this.resturaConfig.customApiFolderPath);
|
|
1634
1784
|
this.authenticationHandler = authenticationHandler;
|
|
1635
1785
|
app.use(compression());
|
|
1636
1786
|
app.use(bodyParser.json({ limit: "32mb" }));
|
|
@@ -1693,7 +1843,7 @@ var ResturaEngine = class {
|
|
|
1693
1843
|
* @returns A promise that resolves when the API has been successfully generated and written to the output file.
|
|
1694
1844
|
*/
|
|
1695
1845
|
async generateApiFromSchema(outputFile, providedSchema) {
|
|
1696
|
-
|
|
1846
|
+
fs3.writeFileSync(
|
|
1697
1847
|
outputFile,
|
|
1698
1848
|
await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
|
|
1699
1849
|
);
|
|
@@ -1706,7 +1856,7 @@ var ResturaEngine = class {
|
|
|
1706
1856
|
* @returns A promise that resolves when the model has been successfully written to the output file.
|
|
1707
1857
|
*/
|
|
1708
1858
|
async generateModelFromSchema(outputFile, providedSchema) {
|
|
1709
|
-
|
|
1859
|
+
fs3.writeFileSync(
|
|
1710
1860
|
outputFile,
|
|
1711
1861
|
await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
|
|
1712
1862
|
);
|
|
@@ -1718,11 +1868,11 @@ var ResturaEngine = class {
|
|
|
1718
1868
|
* @throws {Error} If the schema file is missing or the schema is not valid.
|
|
1719
1869
|
*/
|
|
1720
1870
|
async getLatestFileSystemSchema() {
|
|
1721
|
-
if (!
|
|
1871
|
+
if (!fs3.existsSync(this.resturaConfig.schemaFilePath)) {
|
|
1722
1872
|
logger.error(`Missing restura schema file, expected path: ${this.resturaConfig.schemaFilePath}`);
|
|
1723
1873
|
throw new Error("Missing restura schema file");
|
|
1724
1874
|
}
|
|
1725
|
-
const schemaFileData =
|
|
1875
|
+
const schemaFileData = fs3.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
|
|
1726
1876
|
const schema = ObjectUtils5.safeParse(schemaFileData);
|
|
1727
1877
|
const isValid = await isSchemaValid(schema);
|
|
1728
1878
|
if (!isValid) {
|
|
@@ -1743,9 +1893,9 @@ var ResturaEngine = class {
|
|
|
1743
1893
|
async getHashes(providedSchema) {
|
|
1744
1894
|
var _a, _b, _c, _d;
|
|
1745
1895
|
const schemaHash = await this.generateHashForSchema(providedSchema);
|
|
1746
|
-
const apiFile =
|
|
1896
|
+
const apiFile = fs3.readFileSync(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
|
|
1747
1897
|
const apiCreatedSchemaHash = (_b = (_a = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a[1]) != null ? _b : "";
|
|
1748
|
-
const modelFile =
|
|
1898
|
+
const modelFile = fs3.readFileSync(path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
|
|
1749
1899
|
const modelCreatedSchemaHash = (_d = (_c = modelFile.toString().match(/\((.*)\)/)) == null ? void 0 : _c[1]) != null ? _d : "";
|
|
1750
1900
|
return {
|
|
1751
1901
|
schemaHash,
|
|
@@ -1781,27 +1931,27 @@ var ResturaEngine = class {
|
|
|
1781
1931
|
logger.info(`Restura loaded (${routeCount}) endpoint${routeCount > 1 ? "s" : ""}`);
|
|
1782
1932
|
}
|
|
1783
1933
|
async validateGeneratedTypesFolder() {
|
|
1784
|
-
if (!
|
|
1785
|
-
|
|
1934
|
+
if (!fs3.existsSync(this.resturaConfig.generatedTypesPath)) {
|
|
1935
|
+
fs3.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
|
|
1786
1936
|
}
|
|
1787
|
-
const hasApiFile =
|
|
1788
|
-
const hasModelsFile =
|
|
1937
|
+
const hasApiFile = fs3.existsSync(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
|
|
1938
|
+
const hasModelsFile = fs3.existsSync(path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
|
|
1789
1939
|
if (!hasApiFile) {
|
|
1790
|
-
await this.generateApiFromSchema(
|
|
1940
|
+
await this.generateApiFromSchema(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1791
1941
|
}
|
|
1792
1942
|
if (!hasModelsFile) {
|
|
1793
1943
|
await this.generateModelFromSchema(
|
|
1794
|
-
|
|
1944
|
+
path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1795
1945
|
this.schema
|
|
1796
1946
|
);
|
|
1797
1947
|
}
|
|
1798
1948
|
const hashes = await this.getHashes(this.schema);
|
|
1799
1949
|
if (hashes.schemaHash !== hashes.apiCreatedSchemaHash) {
|
|
1800
|
-
await this.generateApiFromSchema(
|
|
1950
|
+
await this.generateApiFromSchema(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1801
1951
|
}
|
|
1802
1952
|
if (hashes.schemaHash !== hashes.modelCreatedSchemaHash) {
|
|
1803
1953
|
await this.generateModelFromSchema(
|
|
1804
|
-
|
|
1954
|
+
path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1805
1955
|
this.schema
|
|
1806
1956
|
);
|
|
1807
1957
|
}
|
|
@@ -1831,9 +1981,9 @@ var ResturaEngine = class {
|
|
|
1831
1981
|
}
|
|
1832
1982
|
}
|
|
1833
1983
|
async updateTypes() {
|
|
1834
|
-
await this.generateApiFromSchema(
|
|
1984
|
+
await this.generateApiFromSchema(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1835
1985
|
await this.generateModelFromSchema(
|
|
1836
|
-
|
|
1986
|
+
path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1837
1987
|
this.schema
|
|
1838
1988
|
);
|
|
1839
1989
|
}
|
|
@@ -1856,11 +2006,16 @@ var ResturaEngine = class {
|
|
|
1856
2006
|
const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
|
|
1857
2007
|
this.validateAuthorization(req, routeData);
|
|
1858
2008
|
validateRequestParams(req, routeData, this.customTypeValidation);
|
|
2009
|
+
if (this.isCustomRoute(routeData)) {
|
|
2010
|
+
await this.runCustomRouteLogic(req, res, routeData);
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
1859
2013
|
const data = await this.psqlEngine.runQueryForRoute(
|
|
1860
2014
|
req,
|
|
1861
2015
|
routeData,
|
|
1862
2016
|
this.schema
|
|
1863
2017
|
);
|
|
2018
|
+
this.responseValidator.validateResponseParams(data, req.baseUrl, routeData);
|
|
1864
2019
|
if (routeData.type === "PAGED") res.sendNoWrap(data);
|
|
1865
2020
|
else res.sendData(data);
|
|
1866
2021
|
} catch (e) {
|
|
@@ -1870,34 +2025,25 @@ var ResturaEngine = class {
|
|
|
1870
2025
|
isCustomRoute(route) {
|
|
1871
2026
|
return route.type === "CUSTOM_ONE" || route.type === "CUSTOM_ARRAY" || route.type === "CUSTOM_PAGED";
|
|
1872
2027
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
// // @ts-expect-error - Here we are dynamically calling the function from a custom class, not sure how to typescript this
|
|
1893
|
-
// const customFunction = customApi[functionName] as (
|
|
1894
|
-
// req: RsRequest<T>,
|
|
1895
|
-
// res: RsResponse<T>,
|
|
1896
|
-
// routeData: RouteData
|
|
1897
|
-
// ) => Promise<void>;
|
|
1898
|
-
// if (!customFunction) throw new RsError('NOT_FOUND', `API path ${routeData.path} not implemented`);
|
|
1899
|
-
// await customFunction(req, res, routeData);
|
|
1900
|
-
// }
|
|
2028
|
+
async runCustomRouteLogic(req, res, routeData) {
|
|
2029
|
+
const version = req.baseUrl.split("/")[2];
|
|
2030
|
+
let domain = routeData.path.split("/")[1];
|
|
2031
|
+
domain = domain.split("-").reduce((acc, value, index) => {
|
|
2032
|
+
if (index === 0) acc = value;
|
|
2033
|
+
else acc += StringUtils3.capitalizeFirst(value);
|
|
2034
|
+
return acc;
|
|
2035
|
+
}, "");
|
|
2036
|
+
const customApiName = `${StringUtils3.capitalizeFirst(domain)}Api${StringUtils3.capitalizeFirst(version)}`;
|
|
2037
|
+
const customApi = customApiFactory_default.getCustomApi(customApiName);
|
|
2038
|
+
if (!customApi) throw new RsError("NOT_FOUND", `API domain ${domain}-${version} not found`);
|
|
2039
|
+
const functionName = `${routeData.method.toLowerCase()}${routeData.path.replace(new RegExp("-", "g"), "/").split("/").reduce((acc, cur) => {
|
|
2040
|
+
if (cur === "") return acc;
|
|
2041
|
+
return acc + StringUtils3.capitalizeFirst(cur);
|
|
2042
|
+
}, "")}`;
|
|
2043
|
+
const customFunction = customApi[functionName];
|
|
2044
|
+
if (!customFunction) throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented`);
|
|
2045
|
+
await customFunction(req, res, routeData);
|
|
2046
|
+
}
|
|
1901
2047
|
async generateHashForSchema(providedSchema) {
|
|
1902
2048
|
const schemaPrettyStr = await prettier3.format(JSON.stringify(providedSchema), __spreadValues({
|
|
1903
2049
|
parser: "json"
|
|
@@ -1922,7 +2068,7 @@ var ResturaEngine = class {
|
|
|
1922
2068
|
printWidth: 120,
|
|
1923
2069
|
singleQuote: true
|
|
1924
2070
|
}));
|
|
1925
|
-
|
|
2071
|
+
fs3.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
|
|
1926
2072
|
}
|
|
1927
2073
|
resetPublicEndpoints() {
|
|
1928
2074
|
this.publicEndpoints = {
|
|
@@ -1939,13 +2085,13 @@ var ResturaEngine = class {
|
|
|
1939
2085
|
if (!routeData.roles.includes(role))
|
|
1940
2086
|
throw new RsError("UNAUTHORIZED", "Not authorized to access this endpoint");
|
|
1941
2087
|
}
|
|
1942
|
-
getRouteData(method, baseUrl,
|
|
2088
|
+
getRouteData(method, baseUrl, path4) {
|
|
1943
2089
|
const endpoint = this.schema.endpoints.find((item) => {
|
|
1944
2090
|
return item.baseUrl === baseUrl;
|
|
1945
2091
|
});
|
|
1946
2092
|
if (!endpoint) throw new RsError("NOT_FOUND", "Route not found");
|
|
1947
2093
|
const route = endpoint.routes.find((item) => {
|
|
1948
|
-
return item.method === method && item.path ===
|
|
2094
|
+
return item.method === method && item.path === path4;
|
|
1949
2095
|
});
|
|
1950
2096
|
if (!route) throw new RsError("NOT_FOUND", "Route not found");
|
|
1951
2097
|
return route;
|
|
@@ -1972,6 +2118,9 @@ __decorateClass([
|
|
|
1972
2118
|
__decorateClass([
|
|
1973
2119
|
boundMethod
|
|
1974
2120
|
], ResturaEngine.prototype, "isCustomRoute", 1);
|
|
2121
|
+
__decorateClass([
|
|
2122
|
+
boundMethod
|
|
2123
|
+
], ResturaEngine.prototype, "runCustomRouteLogic", 1);
|
|
1975
2124
|
var setupPgReturnTypes = () => {
|
|
1976
2125
|
const TIMESTAMPTZ_OID = 1184;
|
|
1977
2126
|
types.setTypeParser(TIMESTAMPTZ_OID, (val) => {
|
|
@@ -1985,8 +2134,16 @@ var setupPgReturnTypes = () => {
|
|
|
1985
2134
|
setupPgReturnTypes();
|
|
1986
2135
|
var restura = new ResturaEngine();
|
|
1987
2136
|
export {
|
|
2137
|
+
HtmlStatusCodes,
|
|
1988
2138
|
PsqlPool,
|
|
2139
|
+
RsError,
|
|
2140
|
+
SQL,
|
|
2141
|
+
escapeColumnName,
|
|
2142
|
+
insertObjectQuery,
|
|
2143
|
+
isValueNumber2 as isValueNumber,
|
|
1989
2144
|
logger,
|
|
1990
|
-
|
|
2145
|
+
questionMarksToOrderedParams,
|
|
2146
|
+
restura,
|
|
2147
|
+
updateObjectQuery
|
|
1991
2148
|
};
|
|
1992
2149
|
//# sourceMappingURL=index.mjs.map
|