@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.js
CHANGED
|
@@ -67,9 +67,17 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
67
67
|
// src/index.ts
|
|
68
68
|
var src_exports = {};
|
|
69
69
|
__export(src_exports, {
|
|
70
|
+
HtmlStatusCodes: () => HtmlStatusCodes,
|
|
70
71
|
PsqlPool: () => PsqlPool,
|
|
72
|
+
RsError: () => RsError,
|
|
73
|
+
SQL: () => SQL,
|
|
74
|
+
escapeColumnName: () => escapeColumnName,
|
|
75
|
+
insertObjectQuery: () => insertObjectQuery,
|
|
76
|
+
isValueNumber: () => isValueNumber2,
|
|
71
77
|
logger: () => logger,
|
|
72
|
-
|
|
78
|
+
questionMarksToOrderedParams: () => questionMarksToOrderedParams,
|
|
79
|
+
restura: () => restura,
|
|
80
|
+
updateObjectQuery: () => updateObjectQuery
|
|
73
81
|
});
|
|
74
82
|
module.exports = __toCommonJS(src_exports);
|
|
75
83
|
|
|
@@ -81,12 +89,13 @@ var import_logform = require("logform");
|
|
|
81
89
|
// src/config.schema.ts
|
|
82
90
|
var import_zod = require("zod");
|
|
83
91
|
var loggerConfigSchema = import_zod.z.object({
|
|
84
|
-
level: import_zod.z.enum(["info", "warn", "error", "debug"]).default("info")
|
|
92
|
+
level: import_zod.z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
|
|
85
93
|
});
|
|
86
94
|
var resturaConfigSchema = import_zod.z.object({
|
|
87
95
|
authToken: import_zod.z.string().min(1, "Missing Restura Auth Token"),
|
|
88
96
|
sendErrorStackTrace: import_zod.z.boolean().default(false),
|
|
89
97
|
schemaFilePath: import_zod.z.string().default(process.cwd() + "/restura.schema.json"),
|
|
98
|
+
customApiFolderPath: import_zod.z.string().default(process.cwd() + "/dist/api"),
|
|
90
99
|
generatedTypesPath: import_zod.z.string().default(process.cwd() + "/src/@types")
|
|
91
100
|
});
|
|
92
101
|
|
|
@@ -125,6 +134,72 @@ var logger = import_winston.default.createLogger({
|
|
|
125
134
|
]
|
|
126
135
|
});
|
|
127
136
|
|
|
137
|
+
// src/restura/errors.ts
|
|
138
|
+
var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
|
|
139
|
+
HtmlStatusCodes2[HtmlStatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
|
|
140
|
+
HtmlStatusCodes2[HtmlStatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
|
|
141
|
+
HtmlStatusCodes2[HtmlStatusCodes2["FORBIDDEN"] = 403] = "FORBIDDEN";
|
|
142
|
+
HtmlStatusCodes2[HtmlStatusCodes2["NOT_FOUND"] = 404] = "NOT_FOUND";
|
|
143
|
+
HtmlStatusCodes2[HtmlStatusCodes2["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
|
|
144
|
+
HtmlStatusCodes2[HtmlStatusCodes2["CONFLICT"] = 409] = "CONFLICT";
|
|
145
|
+
HtmlStatusCodes2[HtmlStatusCodes2["VERSION_OUT_OF_DATE"] = 418] = "VERSION_OUT_OF_DATE";
|
|
146
|
+
HtmlStatusCodes2[HtmlStatusCodes2["SERVER_ERROR"] = 500] = "SERVER_ERROR";
|
|
147
|
+
HtmlStatusCodes2[HtmlStatusCodes2["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
|
|
148
|
+
HtmlStatusCodes2[HtmlStatusCodes2["NETWORK_CONNECT_TIMEOUT"] = 599] = "NETWORK_CONNECT_TIMEOUT";
|
|
149
|
+
return HtmlStatusCodes2;
|
|
150
|
+
})(HtmlStatusCodes || {});
|
|
151
|
+
var RsError = class _RsError {
|
|
152
|
+
constructor(errCode, message) {
|
|
153
|
+
this.err = errCode;
|
|
154
|
+
this.msg = message || "";
|
|
155
|
+
this.status = _RsError.htmlStatus(errCode);
|
|
156
|
+
this.stack = new Error().stack || "";
|
|
157
|
+
}
|
|
158
|
+
static htmlStatus(code) {
|
|
159
|
+
return htmlStatusMap[code];
|
|
160
|
+
}
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
162
|
+
static isRsError(error) {
|
|
163
|
+
return error instanceof _RsError;
|
|
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
|
+
|
|
128
203
|
// src/restura/restura.ts
|
|
129
204
|
var import_core_utils6 = require("@redskytech/core-utils");
|
|
130
205
|
var import_internal2 = require("@restura/internal");
|
|
@@ -181,59 +256,11 @@ var import_compression = __toESM(require("compression"));
|
|
|
181
256
|
var import_cookie_parser = __toESM(require("cookie-parser"));
|
|
182
257
|
var import_crypto = require("crypto");
|
|
183
258
|
var express = __toESM(require("express"));
|
|
184
|
-
var
|
|
185
|
-
var
|
|
259
|
+
var import_fs3 = __toESM(require("fs"));
|
|
260
|
+
var import_path3 = __toESM(require("path"));
|
|
261
|
+
var import_pg2 = __toESM(require("pg"));
|
|
186
262
|
var prettier3 = __toESM(require("prettier"));
|
|
187
263
|
|
|
188
|
-
// src/restura/errors.ts
|
|
189
|
-
var RsError = class _RsError {
|
|
190
|
-
constructor(errCode, message) {
|
|
191
|
-
this.err = errCode;
|
|
192
|
-
this.msg = message || "";
|
|
193
|
-
this.status = _RsError.htmlStatus(errCode);
|
|
194
|
-
this.stack = new Error().stack || "";
|
|
195
|
-
}
|
|
196
|
-
static htmlStatus(code) {
|
|
197
|
-
return htmlStatusMap[code];
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
var htmlStatusMap = {
|
|
201
|
-
UNKNOWN_ERROR: 500 /* SERVER_ERROR */,
|
|
202
|
-
NOT_FOUND: 404 /* NOT_FOUND */,
|
|
203
|
-
EMAIL_TAKEN: 409 /* CONFLICT */,
|
|
204
|
-
FORBIDDEN: 403 /* FORBIDDEN */,
|
|
205
|
-
CONFLICT: 409 /* CONFLICT */,
|
|
206
|
-
UNAUTHORIZED: 401 /* UNAUTHORIZED */,
|
|
207
|
-
UPDATE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
208
|
-
CREATE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
209
|
-
DELETE_FORBIDDEN: 403 /* FORBIDDEN */,
|
|
210
|
-
DELETE_FAILURE: 500 /* SERVER_ERROR */,
|
|
211
|
-
BAD_REQUEST: 400 /* BAD_REQUEST */,
|
|
212
|
-
INVALID_TOKEN: 401 /* UNAUTHORIZED */,
|
|
213
|
-
INCORRECT_EMAIL_OR_PASSWORD: 401 /* UNAUTHORIZED */,
|
|
214
|
-
DUPLICATE_TOKEN: 409 /* CONFLICT */,
|
|
215
|
-
DUPLICATE_USERNAME: 409 /* CONFLICT */,
|
|
216
|
-
DUPLICATE_EMAIL: 409 /* CONFLICT */,
|
|
217
|
-
DUPLICATE: 409 /* CONFLICT */,
|
|
218
|
-
EMAIL_NOT_VERIFIED: 400 /* BAD_REQUEST */,
|
|
219
|
-
UPDATE_WITHOUT_ID: 400 /* BAD_REQUEST */,
|
|
220
|
-
CONNECTION_ERROR: 599 /* NETWORK_CONNECT_TIMEOUT */,
|
|
221
|
-
INVALID_PAYMENT: 403 /* FORBIDDEN */,
|
|
222
|
-
DECLINED_PAYMENT: 403 /* FORBIDDEN */,
|
|
223
|
-
INTEGRATION_ERROR: 500 /* SERVER_ERROR */,
|
|
224
|
-
CANNOT_RESERVE: 403 /* FORBIDDEN */,
|
|
225
|
-
REFUND_FAILURE: 403 /* FORBIDDEN */,
|
|
226
|
-
INVALID_INVOICE: 403 /* FORBIDDEN */,
|
|
227
|
-
INVALID_COUPON: 403 /* FORBIDDEN */,
|
|
228
|
-
SERVICE_UNAVAILABLE: 503 /* SERVICE_UNAVAILABLE */,
|
|
229
|
-
METHOD_UNALLOWED: 405 /* METHOD_NOT_ALLOWED */,
|
|
230
|
-
LOGIN_EXPIRED: 401 /* UNAUTHORIZED */,
|
|
231
|
-
THIRD_PARTY_ERROR: 400 /* BAD_REQUEST */,
|
|
232
|
-
ACCESS_DENIED: 403 /* FORBIDDEN */,
|
|
233
|
-
DATABASE_ERROR: 500 /* SERVER_ERROR */,
|
|
234
|
-
SCHEMA_ERROR: 500 /* SERVER_ERROR */
|
|
235
|
-
};
|
|
236
|
-
|
|
237
264
|
// src/restura/sql/SqlUtils.ts
|
|
238
265
|
var SqlUtils = class _SqlUtils {
|
|
239
266
|
static convertDatabaseTypeToTypescript(type, value) {
|
|
@@ -325,9 +352,9 @@ var ResponseValidator = class _ResponseValidator {
|
|
|
325
352
|
return { validator: "any" };
|
|
326
353
|
}
|
|
327
354
|
getTypeFromTable(selector, name) {
|
|
328
|
-
const
|
|
329
|
-
if (
|
|
330
|
-
const tableName =
|
|
355
|
+
const path4 = selector.split(".");
|
|
356
|
+
if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { validator: "any", isOptional: false };
|
|
357
|
+
const tableName = path4.length == 2 ? path4[0] : name, columnName = path4.length == 2 ? path4[1] : path4[0];
|
|
331
358
|
const table = this.database.find((t) => t.name == tableName);
|
|
332
359
|
const column = table == null ? void 0 : table.columns.find((c) => c.name == columnName);
|
|
333
360
|
if (!table || !column) return { validator: "any", isOptional: false };
|
|
@@ -543,10 +570,10 @@ var ApiTree = class _ApiTree {
|
|
|
543
570
|
return `${p.name}${optional ? "?" : ""}:${responseType}${array ? "[]" : ""}`;
|
|
544
571
|
}
|
|
545
572
|
getTypeFromTable(selector, name) {
|
|
546
|
-
const
|
|
547
|
-
if (
|
|
548
|
-
let tableName =
|
|
549
|
-
const columnName =
|
|
573
|
+
const path4 = selector.split(".");
|
|
574
|
+
if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { responseType: "any", optional: false };
|
|
575
|
+
let tableName = path4.length == 2 ? path4[0] : name;
|
|
576
|
+
const columnName = path4.length == 2 ? path4[1] : path4[0];
|
|
550
577
|
let table = this.database.find((t) => t.name == tableName);
|
|
551
578
|
if (!table && tableName.includes("_")) {
|
|
552
579
|
const tableAliasSplit = tableName.split("_");
|
|
@@ -561,8 +588,8 @@ var ApiTree = class _ApiTree {
|
|
|
561
588
|
};
|
|
562
589
|
}
|
|
563
590
|
};
|
|
564
|
-
function pathToNamespaces(
|
|
565
|
-
return
|
|
591
|
+
function pathToNamespaces(path4) {
|
|
592
|
+
return path4.split("/").map((e) => import_core_utils.StringUtils.toPascalCasing(e)).filter((e) => e);
|
|
566
593
|
}
|
|
567
594
|
function apiGenerator(schema, schemaHash) {
|
|
568
595
|
let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
|
|
@@ -595,6 +622,92 @@ function apiGenerator(schema, schemaHash) {
|
|
|
595
622
|
}));
|
|
596
623
|
}
|
|
597
624
|
|
|
625
|
+
// src/restura/customApiFactory.ts
|
|
626
|
+
var import_fs = __toESM(require("fs"));
|
|
627
|
+
var import_path = __toESM(require("path"));
|
|
628
|
+
var CustomApiFactory = class {
|
|
629
|
+
constructor() {
|
|
630
|
+
this.customApis = {};
|
|
631
|
+
}
|
|
632
|
+
async loadApiFiles(baseFolderPath) {
|
|
633
|
+
const apiVersions = ["v1"];
|
|
634
|
+
for (const apiVersion of apiVersions) {
|
|
635
|
+
const apiVersionFolderPath = import_path.default.join(baseFolderPath, apiVersion);
|
|
636
|
+
if (!import_fs.default.existsSync(apiVersionFolderPath)) continue;
|
|
637
|
+
await this.addDirectory(apiVersionFolderPath, apiVersion);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
getCustomApi(customApiName) {
|
|
641
|
+
return this.customApis[customApiName];
|
|
642
|
+
}
|
|
643
|
+
async addDirectory(directoryPath, apiVersion) {
|
|
644
|
+
const entries = import_fs.default.readdirSync(directoryPath, {
|
|
645
|
+
withFileTypes: true
|
|
646
|
+
});
|
|
647
|
+
for (const entry of entries) {
|
|
648
|
+
if (entry.isFile()) {
|
|
649
|
+
if (entry.name.endsWith(`.api.${apiVersion}.js`) === false) continue;
|
|
650
|
+
try {
|
|
651
|
+
const importPath = `${import_path.default.join(directoryPath, entry.name)}`;
|
|
652
|
+
const ApiImport = await import(importPath);
|
|
653
|
+
const customApiClass = new ApiImport.default();
|
|
654
|
+
logger.info(`Registering custom API: ${ApiImport.default.name}`);
|
|
655
|
+
this.bindMethodsToInstance(customApiClass);
|
|
656
|
+
this.customApis[ApiImport.default.name] = customApiClass;
|
|
657
|
+
} catch (e) {
|
|
658
|
+
console.error(e);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
bindMethodsToInstance(instance) {
|
|
664
|
+
const proto = Object.getPrototypeOf(instance);
|
|
665
|
+
Object.getOwnPropertyNames(proto).forEach((key) => {
|
|
666
|
+
const property = instance[key];
|
|
667
|
+
if (typeof property === "function" && key !== "constructor") {
|
|
668
|
+
instance[key] = property.bind(instance);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
var customApiFactory = new CustomApiFactory();
|
|
674
|
+
var customApiFactory_default = customApiFactory;
|
|
675
|
+
|
|
676
|
+
// src/restura/customTypeValidationGenerator.ts
|
|
677
|
+
var import_fs2 = __toESM(require("fs"));
|
|
678
|
+
var TJS = __toESM(require("typescript-json-schema"));
|
|
679
|
+
var import_path2 = __toESM(require("path"));
|
|
680
|
+
var import_tmp = __toESM(require("tmp"));
|
|
681
|
+
var process2 = __toESM(require("process"));
|
|
682
|
+
function customTypeValidationGenerator(currentSchema) {
|
|
683
|
+
const schemaObject = {};
|
|
684
|
+
const customInterfaceNames = currentSchema.customTypes.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
|
|
685
|
+
if (!customInterfaceNames) return {};
|
|
686
|
+
const temporaryFile = import_tmp.default.fileSync({ mode: 420, prefix: "prefix-", postfix: ".ts" });
|
|
687
|
+
import_fs2.default.writeFileSync(temporaryFile.name, currentSchema.customTypes);
|
|
688
|
+
const compilerOptions = {
|
|
689
|
+
strictNullChecks: true,
|
|
690
|
+
skipLibCheck: true
|
|
691
|
+
};
|
|
692
|
+
const program = TJS.getProgramFromFiles(
|
|
693
|
+
[
|
|
694
|
+
(0, import_path2.resolve)(temporaryFile.name),
|
|
695
|
+
// find a way to remove
|
|
696
|
+
import_path2.default.join(process2.cwd(), "src/@types/models.d.ts"),
|
|
697
|
+
import_path2.default.join(process2.cwd(), "src/@types/api.d.ts")
|
|
698
|
+
],
|
|
699
|
+
compilerOptions
|
|
700
|
+
);
|
|
701
|
+
customInterfaceNames.forEach((item) => {
|
|
702
|
+
const ddlSchema = TJS.generateSchema(program, item, {
|
|
703
|
+
required: true
|
|
704
|
+
});
|
|
705
|
+
schemaObject[item] = ddlSchema || {};
|
|
706
|
+
});
|
|
707
|
+
temporaryFile.removeCallback();
|
|
708
|
+
return schemaObject;
|
|
709
|
+
}
|
|
710
|
+
|
|
598
711
|
// src/restura/middleware/addApiResponseFunctions.ts
|
|
599
712
|
function addApiResponseFunctions(req, res, next) {
|
|
600
713
|
res.sendData = function(data, statusCode = 200) {
|
|
@@ -623,9 +736,17 @@ function addApiResponseFunctions(req, res, next) {
|
|
|
623
736
|
next();
|
|
624
737
|
}
|
|
625
738
|
|
|
626
|
-
// src/restura/
|
|
627
|
-
|
|
628
|
-
|
|
739
|
+
// src/restura/middleware/authenticateUser.ts
|
|
740
|
+
function authenticateUser(applicationAuthenticateHandler) {
|
|
741
|
+
return (req, res, next) => {
|
|
742
|
+
applicationAuthenticateHandler(req, res, (userDetails) => {
|
|
743
|
+
req.requesterDetails = __spreadValues(__spreadValues({}, req.requesterDetails), userDetails);
|
|
744
|
+
next();
|
|
745
|
+
});
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// src/restura/restura.schema.ts
|
|
629
750
|
var import_zod3 = require("zod");
|
|
630
751
|
|
|
631
752
|
// src/restura/types/validation.types.ts
|
|
@@ -636,264 +757,83 @@ var validatorDataSchema = import_zod2.z.object({
|
|
|
636
757
|
value: validatorDataSchemeValue
|
|
637
758
|
}).strict();
|
|
638
759
|
|
|
639
|
-
// src/restura/utils/addQuotesToStrings.ts
|
|
640
|
-
function addQuotesToStrings(variable) {
|
|
641
|
-
if (typeof variable === "string") {
|
|
642
|
-
return `'${variable}'`;
|
|
643
|
-
} else if (Array.isArray(variable)) {
|
|
644
|
-
const arrayWithQuotes = variable.map(addQuotesToStrings);
|
|
645
|
-
return arrayWithQuotes;
|
|
646
|
-
} else {
|
|
647
|
-
return variable;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// src/restura/validateRequestParams.ts
|
|
652
|
-
function validateRequestParams(req, routeData, validationSchema) {
|
|
653
|
-
const requestData = getRequestData(req);
|
|
654
|
-
req.data = requestData;
|
|
655
|
-
if (routeData.request === void 0) {
|
|
656
|
-
if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
|
|
657
|
-
throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
|
|
658
|
-
if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
|
|
659
|
-
if (!routeData.requestType) throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
|
|
660
|
-
const currentInterface = validationSchema[routeData.requestType];
|
|
661
|
-
const validator = new import_jsonschema.default.Validator();
|
|
662
|
-
const executeValidation = validator.validate(req.data, currentInterface);
|
|
663
|
-
if (!executeValidation.valid) {
|
|
664
|
-
throw new RsError(
|
|
665
|
-
"BAD_REQUEST",
|
|
666
|
-
`Request custom setup has failed the following check: (${executeValidation.errors})`
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
Object.keys(req.data).forEach((requestParamName) => {
|
|
672
|
-
const requestParam = routeData.request.find((param) => param.name === requestParamName);
|
|
673
|
-
if (!requestParam) {
|
|
674
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
|
|
675
|
-
}
|
|
676
|
-
});
|
|
677
|
-
routeData.request.forEach((requestParam) => {
|
|
678
|
-
const requestValue = requestData[requestParam.name];
|
|
679
|
-
if (requestParam.required && requestValue === void 0)
|
|
680
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
|
|
681
|
-
else if (!requestParam.required && requestValue === void 0) return;
|
|
682
|
-
validateRequestSingleParam(requestValue, requestParam);
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
function validateRequestSingleParam(requestValue, requestParam) {
|
|
686
|
-
requestParam.validator.forEach((validator) => {
|
|
687
|
-
switch (validator.type) {
|
|
688
|
-
case "TYPE_CHECK":
|
|
689
|
-
performTypeCheck(requestValue, validator, requestParam.name);
|
|
690
|
-
break;
|
|
691
|
-
case "MIN":
|
|
692
|
-
performMinCheck(requestValue, validator, requestParam.name);
|
|
693
|
-
break;
|
|
694
|
-
case "MAX":
|
|
695
|
-
performMaxCheck(requestValue, validator, requestParam.name);
|
|
696
|
-
break;
|
|
697
|
-
case "ONE_OF":
|
|
698
|
-
performOneOfCheck(requestValue, validator, requestParam.name);
|
|
699
|
-
break;
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
function isValidType(type, requestValue) {
|
|
704
|
-
try {
|
|
705
|
-
expectValidType(type, requestValue);
|
|
706
|
-
return true;
|
|
707
|
-
} catch (e) {
|
|
708
|
-
return false;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
function expectValidType(type, requestValue) {
|
|
712
|
-
if (type === "number") {
|
|
713
|
-
return import_zod3.z.number().parse(requestValue);
|
|
714
|
-
}
|
|
715
|
-
if (type === "string") {
|
|
716
|
-
return import_zod3.z.string().parse(requestValue);
|
|
717
|
-
}
|
|
718
|
-
if (type === "boolean") {
|
|
719
|
-
return import_zod3.z.boolean().parse(requestValue);
|
|
720
|
-
}
|
|
721
|
-
if (type === "string[]") {
|
|
722
|
-
return import_zod3.z.array(import_zod3.z.string()).parse(requestValue);
|
|
723
|
-
}
|
|
724
|
-
if (type === "number[]") {
|
|
725
|
-
return import_zod3.z.array(import_zod3.z.number()).parse(requestValue);
|
|
726
|
-
}
|
|
727
|
-
if (type === "any[]") {
|
|
728
|
-
return import_zod3.z.array(import_zod3.z.any()).parse(requestValue);
|
|
729
|
-
}
|
|
730
|
-
if (type === "object") {
|
|
731
|
-
return import_zod3.z.object({}).strict().parse(requestValue);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
function performTypeCheck(requestValue, validator, requestParamName) {
|
|
735
|
-
if (!isValidType(validator.value, requestValue)) {
|
|
736
|
-
throw new RsError(
|
|
737
|
-
"BAD_REQUEST",
|
|
738
|
-
`Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
|
|
739
|
-
);
|
|
740
|
-
}
|
|
741
|
-
try {
|
|
742
|
-
validatorDataSchemeValue.parse(validator.value);
|
|
743
|
-
} catch (e) {
|
|
744
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
function expectOnlyNumbers(requestValue, validator, requestParamName) {
|
|
748
|
-
if (!isValueNumber(requestValue))
|
|
749
|
-
throw new RsError(
|
|
750
|
-
"BAD_REQUEST",
|
|
751
|
-
`Request param (${requestParamName}) with value (${requestValue}) is not of type number`
|
|
752
|
-
);
|
|
753
|
-
if (!isValueNumber(validator.value))
|
|
754
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
|
|
755
|
-
}
|
|
756
|
-
function performMinCheck(requestValue, validator, requestParamName) {
|
|
757
|
-
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
758
|
-
if (requestValue < validator.value)
|
|
759
|
-
throw new RsError(
|
|
760
|
-
"BAD_REQUEST",
|
|
761
|
-
`Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
function performMaxCheck(requestValue, validator, requestParamName) {
|
|
765
|
-
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
766
|
-
if (requestValue > validator.value)
|
|
767
|
-
throw new RsError(
|
|
768
|
-
"BAD_REQUEST",
|
|
769
|
-
`Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
function performOneOfCheck(requestValue, validator, requestParamName) {
|
|
773
|
-
if (!import_core_utils2.ObjectUtils.isArrayWithData(validator.value))
|
|
774
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
|
|
775
|
-
if (typeof requestValue === "object")
|
|
776
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
|
|
777
|
-
if (!validator.value.includes(requestValue))
|
|
778
|
-
throw new RsError(
|
|
779
|
-
"BAD_REQUEST",
|
|
780
|
-
`Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
function isValueNumber(value) {
|
|
784
|
-
return !isNaN(Number(value));
|
|
785
|
-
}
|
|
786
|
-
function getRequestData(req) {
|
|
787
|
-
let body = "";
|
|
788
|
-
if (req.method === "GET" || req.method === "DELETE") {
|
|
789
|
-
body = "query";
|
|
790
|
-
} else {
|
|
791
|
-
body = "body";
|
|
792
|
-
}
|
|
793
|
-
const bodyData = req[body];
|
|
794
|
-
if (bodyData) {
|
|
795
|
-
for (const attr in bodyData) {
|
|
796
|
-
if (attr === "token") {
|
|
797
|
-
delete bodyData[attr];
|
|
798
|
-
continue;
|
|
799
|
-
}
|
|
800
|
-
if (bodyData[attr] instanceof Array) {
|
|
801
|
-
const attrList = [];
|
|
802
|
-
for (const value of bodyData[attr]) {
|
|
803
|
-
if (isNaN(Number(value))) continue;
|
|
804
|
-
attrList.push(Number(value));
|
|
805
|
-
}
|
|
806
|
-
if (import_core_utils2.ObjectUtils.isArrayWithData(attrList)) {
|
|
807
|
-
bodyData[attr] = attrList;
|
|
808
|
-
}
|
|
809
|
-
} else {
|
|
810
|
-
bodyData[attr] = import_core_utils2.ObjectUtils.safeParse(bodyData[attr]);
|
|
811
|
-
if (isNaN(Number(bodyData[attr]))) continue;
|
|
812
|
-
bodyData[attr] = Number(bodyData[attr]);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return bodyData;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
760
|
// src/restura/restura.schema.ts
|
|
820
|
-
var
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
tableName: import_zod4.z.string()
|
|
761
|
+
var orderBySchema = import_zod3.z.object({
|
|
762
|
+
columnName: import_zod3.z.string(),
|
|
763
|
+
order: import_zod3.z.enum(["ASC", "DESC"]),
|
|
764
|
+
tableName: import_zod3.z.string()
|
|
825
765
|
}).strict();
|
|
826
|
-
var groupBySchema =
|
|
827
|
-
columnName:
|
|
828
|
-
tableName:
|
|
766
|
+
var groupBySchema = import_zod3.z.object({
|
|
767
|
+
columnName: import_zod3.z.string(),
|
|
768
|
+
tableName: import_zod3.z.string()
|
|
829
769
|
}).strict();
|
|
830
|
-
var whereDataSchema =
|
|
831
|
-
tableName:
|
|
832
|
-
columnName:
|
|
833
|
-
operator:
|
|
834
|
-
value:
|
|
835
|
-
custom:
|
|
836
|
-
conjunction:
|
|
770
|
+
var whereDataSchema = import_zod3.z.object({
|
|
771
|
+
tableName: import_zod3.z.string().optional(),
|
|
772
|
+
columnName: import_zod3.z.string().optional(),
|
|
773
|
+
operator: import_zod3.z.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH"]).optional(),
|
|
774
|
+
value: import_zod3.z.string().or(import_zod3.z.number()).optional(),
|
|
775
|
+
custom: import_zod3.z.string().optional(),
|
|
776
|
+
conjunction: import_zod3.z.enum(["AND", "OR"]).optional()
|
|
837
777
|
}).strict();
|
|
838
|
-
var assignmentDataSchema =
|
|
839
|
-
name:
|
|
840
|
-
value:
|
|
778
|
+
var assignmentDataSchema = import_zod3.z.object({
|
|
779
|
+
name: import_zod3.z.string(),
|
|
780
|
+
value: import_zod3.z.string()
|
|
841
781
|
}).strict();
|
|
842
|
-
var joinDataSchema =
|
|
843
|
-
table:
|
|
844
|
-
localColumnName:
|
|
845
|
-
foreignColumnName:
|
|
846
|
-
custom:
|
|
847
|
-
type:
|
|
848
|
-
alias:
|
|
782
|
+
var joinDataSchema = import_zod3.z.object({
|
|
783
|
+
table: import_zod3.z.string(),
|
|
784
|
+
localColumnName: import_zod3.z.string().optional(),
|
|
785
|
+
foreignColumnName: import_zod3.z.string().optional(),
|
|
786
|
+
custom: import_zod3.z.string().optional(),
|
|
787
|
+
type: import_zod3.z.enum(["LEFT", "INNER"]),
|
|
788
|
+
alias: import_zod3.z.string().optional()
|
|
849
789
|
}).strict();
|
|
850
|
-
var requestDataSchema =
|
|
851
|
-
name:
|
|
852
|
-
required:
|
|
853
|
-
validator:
|
|
790
|
+
var requestDataSchema = import_zod3.z.object({
|
|
791
|
+
name: import_zod3.z.string(),
|
|
792
|
+
required: import_zod3.z.boolean(),
|
|
793
|
+
validator: import_zod3.z.array(validatorDataSchema)
|
|
854
794
|
}).strict();
|
|
855
|
-
var responseDataSchema =
|
|
856
|
-
name:
|
|
857
|
-
selector:
|
|
858
|
-
subquery:
|
|
859
|
-
table:
|
|
860
|
-
joins:
|
|
861
|
-
where:
|
|
862
|
-
properties:
|
|
795
|
+
var responseDataSchema = import_zod3.z.object({
|
|
796
|
+
name: import_zod3.z.string(),
|
|
797
|
+
selector: import_zod3.z.string().optional(),
|
|
798
|
+
subquery: import_zod3.z.object({
|
|
799
|
+
table: import_zod3.z.string(),
|
|
800
|
+
joins: import_zod3.z.array(joinDataSchema),
|
|
801
|
+
where: import_zod3.z.array(whereDataSchema),
|
|
802
|
+
properties: import_zod3.z.array(import_zod3.z.lazy(() => responseDataSchema)),
|
|
863
803
|
// Explicit type for the lazy schema
|
|
864
804
|
groupBy: groupBySchema.optional(),
|
|
865
805
|
orderBy: orderBySchema.optional()
|
|
866
806
|
}).optional()
|
|
867
807
|
}).strict();
|
|
868
|
-
var routeDataBaseSchema =
|
|
869
|
-
method:
|
|
870
|
-
name:
|
|
871
|
-
description:
|
|
872
|
-
path:
|
|
873
|
-
roles:
|
|
808
|
+
var routeDataBaseSchema = import_zod3.z.object({
|
|
809
|
+
method: import_zod3.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
810
|
+
name: import_zod3.z.string(),
|
|
811
|
+
description: import_zod3.z.string(),
|
|
812
|
+
path: import_zod3.z.string(),
|
|
813
|
+
roles: import_zod3.z.array(import_zod3.z.string())
|
|
874
814
|
}).strict();
|
|
875
815
|
var standardRouteSchema = routeDataBaseSchema.extend({
|
|
876
|
-
type:
|
|
877
|
-
table:
|
|
878
|
-
joins:
|
|
879
|
-
assignments:
|
|
880
|
-
where:
|
|
881
|
-
request:
|
|
882
|
-
response:
|
|
816
|
+
type: import_zod3.z.enum(["ONE", "ARRAY", "PAGED"]),
|
|
817
|
+
table: import_zod3.z.string(),
|
|
818
|
+
joins: import_zod3.z.array(joinDataSchema),
|
|
819
|
+
assignments: import_zod3.z.array(assignmentDataSchema),
|
|
820
|
+
where: import_zod3.z.array(whereDataSchema),
|
|
821
|
+
request: import_zod3.z.array(requestDataSchema),
|
|
822
|
+
response: import_zod3.z.array(responseDataSchema),
|
|
883
823
|
groupBy: groupBySchema.optional(),
|
|
884
824
|
orderBy: orderBySchema.optional()
|
|
885
825
|
}).strict();
|
|
886
826
|
var customRouteSchema = routeDataBaseSchema.extend({
|
|
887
|
-
type:
|
|
888
|
-
responseType:
|
|
889
|
-
requestType:
|
|
890
|
-
request:
|
|
891
|
-
table:
|
|
892
|
-
joins:
|
|
893
|
-
assignments:
|
|
894
|
-
fileUploadType:
|
|
827
|
+
type: import_zod3.z.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
|
|
828
|
+
responseType: import_zod3.z.union([import_zod3.z.string(), import_zod3.z.enum(["string", "number", "boolean"])]),
|
|
829
|
+
requestType: import_zod3.z.string().optional(),
|
|
830
|
+
request: import_zod3.z.array(requestDataSchema).optional(),
|
|
831
|
+
table: import_zod3.z.undefined(),
|
|
832
|
+
joins: import_zod3.z.undefined(),
|
|
833
|
+
assignments: import_zod3.z.undefined(),
|
|
834
|
+
fileUploadType: import_zod3.z.enum(["SINGLE", "MULTIPLE"]).optional()
|
|
895
835
|
}).strict();
|
|
896
|
-
var postgresColumnNumericTypesSchema =
|
|
836
|
+
var postgresColumnNumericTypesSchema = import_zod3.z.enum([
|
|
897
837
|
"SMALLINT",
|
|
898
838
|
// 2 bytes, -32,768 to 32,767
|
|
899
839
|
"INTEGER",
|
|
@@ -913,7 +853,7 @@ var postgresColumnNumericTypesSchema = import_zod4.z.enum([
|
|
|
913
853
|
"BIGSERIAL"
|
|
914
854
|
// auto-incrementing big integer
|
|
915
855
|
]);
|
|
916
|
-
var postgresColumnStringTypesSchema =
|
|
856
|
+
var postgresColumnStringTypesSchema = import_zod3.z.enum([
|
|
917
857
|
"CHAR",
|
|
918
858
|
// fixed-length, blank-padded
|
|
919
859
|
"VARCHAR",
|
|
@@ -923,7 +863,7 @@ var postgresColumnStringTypesSchema = import_zod4.z.enum([
|
|
|
923
863
|
"BYTEA"
|
|
924
864
|
// binary data
|
|
925
865
|
]);
|
|
926
|
-
var postgresColumnDateTypesSchema =
|
|
866
|
+
var postgresColumnDateTypesSchema = import_zod3.z.enum([
|
|
927
867
|
"DATE",
|
|
928
868
|
// calendar date (year, month, day)
|
|
929
869
|
"TIMESTAMP",
|
|
@@ -935,7 +875,7 @@ var postgresColumnDateTypesSchema = import_zod4.z.enum([
|
|
|
935
875
|
"INTERVAL"
|
|
936
876
|
// time span
|
|
937
877
|
]);
|
|
938
|
-
var mariaDbColumnNumericTypesSchema =
|
|
878
|
+
var mariaDbColumnNumericTypesSchema = import_zod3.z.enum([
|
|
939
879
|
"BOOLEAN",
|
|
940
880
|
// 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
|
|
941
881
|
"TINYINT",
|
|
@@ -955,7 +895,7 @@ var mariaDbColumnNumericTypesSchema = import_zod4.z.enum([
|
|
|
955
895
|
"DOUBLE"
|
|
956
896
|
// 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.
|
|
957
897
|
]);
|
|
958
|
-
var mariaDbColumnStringTypesSchema =
|
|
898
|
+
var mariaDbColumnStringTypesSchema = import_zod3.z.enum([
|
|
959
899
|
"CHAR",
|
|
960
900
|
// 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.
|
|
961
901
|
"VARCHAR",
|
|
@@ -981,7 +921,7 @@ var mariaDbColumnStringTypesSchema = import_zod4.z.enum([
|
|
|
981
921
|
"ENUM"
|
|
982
922
|
// Enum type
|
|
983
923
|
]);
|
|
984
|
-
var mariaDbColumnDateTypesSchema =
|
|
924
|
+
var mariaDbColumnDateTypesSchema = import_zod3.z.enum([
|
|
985
925
|
"DATE",
|
|
986
926
|
// 4-bytes Date has year, month, and day.
|
|
987
927
|
"DATETIME",
|
|
@@ -991,9 +931,9 @@ var mariaDbColumnDateTypesSchema = import_zod4.z.enum([
|
|
|
991
931
|
"TIMESTAMP"
|
|
992
932
|
// 4-bytes Values are stored as the number of seconds since 1970-01-01 00:00:00 UTC, and optionally microseconds.
|
|
993
933
|
]);
|
|
994
|
-
var columnDataSchema =
|
|
995
|
-
name:
|
|
996
|
-
type:
|
|
934
|
+
var columnDataSchema = import_zod3.z.object({
|
|
935
|
+
name: import_zod3.z.string(),
|
|
936
|
+
type: import_zod3.z.union([
|
|
997
937
|
postgresColumnNumericTypesSchema,
|
|
998
938
|
postgresColumnStringTypesSchema,
|
|
999
939
|
postgresColumnDateTypesSchema,
|
|
@@ -1001,24 +941,24 @@ var columnDataSchema = import_zod4.z.object({
|
|
|
1001
941
|
mariaDbColumnStringTypesSchema,
|
|
1002
942
|
mariaDbColumnDateTypesSchema
|
|
1003
943
|
]),
|
|
1004
|
-
isNullable:
|
|
1005
|
-
roles:
|
|
1006
|
-
comment:
|
|
1007
|
-
default:
|
|
1008
|
-
value:
|
|
1009
|
-
isPrimary:
|
|
1010
|
-
isUnique:
|
|
1011
|
-
hasAutoIncrement:
|
|
1012
|
-
length:
|
|
944
|
+
isNullable: import_zod3.z.boolean(),
|
|
945
|
+
roles: import_zod3.z.array(import_zod3.z.string()),
|
|
946
|
+
comment: import_zod3.z.string().optional(),
|
|
947
|
+
default: import_zod3.z.string().optional(),
|
|
948
|
+
value: import_zod3.z.string().optional(),
|
|
949
|
+
isPrimary: import_zod3.z.boolean().optional(),
|
|
950
|
+
isUnique: import_zod3.z.boolean().optional(),
|
|
951
|
+
hasAutoIncrement: import_zod3.z.boolean().optional(),
|
|
952
|
+
length: import_zod3.z.number().optional()
|
|
1013
953
|
}).strict();
|
|
1014
|
-
var indexDataSchema =
|
|
1015
|
-
name:
|
|
1016
|
-
columns:
|
|
1017
|
-
isUnique:
|
|
1018
|
-
isPrimaryKey:
|
|
1019
|
-
order:
|
|
954
|
+
var indexDataSchema = import_zod3.z.object({
|
|
955
|
+
name: import_zod3.z.string(),
|
|
956
|
+
columns: import_zod3.z.array(import_zod3.z.string()),
|
|
957
|
+
isUnique: import_zod3.z.boolean(),
|
|
958
|
+
isPrimaryKey: import_zod3.z.boolean(),
|
|
959
|
+
order: import_zod3.z.enum(["ASC", "DESC"])
|
|
1020
960
|
}).strict();
|
|
1021
|
-
var foreignKeyActionsSchema =
|
|
961
|
+
var foreignKeyActionsSchema = import_zod3.z.enum([
|
|
1022
962
|
"CASCADE",
|
|
1023
963
|
// CASCADE action for foreign keys
|
|
1024
964
|
"SET NULL",
|
|
@@ -1030,48 +970,229 @@ var foreignKeyActionsSchema = import_zod4.z.enum([
|
|
|
1030
970
|
"SET DEFAULT"
|
|
1031
971
|
// SET DEFAULT action for foreign keys
|
|
1032
972
|
]);
|
|
1033
|
-
var foreignKeyDataSchema =
|
|
1034
|
-
name:
|
|
1035
|
-
column:
|
|
1036
|
-
refTable:
|
|
1037
|
-
refColumn:
|
|
973
|
+
var foreignKeyDataSchema = import_zod3.z.object({
|
|
974
|
+
name: import_zod3.z.string(),
|
|
975
|
+
column: import_zod3.z.string(),
|
|
976
|
+
refTable: import_zod3.z.string(),
|
|
977
|
+
refColumn: import_zod3.z.string(),
|
|
1038
978
|
onDelete: foreignKeyActionsSchema,
|
|
1039
979
|
onUpdate: foreignKeyActionsSchema
|
|
1040
980
|
}).strict();
|
|
1041
|
-
var checkConstraintDataSchema =
|
|
1042
|
-
name:
|
|
1043
|
-
check:
|
|
981
|
+
var checkConstraintDataSchema = import_zod3.z.object({
|
|
982
|
+
name: import_zod3.z.string(),
|
|
983
|
+
check: import_zod3.z.string()
|
|
1044
984
|
}).strict();
|
|
1045
|
-
var tableDataSchema =
|
|
1046
|
-
name:
|
|
1047
|
-
columns:
|
|
1048
|
-
indexes:
|
|
1049
|
-
foreignKeys:
|
|
1050
|
-
checkConstraints:
|
|
1051
|
-
roles:
|
|
985
|
+
var tableDataSchema = import_zod3.z.object({
|
|
986
|
+
name: import_zod3.z.string(),
|
|
987
|
+
columns: import_zod3.z.array(columnDataSchema),
|
|
988
|
+
indexes: import_zod3.z.array(indexDataSchema),
|
|
989
|
+
foreignKeys: import_zod3.z.array(foreignKeyDataSchema),
|
|
990
|
+
checkConstraints: import_zod3.z.array(checkConstraintDataSchema),
|
|
991
|
+
roles: import_zod3.z.array(import_zod3.z.string())
|
|
1052
992
|
}).strict();
|
|
1053
|
-
var endpointDataSchema =
|
|
1054
|
-
name:
|
|
1055
|
-
description:
|
|
1056
|
-
baseUrl:
|
|
1057
|
-
routes:
|
|
993
|
+
var endpointDataSchema = import_zod3.z.object({
|
|
994
|
+
name: import_zod3.z.string(),
|
|
995
|
+
description: import_zod3.z.string(),
|
|
996
|
+
baseUrl: import_zod3.z.string(),
|
|
997
|
+
routes: import_zod3.z.array(import_zod3.z.union([standardRouteSchema, customRouteSchema]))
|
|
1058
998
|
}).strict();
|
|
1059
|
-
var resturaZodSchema =
|
|
1060
|
-
database:
|
|
1061
|
-
endpoints:
|
|
1062
|
-
globalParams:
|
|
1063
|
-
roles:
|
|
1064
|
-
customTypes:
|
|
999
|
+
var resturaZodSchema = import_zod3.z.object({
|
|
1000
|
+
database: import_zod3.z.array(tableDataSchema),
|
|
1001
|
+
endpoints: import_zod3.z.array(endpointDataSchema),
|
|
1002
|
+
globalParams: import_zod3.z.array(import_zod3.z.string()),
|
|
1003
|
+
roles: import_zod3.z.array(import_zod3.z.string()),
|
|
1004
|
+
customTypes: import_zod3.z.string()
|
|
1065
1005
|
}).strict();
|
|
1066
1006
|
async function isSchemaValid(schemaToCheck) {
|
|
1067
1007
|
try {
|
|
1068
1008
|
resturaZodSchema.parse(schemaToCheck);
|
|
1069
1009
|
return true;
|
|
1070
|
-
} catch (error) {
|
|
1071
|
-
logger.error(error);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
logger.error(error);
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// src/restura/validateRequestParams.ts
|
|
1017
|
+
var import_core_utils2 = require("@redskytech/core-utils");
|
|
1018
|
+
var import_jsonschema = __toESM(require("jsonschema"));
|
|
1019
|
+
var import_zod4 = require("zod");
|
|
1020
|
+
|
|
1021
|
+
// src/restura/utils/addQuotesToStrings.ts
|
|
1022
|
+
function addQuotesToStrings(variable) {
|
|
1023
|
+
if (typeof variable === "string") {
|
|
1024
|
+
return `'${variable}'`;
|
|
1025
|
+
} else if (Array.isArray(variable)) {
|
|
1026
|
+
const arrayWithQuotes = variable.map(addQuotesToStrings);
|
|
1027
|
+
return arrayWithQuotes;
|
|
1028
|
+
} else {
|
|
1029
|
+
return variable;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// src/restura/validateRequestParams.ts
|
|
1034
|
+
function validateRequestParams(req, routeData, validationSchema) {
|
|
1035
|
+
const requestData = getRequestData(req);
|
|
1036
|
+
req.data = requestData;
|
|
1037
|
+
if (routeData.request === void 0) {
|
|
1038
|
+
if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
|
|
1039
|
+
throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
|
|
1040
|
+
if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
|
|
1041
|
+
if (!routeData.requestType) throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
|
|
1042
|
+
const currentInterface = validationSchema[routeData.requestType];
|
|
1043
|
+
const validator = new import_jsonschema.default.Validator();
|
|
1044
|
+
const executeValidation = validator.validate(req.data, currentInterface);
|
|
1045
|
+
if (!executeValidation.valid) {
|
|
1046
|
+
throw new RsError(
|
|
1047
|
+
"BAD_REQUEST",
|
|
1048
|
+
`Request custom setup has failed the following check: (${executeValidation.errors})`
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
Object.keys(req.data).forEach((requestParamName) => {
|
|
1054
|
+
const requestParam = routeData.request.find((param) => param.name === requestParamName);
|
|
1055
|
+
if (!requestParam) {
|
|
1056
|
+
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
routeData.request.forEach((requestParam) => {
|
|
1060
|
+
const requestValue = requestData[requestParam.name];
|
|
1061
|
+
if (requestParam.required && requestValue === void 0)
|
|
1062
|
+
throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
|
|
1063
|
+
else if (!requestParam.required && requestValue === void 0) return;
|
|
1064
|
+
validateRequestSingleParam(requestValue, requestParam);
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
function validateRequestSingleParam(requestValue, requestParam) {
|
|
1068
|
+
requestParam.validator.forEach((validator) => {
|
|
1069
|
+
switch (validator.type) {
|
|
1070
|
+
case "TYPE_CHECK":
|
|
1071
|
+
performTypeCheck(requestValue, validator, requestParam.name);
|
|
1072
|
+
break;
|
|
1073
|
+
case "MIN":
|
|
1074
|
+
performMinCheck(requestValue, validator, requestParam.name);
|
|
1075
|
+
break;
|
|
1076
|
+
case "MAX":
|
|
1077
|
+
performMaxCheck(requestValue, validator, requestParam.name);
|
|
1078
|
+
break;
|
|
1079
|
+
case "ONE_OF":
|
|
1080
|
+
performOneOfCheck(requestValue, validator, requestParam.name);
|
|
1081
|
+
break;
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
function isValidType(type, requestValue) {
|
|
1086
|
+
try {
|
|
1087
|
+
expectValidType(type, requestValue);
|
|
1088
|
+
return true;
|
|
1089
|
+
} catch (e) {
|
|
1072
1090
|
return false;
|
|
1073
1091
|
}
|
|
1074
1092
|
}
|
|
1093
|
+
function expectValidType(type, requestValue) {
|
|
1094
|
+
if (type === "number") {
|
|
1095
|
+
return import_zod4.z.number().parse(requestValue);
|
|
1096
|
+
}
|
|
1097
|
+
if (type === "string") {
|
|
1098
|
+
return import_zod4.z.string().parse(requestValue);
|
|
1099
|
+
}
|
|
1100
|
+
if (type === "boolean") {
|
|
1101
|
+
return import_zod4.z.boolean().parse(requestValue);
|
|
1102
|
+
}
|
|
1103
|
+
if (type === "string[]") {
|
|
1104
|
+
return import_zod4.z.array(import_zod4.z.string()).parse(requestValue);
|
|
1105
|
+
}
|
|
1106
|
+
if (type === "number[]") {
|
|
1107
|
+
return import_zod4.z.array(import_zod4.z.number()).parse(requestValue);
|
|
1108
|
+
}
|
|
1109
|
+
if (type === "any[]") {
|
|
1110
|
+
return import_zod4.z.array(import_zod4.z.any()).parse(requestValue);
|
|
1111
|
+
}
|
|
1112
|
+
if (type === "object") {
|
|
1113
|
+
return import_zod4.z.object({}).strict().parse(requestValue);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
function performTypeCheck(requestValue, validator, requestParamName) {
|
|
1117
|
+
if (!isValidType(validator.value, requestValue)) {
|
|
1118
|
+
throw new RsError(
|
|
1119
|
+
"BAD_REQUEST",
|
|
1120
|
+
`Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
try {
|
|
1124
|
+
validatorDataSchemeValue.parse(validator.value);
|
|
1125
|
+
} catch (e) {
|
|
1126
|
+
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
function expectOnlyNumbers(requestValue, validator, requestParamName) {
|
|
1130
|
+
if (!isValueNumber(requestValue))
|
|
1131
|
+
throw new RsError(
|
|
1132
|
+
"BAD_REQUEST",
|
|
1133
|
+
`Request param (${requestParamName}) with value (${requestValue}) is not of type number`
|
|
1134
|
+
);
|
|
1135
|
+
if (!isValueNumber(validator.value))
|
|
1136
|
+
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
|
|
1137
|
+
}
|
|
1138
|
+
function performMinCheck(requestValue, validator, requestParamName) {
|
|
1139
|
+
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
1140
|
+
if (requestValue < validator.value)
|
|
1141
|
+
throw new RsError(
|
|
1142
|
+
"BAD_REQUEST",
|
|
1143
|
+
`Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
function performMaxCheck(requestValue, validator, requestParamName) {
|
|
1147
|
+
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
1148
|
+
if (requestValue > validator.value)
|
|
1149
|
+
throw new RsError(
|
|
1150
|
+
"BAD_REQUEST",
|
|
1151
|
+
`Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
function performOneOfCheck(requestValue, validator, requestParamName) {
|
|
1155
|
+
if (!import_core_utils2.ObjectUtils.isArrayWithData(validator.value))
|
|
1156
|
+
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
|
|
1157
|
+
if (typeof requestValue === "object")
|
|
1158
|
+
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
|
|
1159
|
+
if (!validator.value.includes(requestValue))
|
|
1160
|
+
throw new RsError(
|
|
1161
|
+
"BAD_REQUEST",
|
|
1162
|
+
`Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
function isValueNumber(value) {
|
|
1166
|
+
return !isNaN(Number(value));
|
|
1167
|
+
}
|
|
1168
|
+
function getRequestData(req) {
|
|
1169
|
+
let body = "";
|
|
1170
|
+
if (req.method === "GET" || req.method === "DELETE") {
|
|
1171
|
+
body = "query";
|
|
1172
|
+
} else {
|
|
1173
|
+
body = "body";
|
|
1174
|
+
}
|
|
1175
|
+
const bodyData = req[body];
|
|
1176
|
+
if (bodyData && body === "query") {
|
|
1177
|
+
for (const attr in bodyData) {
|
|
1178
|
+
if (bodyData[attr] instanceof Array) {
|
|
1179
|
+
const attrList = [];
|
|
1180
|
+
for (const value of bodyData[attr]) {
|
|
1181
|
+
if (isNaN(Number(value))) continue;
|
|
1182
|
+
attrList.push(Number(value));
|
|
1183
|
+
}
|
|
1184
|
+
if (import_core_utils2.ObjectUtils.isArrayWithData(attrList)) {
|
|
1185
|
+
bodyData[attr] = attrList;
|
|
1186
|
+
}
|
|
1187
|
+
} else {
|
|
1188
|
+
bodyData[attr] = import_core_utils2.ObjectUtils.safeParse(bodyData[attr]);
|
|
1189
|
+
if (isNaN(Number(bodyData[attr]))) continue;
|
|
1190
|
+
bodyData[attr] = Number(bodyData[attr]);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return bodyData;
|
|
1195
|
+
}
|
|
1075
1196
|
|
|
1076
1197
|
// src/restura/middleware/schemaValidation.ts
|
|
1077
1198
|
async function schemaValidation(req, res, next) {
|
|
@@ -1120,55 +1241,52 @@ function convertTable(table) {
|
|
|
1120
1241
|
return modelString;
|
|
1121
1242
|
}
|
|
1122
1243
|
|
|
1123
|
-
// src/restura/
|
|
1124
|
-
|
|
1125
|
-
return (req, res, next) => {
|
|
1126
|
-
applicationAuthenticateHandler(
|
|
1127
|
-
req,
|
|
1128
|
-
(userDetails) => {
|
|
1129
|
-
req.requesterDetails = __spreadValues(__spreadValues({}, req.requesterDetails), userDetails);
|
|
1130
|
-
next();
|
|
1131
|
-
},
|
|
1132
|
-
(errorMessage) => {
|
|
1133
|
-
res.sendError("UNAUTHORIZED", errorMessage);
|
|
1134
|
-
}
|
|
1135
|
-
);
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1244
|
+
// src/restura/sql/PsqlEngine.ts
|
|
1245
|
+
var import_core_utils5 = require("@redskytech/core-utils");
|
|
1138
1246
|
|
|
1139
|
-
// src/restura/
|
|
1140
|
-
var
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
function
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1247
|
+
// src/restura/sql/PsqlUtils.ts
|
|
1248
|
+
var import_pg_format = __toESM(require("pg-format"));
|
|
1249
|
+
function escapeColumnName(columnName) {
|
|
1250
|
+
if (columnName === void 0) return "";
|
|
1251
|
+
return `"${columnName.replace(/"/g, "")}"`.replace(".", '"."');
|
|
1252
|
+
}
|
|
1253
|
+
function questionMarksToOrderedParams(query) {
|
|
1254
|
+
let count = 1;
|
|
1255
|
+
return query.replace(/'\?'|\?/g, () => `$${count++}`);
|
|
1256
|
+
}
|
|
1257
|
+
function insertObjectQuery(table, obj) {
|
|
1258
|
+
const keys = Object.keys(obj);
|
|
1259
|
+
const params = Object.values(obj);
|
|
1260
|
+
const columns = keys.map((column) => escapeColumnName(column)).join(", ");
|
|
1261
|
+
const values = params.map((value) => SQL`${value}`).join(", ");
|
|
1262
|
+
const query = `INSERT INTO "${table}" (${columns})
|
|
1263
|
+
VALUES (${values})
|
|
1264
|
+
RETURNING *`;
|
|
1265
|
+
return query;
|
|
1266
|
+
}
|
|
1267
|
+
function updateObjectQuery(table, obj, whereStatement) {
|
|
1268
|
+
const setArray = [];
|
|
1269
|
+
for (const i in obj) {
|
|
1270
|
+
setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
|
|
1271
|
+
}
|
|
1272
|
+
return `UPDATE ${escapeColumnName(table)}
|
|
1273
|
+
SET ${setArray.join(", ")} ${whereStatement}
|
|
1274
|
+
RETURNING *`;
|
|
1275
|
+
}
|
|
1276
|
+
function isValueNumber2(value) {
|
|
1277
|
+
return !isNaN(Number(value));
|
|
1278
|
+
}
|
|
1279
|
+
function SQL(strings, ...values) {
|
|
1280
|
+
let query = strings[0];
|
|
1281
|
+
values.forEach((value, index) => {
|
|
1282
|
+
if (isValueNumber2(value)) {
|
|
1283
|
+
query += value;
|
|
1284
|
+
} else {
|
|
1285
|
+
query += import_pg_format.default.literal(value);
|
|
1286
|
+
}
|
|
1287
|
+
query += strings[index + 1];
|
|
1169
1288
|
});
|
|
1170
|
-
|
|
1171
|
-
return schemaObject;
|
|
1289
|
+
return query;
|
|
1172
1290
|
}
|
|
1173
1291
|
|
|
1174
1292
|
// src/restura/sql/SqlEngine.ts
|
|
@@ -1260,7 +1378,10 @@ var SqlEngine = class {
|
|
|
1260
1378
|
param = param.replace("#", "");
|
|
1261
1379
|
const globalParamValue = req.requesterDetails[param];
|
|
1262
1380
|
if (!globalParamValue)
|
|
1263
|
-
throw new RsError(
|
|
1381
|
+
throw new RsError(
|
|
1382
|
+
"SCHEMA_ERROR",
|
|
1383
|
+
`Invalid global keyword clause in route (${routeData.path}) when looking for (#${param})`
|
|
1384
|
+
);
|
|
1264
1385
|
sqlParams.push(globalParamValue);
|
|
1265
1386
|
});
|
|
1266
1387
|
return value.replace(new RegExp(/#[a-zA-Z][a-zA-Z0-9_]+/g), "?");
|
|
@@ -1269,7 +1390,7 @@ var SqlEngine = class {
|
|
|
1269
1390
|
}
|
|
1270
1391
|
};
|
|
1271
1392
|
|
|
1272
|
-
// src/restura/sql/
|
|
1393
|
+
// src/restura/sql/filterPsqlParser.ts
|
|
1273
1394
|
var import_pegjs = __toESM(require("pegjs"));
|
|
1274
1395
|
var filterSqlGrammar = `
|
|
1275
1396
|
start = expressionList
|
|
@@ -1281,7 +1402,7 @@ expressionList =
|
|
|
1281
1402
|
|
|
1282
1403
|
expression =
|
|
1283
1404
|
negate:negate?"(" "column:" column:column ","? value:value? ","? type:type? ")"
|
|
1284
|
-
{return \`\${negate? "!" : ""}(\${type? type(column, value) : \`\${column} = \${
|
|
1405
|
+
{return \`\${negate? "!" : ""}(\${type? type(column, value) : \`\${column} = \${format.literal(value)}\`})\`;}
|
|
1285
1406
|
/
|
|
1286
1407
|
negate:negate?"("expression:expressionList")" { return \`\${negate? "!" : ""}(\${expression})\`; }
|
|
1287
1408
|
|
|
@@ -1289,80 +1410,33 @@ negate = "!"
|
|
|
1289
1410
|
|
|
1290
1411
|
operator = "and"i / "or"i
|
|
1291
1412
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1413
|
+
|
|
1414
|
+
column = left:text "." right:text { return \`\${format.ident(left)}.\${format.ident(right)}\`; }
|
|
1415
|
+
/
|
|
1416
|
+
text:text { return format.ident(text); }
|
|
1417
|
+
|
|
1296
1418
|
|
|
1297
|
-
text = text:[
|
|
1419
|
+
text = text:[a-z0-9-_:@]i+ { return text.join("");}
|
|
1298
1420
|
|
|
1299
1421
|
type = "type:" type:typeString { return type; }
|
|
1300
|
-
typeString = text:"startsWith" { return function(column, value) { return \`\${column}
|
|
1301
|
-
text:"endsWith" { return function(column, value) { return \`\${column}
|
|
1302
|
-
text:"contains" { return function(column, value) { return \`\${column}
|
|
1303
|
-
text:"exact" { return function(column, value) { return \`\${column} = '\${
|
|
1304
|
-
text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${
|
|
1305
|
-
text:"greaterThan" { return function(column, value) { return \`\${column} > '\${
|
|
1306
|
-
text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${
|
|
1307
|
-
text:"lessThan" { return function(column, value) { return \`\${column} < '\${
|
|
1422
|
+
typeString = text:"startsWith" { return function(column, value) { return \`\${column} ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } } /
|
|
1423
|
+
text:"endsWith" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1424
|
+
text:"contains" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } } /
|
|
1425
|
+
text:"exact" { return function(column, value) { return \`\${column} = '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1426
|
+
text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1427
|
+
text:"greaterThan" { return function(column, value) { return \`\${column} > '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1428
|
+
text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1429
|
+
text:"lessThan" { return function(column, value) { return \`\${column} < '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1308
1430
|
text:"isNull" { return function(column, value) { return \`isNull(\${column})\`; } }
|
|
1309
1431
|
|
|
1310
1432
|
value = "value:" value:text { return value; }
|
|
1311
1433
|
|
|
1312
1434
|
`;
|
|
1313
|
-
var
|
|
1314
|
-
format: "commonjs"
|
|
1315
|
-
|
|
1435
|
+
var filterPsqlParser = import_pegjs.default.generate(filterSqlGrammar, {
|
|
1436
|
+
format: "commonjs",
|
|
1437
|
+
dependencies: { format: "pg-format" }
|
|
1316
1438
|
});
|
|
1317
|
-
var
|
|
1318
|
-
|
|
1319
|
-
// src/restura/sql/PsqlEngine.ts
|
|
1320
|
-
var import_core_utils5 = require("@redskytech/core-utils");
|
|
1321
|
-
|
|
1322
|
-
// src/restura/sql/PsqlUtils.ts
|
|
1323
|
-
var import_pg_format = __toESM(require("pg-format"));
|
|
1324
|
-
function escapeColumnName(columnName) {
|
|
1325
|
-
if (columnName === void 0) return "";
|
|
1326
|
-
return `"${columnName.replace(/"/g, "")}"`.replace(".", '"."');
|
|
1327
|
-
}
|
|
1328
|
-
function questionMarksToOrderedParams(query) {
|
|
1329
|
-
let count = 1;
|
|
1330
|
-
return query.replace(/'\?'/g, () => `$${count++}`);
|
|
1331
|
-
}
|
|
1332
|
-
function insertObjectQuery(table, obj) {
|
|
1333
|
-
const keys = Object.keys(obj);
|
|
1334
|
-
const params = Object.values(obj);
|
|
1335
|
-
const columns = keys.map((column) => escapeColumnName(column)).join(", ");
|
|
1336
|
-
const values = params.map((value) => SQL`${value}`).join(", ");
|
|
1337
|
-
const query = `INSERT INTO "${table}" (${columns})
|
|
1338
|
-
VALUES (${values})
|
|
1339
|
-
RETURNING *`;
|
|
1340
|
-
return query;
|
|
1341
|
-
}
|
|
1342
|
-
function updateObjectQuery(table, obj, whereStatement) {
|
|
1343
|
-
const setArray = [];
|
|
1344
|
-
for (const i in obj) {
|
|
1345
|
-
setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
|
|
1346
|
-
}
|
|
1347
|
-
return `UPDATE ${escapeColumnName(table)}
|
|
1348
|
-
SET ${setArray.join(", ")} ${whereStatement}
|
|
1349
|
-
RETURNING *`;
|
|
1350
|
-
}
|
|
1351
|
-
function isValueNumber2(value) {
|
|
1352
|
-
return !isNaN(Number(value));
|
|
1353
|
-
}
|
|
1354
|
-
function SQL(strings, ...values) {
|
|
1355
|
-
let query = strings[0];
|
|
1356
|
-
values.forEach((value, index) => {
|
|
1357
|
-
if (isValueNumber2(value)) {
|
|
1358
|
-
query += value;
|
|
1359
|
-
} else {
|
|
1360
|
-
query += import_pg_format.default.literal(value);
|
|
1361
|
-
}
|
|
1362
|
-
query += strings[index + 1];
|
|
1363
|
-
});
|
|
1364
|
-
return query;
|
|
1365
|
-
}
|
|
1439
|
+
var filterPsqlParser_default = filterPsqlParser;
|
|
1366
1440
|
|
|
1367
1441
|
// src/restura/sql/PsqlEngine.ts
|
|
1368
1442
|
var PsqlEngine = class extends SqlEngine {
|
|
@@ -1379,8 +1453,45 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1379
1453
|
return "";
|
|
1380
1454
|
}
|
|
1381
1455
|
createNestedSelect(req, schema, item, routeData, userRole, sqlParams) {
|
|
1382
|
-
|
|
1383
|
-
|
|
1456
|
+
if (!item.subquery) return "";
|
|
1457
|
+
if (!import_core_utils5.ObjectUtils.isArrayWithData(
|
|
1458
|
+
item.subquery.properties.filter((nestedItem) => {
|
|
1459
|
+
return this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
|
|
1460
|
+
...routeData.joins,
|
|
1461
|
+
...item.subquery.joins
|
|
1462
|
+
]);
|
|
1463
|
+
})
|
|
1464
|
+
)) {
|
|
1465
|
+
return "'[]'";
|
|
1466
|
+
}
|
|
1467
|
+
return `COALESCE((
|
|
1468
|
+
SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
1469
|
+
${item.subquery.properties.map((nestedItem) => {
|
|
1470
|
+
if (!this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
|
|
1471
|
+
...routeData.joins,
|
|
1472
|
+
...item.subquery.joins
|
|
1473
|
+
])) {
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (nestedItem.subquery) {
|
|
1477
|
+
return `"${nestedItem.name}", ${this.createNestedSelect(
|
|
1478
|
+
// recursion
|
|
1479
|
+
req,
|
|
1480
|
+
schema,
|
|
1481
|
+
nestedItem,
|
|
1482
|
+
routeData,
|
|
1483
|
+
userRole,
|
|
1484
|
+
sqlParams
|
|
1485
|
+
)}`;
|
|
1486
|
+
}
|
|
1487
|
+
return `'${nestedItem.name}', ${escapeColumnName(nestedItem.selector)}`;
|
|
1488
|
+
}).filter(Boolean).join(",")}
|
|
1489
|
+
))
|
|
1490
|
+
FROM
|
|
1491
|
+
"${item.subquery.table}"
|
|
1492
|
+
${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, userRole, sqlParams)}
|
|
1493
|
+
${this.generateWhereClause(req, item.subquery.where, routeData, sqlParams)}
|
|
1494
|
+
), '[]')`;
|
|
1384
1495
|
}
|
|
1385
1496
|
async executeCreateRequest(req, routeData, schema) {
|
|
1386
1497
|
const sqlParams = [];
|
|
@@ -1389,7 +1500,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1389
1500
|
parameterObj[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
|
|
1390
1501
|
});
|
|
1391
1502
|
const query = insertObjectQuery(routeData.table, __spreadValues(__spreadValues({}, req.data), parameterObj));
|
|
1392
|
-
const createdItem = await this.psqlConnectionPool.queryOne(query, sqlParams);
|
|
1503
|
+
const createdItem = await this.psqlConnectionPool.queryOne(query, sqlParams, req.requesterDetails);
|
|
1393
1504
|
const insertId = createdItem == null ? void 0 : createdItem.id;
|
|
1394
1505
|
const whereData = [
|
|
1395
1506
|
{
|
|
@@ -1439,12 +1550,14 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1439
1550
|
if (routeData.type === "ONE") {
|
|
1440
1551
|
return this.psqlConnectionPool.queryOne(
|
|
1441
1552
|
`${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
|
|
1442
|
-
sqlParams
|
|
1553
|
+
sqlParams,
|
|
1554
|
+
req.requesterDetails
|
|
1443
1555
|
);
|
|
1444
1556
|
} else if (routeData.type === "ARRAY") {
|
|
1445
1557
|
return this.psqlConnectionPool.runQuery(
|
|
1446
1558
|
`${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
|
|
1447
|
-
sqlParams
|
|
1559
|
+
sqlParams,
|
|
1560
|
+
req.requesterDetails
|
|
1448
1561
|
);
|
|
1449
1562
|
} else if (routeData.type === "PAGED") {
|
|
1450
1563
|
const data = req.data;
|
|
@@ -1456,7 +1569,8 @@ ${sqlStatement};`,
|
|
|
1456
1569
|
data.perPage || DEFAULT_PAGED_PER_PAGE_NUMBER,
|
|
1457
1570
|
(data.page - 1) * data.perPage || DEFAULT_PAGED_PAGE_NUMBER,
|
|
1458
1571
|
...sqlParams
|
|
1459
|
-
]
|
|
1572
|
+
],
|
|
1573
|
+
req.requesterDetails
|
|
1460
1574
|
);
|
|
1461
1575
|
let total = 0;
|
|
1462
1576
|
if (import_core_utils5.ObjectUtils.isArrayWithData(pageResults)) {
|
|
@@ -1487,7 +1601,7 @@ ${sqlStatement};`,
|
|
|
1487
1601
|
}
|
|
1488
1602
|
const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
1489
1603
|
const query = updateObjectQuery(routeData.table, bodyNoId, whereClause);
|
|
1490
|
-
await this.psqlConnectionPool.queryOne(query, [...sqlParams]);
|
|
1604
|
+
await this.psqlConnectionPool.queryOne(query, [...sqlParams], req.requesterDetails);
|
|
1491
1605
|
return this.executeGetRequest(req, routeData, schema);
|
|
1492
1606
|
}
|
|
1493
1607
|
async executeDeleteRequest(req, routeData, schema) {
|
|
@@ -1505,16 +1619,26 @@ ${sqlStatement};`,
|
|
|
1505
1619
|
FROM "${routeData.table}" ${joinStatement}`;
|
|
1506
1620
|
deleteStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
1507
1621
|
deleteStatement += ";";
|
|
1508
|
-
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams);
|
|
1622
|
+
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
|
|
1509
1623
|
return true;
|
|
1510
1624
|
}
|
|
1511
1625
|
generateJoinStatements(req, joins, baseTable, routeData, schema, userRole, sqlParams) {
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1626
|
+
let joinStatements = "";
|
|
1627
|
+
joins.forEach((item) => {
|
|
1628
|
+
if (!this.doesRoleHavePermissionToTable(userRole, schema, item.table))
|
|
1629
|
+
throw new RsError("UNAUTHORIZED", "You do not have permission to access this table");
|
|
1630
|
+
if (item.custom) {
|
|
1631
|
+
const customReplaced = this.replaceParamKeywords(item.custom, routeData, req, sqlParams);
|
|
1632
|
+
joinStatements += ` ${item.type} JOIN ${escapeColumnName(item.table)} ON ${customReplaced}
|
|
1633
|
+
`;
|
|
1634
|
+
} else {
|
|
1635
|
+
joinStatements += ` ${item.type} JOIN ${escapeColumnName(item.table)}${item.alias ? `AS "${item.alias}"` : ""} ON "${baseTable}"."${item.localColumnName}" = ${escapeColumnName(item.alias ? item.alias : item.table)}.${escapeColumnName(
|
|
1636
|
+
item.foreignColumnName
|
|
1637
|
+
)}
|
|
1638
|
+
`;
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
return joinStatements;
|
|
1518
1642
|
}
|
|
1519
1643
|
generateGroupBy(routeData) {
|
|
1520
1644
|
let groupBy = "";
|
|
@@ -1573,22 +1697,24 @@ ${sqlStatement};`,
|
|
|
1573
1697
|
const data = req.data;
|
|
1574
1698
|
if (routeData.type === "PAGED" && !!(data == null ? void 0 : data.filter)) {
|
|
1575
1699
|
let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
1700
|
+
var _a;
|
|
1576
1701
|
const requestParam = routeData.request.find((item) => {
|
|
1577
1702
|
return item.name === value.replace("$", "");
|
|
1578
1703
|
});
|
|
1579
1704
|
if (!requestParam)
|
|
1580
1705
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
1581
|
-
return data[requestParam.name];
|
|
1706
|
+
return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
|
|
1582
1707
|
});
|
|
1583
1708
|
statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
1709
|
+
var _a;
|
|
1584
1710
|
const requestParam = routeData.request.find((item) => {
|
|
1585
1711
|
return item.name === value.replace("#", "");
|
|
1586
1712
|
});
|
|
1587
1713
|
if (!requestParam)
|
|
1588
1714
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
1589
|
-
return data[requestParam.name];
|
|
1715
|
+
return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
|
|
1590
1716
|
});
|
|
1591
|
-
statement =
|
|
1717
|
+
statement = filterPsqlParser_default.parse(statement);
|
|
1592
1718
|
if (whereClause.startsWith("WHERE")) {
|
|
1593
1719
|
whereClause += ` AND (${statement})
|
|
1594
1720
|
`;
|
|
@@ -1601,24 +1727,33 @@ ${sqlStatement};`,
|
|
|
1601
1727
|
}
|
|
1602
1728
|
};
|
|
1603
1729
|
|
|
1604
|
-
// src/restura/restura.ts
|
|
1605
|
-
var import_pg2 = require("pg");
|
|
1606
|
-
|
|
1607
1730
|
// src/restura/sql/PsqlPool.ts
|
|
1608
|
-
var import_pg = require("pg");
|
|
1731
|
+
var import_pg = __toESM(require("pg"));
|
|
1732
|
+
var import_pg_format2 = __toESM(require("pg-format"));
|
|
1733
|
+
var { Pool } = import_pg.default;
|
|
1609
1734
|
var PsqlPool = class {
|
|
1610
1735
|
constructor(poolConfig) {
|
|
1611
1736
|
this.poolConfig = poolConfig;
|
|
1612
|
-
this.pool = new
|
|
1737
|
+
this.pool = new Pool(poolConfig);
|
|
1738
|
+
this.queryOne("SELECT NOW();", [], { isSystemUser: true, role: "", host: "localhost", ipAddress: "" }).then(() => {
|
|
1739
|
+
logger.info("Connected to PostgreSQL database");
|
|
1740
|
+
}).catch((error) => {
|
|
1741
|
+
logger.error("Error connecting to database", error);
|
|
1742
|
+
process.exit(1);
|
|
1743
|
+
});
|
|
1613
1744
|
}
|
|
1614
1745
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1615
|
-
async queryOne(query, options) {
|
|
1746
|
+
async queryOne(query, options, requesterDetails) {
|
|
1616
1747
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1748
|
+
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1617
1749
|
try {
|
|
1618
1750
|
const response = await this.pool.query(formattedQuery, options);
|
|
1751
|
+
if (response.rows.length === 0) throw new RsError("NOT_FOUND", "No results found");
|
|
1752
|
+
else if (response.rows.length > 1) throw new RsError("DUPLICATE", "More than one result found");
|
|
1619
1753
|
return response.rows[0];
|
|
1620
1754
|
} catch (error) {
|
|
1621
1755
|
console.error(error, query, options);
|
|
1756
|
+
if (RsError.isRsError(error)) throw error;
|
|
1622
1757
|
if ((error == null ? void 0 : error.routine) === "_bt_check_unique") {
|
|
1623
1758
|
throw new RsError("DUPLICATE", error.message);
|
|
1624
1759
|
}
|
|
@@ -1626,8 +1761,9 @@ var PsqlPool = class {
|
|
|
1626
1761
|
}
|
|
1627
1762
|
}
|
|
1628
1763
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1629
|
-
async runQuery(query, options) {
|
|
1764
|
+
async runQuery(query, options, requesterDetails) {
|
|
1630
1765
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1766
|
+
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1631
1767
|
const queryUpdated = query.replace(/[\t\n]/g, " ");
|
|
1632
1768
|
console.log(queryUpdated, options);
|
|
1633
1769
|
try {
|
|
@@ -1641,9 +1777,30 @@ var PsqlPool = class {
|
|
|
1641
1777
|
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
1642
1778
|
}
|
|
1643
1779
|
}
|
|
1780
|
+
logSqlStatement(query, options, requesterDetails, prefix = "") {
|
|
1781
|
+
if (logger.level !== "silly") return;
|
|
1782
|
+
let sqlStatement = "";
|
|
1783
|
+
if (options.length === 0) {
|
|
1784
|
+
sqlStatement = query;
|
|
1785
|
+
} else {
|
|
1786
|
+
let stringIndex = 0;
|
|
1787
|
+
sqlStatement = query.replace(/\$\d+/g, () => {
|
|
1788
|
+
const value = options[stringIndex++];
|
|
1789
|
+
if (typeof value === "number") return value.toString();
|
|
1790
|
+
return import_pg_format2.default.literal(value);
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
let initiator = "Anonymous";
|
|
1794
|
+
if ("userId" in requesterDetails && requesterDetails.userId)
|
|
1795
|
+
initiator = `User Id (${requesterDetails.userId.toString()})`;
|
|
1796
|
+
if ("isSystemUser" in requesterDetails && requesterDetails.isSystemUser) initiator = "SYSTEM";
|
|
1797
|
+
logger.silly(`${prefix}query by ${initiator}, Query ->
|
|
1798
|
+
${sqlStatement}`);
|
|
1799
|
+
}
|
|
1644
1800
|
};
|
|
1645
1801
|
|
|
1646
1802
|
// src/restura/restura.ts
|
|
1803
|
+
var { types } = import_pg2.default;
|
|
1647
1804
|
var ResturaEngine = class {
|
|
1648
1805
|
constructor() {
|
|
1649
1806
|
this.publicEndpoints = {
|
|
@@ -1661,10 +1818,11 @@ var ResturaEngine = class {
|
|
|
1661
1818
|
* @returns A promise that resolves when the initialization is complete.
|
|
1662
1819
|
*/
|
|
1663
1820
|
async init(app, authenticationHandler, psqlConnectionPool) {
|
|
1821
|
+
this.resturaConfig = import_internal2.config.validate("restura", resturaConfigSchema);
|
|
1664
1822
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
1665
1823
|
this.psqlEngine = new PsqlEngine(this.psqlConnectionPool);
|
|
1666
1824
|
setupPgReturnTypes();
|
|
1667
|
-
this.resturaConfig
|
|
1825
|
+
await customApiFactory_default.loadApiFiles(this.resturaConfig.customApiFolderPath);
|
|
1668
1826
|
this.authenticationHandler = authenticationHandler;
|
|
1669
1827
|
app.use((0, import_compression.default)());
|
|
1670
1828
|
app.use(import_body_parser.default.json({ limit: "32mb" }));
|
|
@@ -1727,7 +1885,7 @@ var ResturaEngine = class {
|
|
|
1727
1885
|
* @returns A promise that resolves when the API has been successfully generated and written to the output file.
|
|
1728
1886
|
*/
|
|
1729
1887
|
async generateApiFromSchema(outputFile, providedSchema) {
|
|
1730
|
-
|
|
1888
|
+
import_fs3.default.writeFileSync(
|
|
1731
1889
|
outputFile,
|
|
1732
1890
|
await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
|
|
1733
1891
|
);
|
|
@@ -1740,7 +1898,7 @@ var ResturaEngine = class {
|
|
|
1740
1898
|
* @returns A promise that resolves when the model has been successfully written to the output file.
|
|
1741
1899
|
*/
|
|
1742
1900
|
async generateModelFromSchema(outputFile, providedSchema) {
|
|
1743
|
-
|
|
1901
|
+
import_fs3.default.writeFileSync(
|
|
1744
1902
|
outputFile,
|
|
1745
1903
|
await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
|
|
1746
1904
|
);
|
|
@@ -1752,11 +1910,11 @@ var ResturaEngine = class {
|
|
|
1752
1910
|
* @throws {Error} If the schema file is missing or the schema is not valid.
|
|
1753
1911
|
*/
|
|
1754
1912
|
async getLatestFileSystemSchema() {
|
|
1755
|
-
if (!
|
|
1913
|
+
if (!import_fs3.default.existsSync(this.resturaConfig.schemaFilePath)) {
|
|
1756
1914
|
logger.error(`Missing restura schema file, expected path: ${this.resturaConfig.schemaFilePath}`);
|
|
1757
1915
|
throw new Error("Missing restura schema file");
|
|
1758
1916
|
}
|
|
1759
|
-
const schemaFileData =
|
|
1917
|
+
const schemaFileData = import_fs3.default.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
|
|
1760
1918
|
const schema = import_core_utils6.ObjectUtils.safeParse(schemaFileData);
|
|
1761
1919
|
const isValid = await isSchemaValid(schema);
|
|
1762
1920
|
if (!isValid) {
|
|
@@ -1777,9 +1935,9 @@ var ResturaEngine = class {
|
|
|
1777
1935
|
async getHashes(providedSchema) {
|
|
1778
1936
|
var _a, _b, _c, _d;
|
|
1779
1937
|
const schemaHash = await this.generateHashForSchema(providedSchema);
|
|
1780
|
-
const apiFile =
|
|
1938
|
+
const apiFile = import_fs3.default.readFileSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
|
|
1781
1939
|
const apiCreatedSchemaHash = (_b = (_a = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a[1]) != null ? _b : "";
|
|
1782
|
-
const modelFile =
|
|
1940
|
+
const modelFile = import_fs3.default.readFileSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
|
|
1783
1941
|
const modelCreatedSchemaHash = (_d = (_c = modelFile.toString().match(/\((.*)\)/)) == null ? void 0 : _c[1]) != null ? _d : "";
|
|
1784
1942
|
return {
|
|
1785
1943
|
schemaHash,
|
|
@@ -1815,27 +1973,27 @@ var ResturaEngine = class {
|
|
|
1815
1973
|
logger.info(`Restura loaded (${routeCount}) endpoint${routeCount > 1 ? "s" : ""}`);
|
|
1816
1974
|
}
|
|
1817
1975
|
async validateGeneratedTypesFolder() {
|
|
1818
|
-
if (!
|
|
1819
|
-
|
|
1976
|
+
if (!import_fs3.default.existsSync(this.resturaConfig.generatedTypesPath)) {
|
|
1977
|
+
import_fs3.default.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
|
|
1820
1978
|
}
|
|
1821
|
-
const hasApiFile =
|
|
1822
|
-
const hasModelsFile =
|
|
1979
|
+
const hasApiFile = import_fs3.default.existsSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
|
|
1980
|
+
const hasModelsFile = import_fs3.default.existsSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
|
|
1823
1981
|
if (!hasApiFile) {
|
|
1824
|
-
await this.generateApiFromSchema(
|
|
1982
|
+
await this.generateApiFromSchema(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1825
1983
|
}
|
|
1826
1984
|
if (!hasModelsFile) {
|
|
1827
1985
|
await this.generateModelFromSchema(
|
|
1828
|
-
|
|
1986
|
+
import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1829
1987
|
this.schema
|
|
1830
1988
|
);
|
|
1831
1989
|
}
|
|
1832
1990
|
const hashes = await this.getHashes(this.schema);
|
|
1833
1991
|
if (hashes.schemaHash !== hashes.apiCreatedSchemaHash) {
|
|
1834
|
-
await this.generateApiFromSchema(
|
|
1992
|
+
await this.generateApiFromSchema(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1835
1993
|
}
|
|
1836
1994
|
if (hashes.schemaHash !== hashes.modelCreatedSchemaHash) {
|
|
1837
1995
|
await this.generateModelFromSchema(
|
|
1838
|
-
|
|
1996
|
+
import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1839
1997
|
this.schema
|
|
1840
1998
|
);
|
|
1841
1999
|
}
|
|
@@ -1865,9 +2023,9 @@ var ResturaEngine = class {
|
|
|
1865
2023
|
}
|
|
1866
2024
|
}
|
|
1867
2025
|
async updateTypes() {
|
|
1868
|
-
await this.generateApiFromSchema(
|
|
2026
|
+
await this.generateApiFromSchema(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1869
2027
|
await this.generateModelFromSchema(
|
|
1870
|
-
|
|
2028
|
+
import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1871
2029
|
this.schema
|
|
1872
2030
|
);
|
|
1873
2031
|
}
|
|
@@ -1890,11 +2048,16 @@ var ResturaEngine = class {
|
|
|
1890
2048
|
const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
|
|
1891
2049
|
this.validateAuthorization(req, routeData);
|
|
1892
2050
|
validateRequestParams(req, routeData, this.customTypeValidation);
|
|
2051
|
+
if (this.isCustomRoute(routeData)) {
|
|
2052
|
+
await this.runCustomRouteLogic(req, res, routeData);
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
1893
2055
|
const data = await this.psqlEngine.runQueryForRoute(
|
|
1894
2056
|
req,
|
|
1895
2057
|
routeData,
|
|
1896
2058
|
this.schema
|
|
1897
2059
|
);
|
|
2060
|
+
this.responseValidator.validateResponseParams(data, req.baseUrl, routeData);
|
|
1898
2061
|
if (routeData.type === "PAGED") res.sendNoWrap(data);
|
|
1899
2062
|
else res.sendData(data);
|
|
1900
2063
|
} catch (e) {
|
|
@@ -1904,34 +2067,25 @@ var ResturaEngine = class {
|
|
|
1904
2067
|
isCustomRoute(route) {
|
|
1905
2068
|
return route.type === "CUSTOM_ONE" || route.type === "CUSTOM_ARRAY" || route.type === "CUSTOM_PAGED";
|
|
1906
2069
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
// // @ts-expect-error - Here we are dynamically calling the function from a custom class, not sure how to typescript this
|
|
1927
|
-
// const customFunction = customApi[functionName] as (
|
|
1928
|
-
// req: RsRequest<T>,
|
|
1929
|
-
// res: RsResponse<T>,
|
|
1930
|
-
// routeData: RouteData
|
|
1931
|
-
// ) => Promise<void>;
|
|
1932
|
-
// if (!customFunction) throw new RsError('NOT_FOUND', `API path ${routeData.path} not implemented`);
|
|
1933
|
-
// await customFunction(req, res, routeData);
|
|
1934
|
-
// }
|
|
2070
|
+
async runCustomRouteLogic(req, res, routeData) {
|
|
2071
|
+
const version = req.baseUrl.split("/")[2];
|
|
2072
|
+
let domain = routeData.path.split("/")[1];
|
|
2073
|
+
domain = domain.split("-").reduce((acc, value, index) => {
|
|
2074
|
+
if (index === 0) acc = value;
|
|
2075
|
+
else acc += import_core_utils6.StringUtils.capitalizeFirst(value);
|
|
2076
|
+
return acc;
|
|
2077
|
+
}, "");
|
|
2078
|
+
const customApiName = `${import_core_utils6.StringUtils.capitalizeFirst(domain)}Api${import_core_utils6.StringUtils.capitalizeFirst(version)}`;
|
|
2079
|
+
const customApi = customApiFactory_default.getCustomApi(customApiName);
|
|
2080
|
+
if (!customApi) throw new RsError("NOT_FOUND", `API domain ${domain}-${version} not found`);
|
|
2081
|
+
const functionName = `${routeData.method.toLowerCase()}${routeData.path.replace(new RegExp("-", "g"), "/").split("/").reduce((acc, cur) => {
|
|
2082
|
+
if (cur === "") return acc;
|
|
2083
|
+
return acc + import_core_utils6.StringUtils.capitalizeFirst(cur);
|
|
2084
|
+
}, "")}`;
|
|
2085
|
+
const customFunction = customApi[functionName];
|
|
2086
|
+
if (!customFunction) throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented`);
|
|
2087
|
+
await customFunction(req, res, routeData);
|
|
2088
|
+
}
|
|
1935
2089
|
async generateHashForSchema(providedSchema) {
|
|
1936
2090
|
const schemaPrettyStr = await prettier3.format(JSON.stringify(providedSchema), __spreadValues({
|
|
1937
2091
|
parser: "json"
|
|
@@ -1956,7 +2110,7 @@ var ResturaEngine = class {
|
|
|
1956
2110
|
printWidth: 120,
|
|
1957
2111
|
singleQuote: true
|
|
1958
2112
|
}));
|
|
1959
|
-
|
|
2113
|
+
import_fs3.default.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
|
|
1960
2114
|
}
|
|
1961
2115
|
resetPublicEndpoints() {
|
|
1962
2116
|
this.publicEndpoints = {
|
|
@@ -1973,13 +2127,13 @@ var ResturaEngine = class {
|
|
|
1973
2127
|
if (!routeData.roles.includes(role))
|
|
1974
2128
|
throw new RsError("UNAUTHORIZED", "Not authorized to access this endpoint");
|
|
1975
2129
|
}
|
|
1976
|
-
getRouteData(method, baseUrl,
|
|
2130
|
+
getRouteData(method, baseUrl, path4) {
|
|
1977
2131
|
const endpoint = this.schema.endpoints.find((item) => {
|
|
1978
2132
|
return item.baseUrl === baseUrl;
|
|
1979
2133
|
});
|
|
1980
2134
|
if (!endpoint) throw new RsError("NOT_FOUND", "Route not found");
|
|
1981
2135
|
const route = endpoint.routes.find((item) => {
|
|
1982
|
-
return item.method === method && item.path ===
|
|
2136
|
+
return item.method === method && item.path === path4;
|
|
1983
2137
|
});
|
|
1984
2138
|
if (!route) throw new RsError("NOT_FOUND", "Route not found");
|
|
1985
2139
|
return route;
|
|
@@ -2006,13 +2160,16 @@ __decorateClass([
|
|
|
2006
2160
|
__decorateClass([
|
|
2007
2161
|
boundMethod
|
|
2008
2162
|
], ResturaEngine.prototype, "isCustomRoute", 1);
|
|
2163
|
+
__decorateClass([
|
|
2164
|
+
boundMethod
|
|
2165
|
+
], ResturaEngine.prototype, "runCustomRouteLogic", 1);
|
|
2009
2166
|
var setupPgReturnTypes = () => {
|
|
2010
2167
|
const TIMESTAMPTZ_OID = 1184;
|
|
2011
|
-
|
|
2168
|
+
types.setTypeParser(TIMESTAMPTZ_OID, (val) => {
|
|
2012
2169
|
return val === null ? null : new Date(val).toISOString();
|
|
2013
2170
|
});
|
|
2014
2171
|
const BIGINT_OID = 20;
|
|
2015
|
-
|
|
2172
|
+
types.setTypeParser(BIGINT_OID, (val) => {
|
|
2016
2173
|
return val === null ? null : Number(val);
|
|
2017
2174
|
});
|
|
2018
2175
|
};
|
|
@@ -2020,8 +2177,16 @@ setupPgReturnTypes();
|
|
|
2020
2177
|
var restura = new ResturaEngine();
|
|
2021
2178
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2022
2179
|
0 && (module.exports = {
|
|
2180
|
+
HtmlStatusCodes,
|
|
2023
2181
|
PsqlPool,
|
|
2182
|
+
RsError,
|
|
2183
|
+
SQL,
|
|
2184
|
+
escapeColumnName,
|
|
2185
|
+
insertObjectQuery,
|
|
2186
|
+
isValueNumber,
|
|
2024
2187
|
logger,
|
|
2025
|
-
|
|
2188
|
+
questionMarksToOrderedParams,
|
|
2189
|
+
restura,
|
|
2190
|
+
updateObjectQuery
|
|
2026
2191
|
});
|
|
2027
2192
|
//# sourceMappingURL=index.js.map
|