@restura/core 0.1.0-alpha.7 → 0.1.0-alpha.8
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 +58 -24
- package/dist/index.d.ts +58 -24
- package/dist/index.js +850 -148
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +849 -148
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
3
5
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
7
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -15,6 +17,19 @@ var __spreadValues = (a, b) => {
|
|
|
15
17
|
}
|
|
16
18
|
return a;
|
|
17
19
|
};
|
|
20
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
+
var __objRest = (source, exclude) => {
|
|
22
|
+
var target = {};
|
|
23
|
+
for (var prop in source)
|
|
24
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
25
|
+
target[prop] = source[prop];
|
|
26
|
+
if (source != null && __getOwnPropSymbols)
|
|
27
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
28
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
29
|
+
target[prop] = source[prop];
|
|
30
|
+
}
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
18
33
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
19
34
|
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
20
35
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
@@ -77,7 +92,7 @@ var logger = winston.createLogger({
|
|
|
77
92
|
});
|
|
78
93
|
|
|
79
94
|
// src/restura/restura.ts
|
|
80
|
-
import { ObjectUtils as
|
|
95
|
+
import { ObjectUtils as ObjectUtils5 } from "@redskytech/core-utils";
|
|
81
96
|
import { config as config2 } from "@restura/internal";
|
|
82
97
|
|
|
83
98
|
// ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
|
|
@@ -132,8 +147,8 @@ import compression from "compression";
|
|
|
132
147
|
import cookieParser from "cookie-parser";
|
|
133
148
|
import { createHash } from "crypto";
|
|
134
149
|
import * as express from "express";
|
|
135
|
-
import
|
|
136
|
-
import
|
|
150
|
+
import fs2 from "fs";
|
|
151
|
+
import path2 from "path";
|
|
137
152
|
import * as prettier3 from "prettier";
|
|
138
153
|
|
|
139
154
|
// src/restura/errors.ts
|
|
@@ -185,7 +200,7 @@ var htmlStatusMap = {
|
|
|
185
200
|
SCHEMA_ERROR: 500 /* SERVER_ERROR */
|
|
186
201
|
};
|
|
187
202
|
|
|
188
|
-
// src/restura/SqlUtils.ts
|
|
203
|
+
// src/restura/sql/SqlUtils.ts
|
|
189
204
|
var SqlUtils = class _SqlUtils {
|
|
190
205
|
static convertDatabaseTypeToTypescript(type, value) {
|
|
191
206
|
type = type.toLocaleLowerCase();
|
|
@@ -276,9 +291,9 @@ var ResponseValidator = class _ResponseValidator {
|
|
|
276
291
|
return { validator: "any" };
|
|
277
292
|
}
|
|
278
293
|
getTypeFromTable(selector, name) {
|
|
279
|
-
const
|
|
280
|
-
if (
|
|
281
|
-
const tableName =
|
|
294
|
+
const path3 = selector.split(".");
|
|
295
|
+
if (path3.length === 0 || path3.length > 2 || path3[0] === "") return { validator: "any", isOptional: false };
|
|
296
|
+
const tableName = path3.length == 2 ? path3[0] : name, columnName = path3.length == 2 ? path3[1] : path3[0];
|
|
282
297
|
const table = this.database.find((t) => t.name == tableName);
|
|
283
298
|
const column = table == null ? void 0 : table.columns.find((c) => c.name == columnName);
|
|
284
299
|
if (!table || !column) return { validator: "any", isOptional: false };
|
|
@@ -494,10 +509,10 @@ var ApiTree = class _ApiTree {
|
|
|
494
509
|
return `${p.name}${optional ? "?" : ""}:${responseType}${array ? "[]" : ""}`;
|
|
495
510
|
}
|
|
496
511
|
getTypeFromTable(selector, name) {
|
|
497
|
-
const
|
|
498
|
-
if (
|
|
499
|
-
let tableName =
|
|
500
|
-
const columnName =
|
|
512
|
+
const path3 = selector.split(".");
|
|
513
|
+
if (path3.length === 0 || path3.length > 2 || path3[0] === "") return { responseType: "any", optional: false };
|
|
514
|
+
let tableName = path3.length == 2 ? path3[0] : name;
|
|
515
|
+
const columnName = path3.length == 2 ? path3[1] : path3[0];
|
|
501
516
|
let table = this.database.find((t) => t.name == tableName);
|
|
502
517
|
if (!table && tableName.includes("_")) {
|
|
503
518
|
const tableAliasSplit = tableName.split("_");
|
|
@@ -512,8 +527,8 @@ var ApiTree = class _ApiTree {
|
|
|
512
527
|
};
|
|
513
528
|
}
|
|
514
529
|
};
|
|
515
|
-
function pathToNamespaces(
|
|
516
|
-
return
|
|
530
|
+
function pathToNamespaces(path3) {
|
|
531
|
+
return path3.split("/").map((e) => StringUtils.toPascalCasing(e)).filter((e) => e);
|
|
517
532
|
}
|
|
518
533
|
function apiGenerator(schema, schemaHash) {
|
|
519
534
|
let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
|
|
@@ -577,6 +592,163 @@ function addApiResponseFunctions(req, res, next) {
|
|
|
577
592
|
// src/restura/validateRequestParams.ts
|
|
578
593
|
import { ObjectUtils as ObjectUtils2 } from "@redskytech/core-utils";
|
|
579
594
|
import jsonschema from "jsonschema";
|
|
595
|
+
import { z as z3 } from "zod";
|
|
596
|
+
|
|
597
|
+
// src/restura/types/validation.types.ts
|
|
598
|
+
import { z as z2 } from "zod";
|
|
599
|
+
var validatorDataSchemeValue = z2.union([z2.string(), z2.array(z2.string()), z2.number(), z2.array(z2.number())]);
|
|
600
|
+
var validatorDataSchema = z2.object({
|
|
601
|
+
type: z2.enum(["TYPE_CHECK", "MIN", "MAX", "ONE_OF"]),
|
|
602
|
+
value: validatorDataSchemeValue
|
|
603
|
+
}).strict();
|
|
604
|
+
|
|
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
|
+
}
|
|
580
752
|
function getRequestData(req) {
|
|
581
753
|
let body = "";
|
|
582
754
|
if (req.method === "GET" || req.method === "DELETE") {
|
|
@@ -611,84 +783,83 @@ function getRequestData(req) {
|
|
|
611
783
|
}
|
|
612
784
|
|
|
613
785
|
// src/restura/restura.schema.ts
|
|
614
|
-
import { z as
|
|
615
|
-
var orderBySchema =
|
|
616
|
-
columnName:
|
|
617
|
-
order:
|
|
618
|
-
tableName:
|
|
786
|
+
import { z as z4 } from "zod";
|
|
787
|
+
var orderBySchema = z4.object({
|
|
788
|
+
columnName: z4.string(),
|
|
789
|
+
order: z4.enum(["ASC", "DESC"]),
|
|
790
|
+
tableName: z4.string()
|
|
619
791
|
}).strict();
|
|
620
|
-
var groupBySchema =
|
|
621
|
-
columnName:
|
|
622
|
-
tableName:
|
|
792
|
+
var groupBySchema = z4.object({
|
|
793
|
+
columnName: z4.string(),
|
|
794
|
+
tableName: z4.string()
|
|
623
795
|
}).strict();
|
|
624
|
-
var whereDataSchema =
|
|
625
|
-
tableName:
|
|
626
|
-
columnName:
|
|
627
|
-
operator:
|
|
628
|
-
value:
|
|
629
|
-
custom:
|
|
630
|
-
conjunction:
|
|
796
|
+
var whereDataSchema = z4.object({
|
|
797
|
+
tableName: z4.string().optional(),
|
|
798
|
+
columnName: z4.string().optional(),
|
|
799
|
+
operator: z4.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH"]).optional(),
|
|
800
|
+
value: z4.string().or(z4.number()).optional(),
|
|
801
|
+
custom: z4.string().optional(),
|
|
802
|
+
conjunction: z4.enum(["AND", "OR"]).optional()
|
|
631
803
|
}).strict();
|
|
632
|
-
var assignmentDataSchema =
|
|
633
|
-
name:
|
|
634
|
-
value:
|
|
804
|
+
var assignmentDataSchema = z4.object({
|
|
805
|
+
name: z4.string(),
|
|
806
|
+
value: z4.string()
|
|
635
807
|
}).strict();
|
|
636
|
-
var joinDataSchema =
|
|
637
|
-
table:
|
|
638
|
-
localColumnName:
|
|
639
|
-
foreignColumnName:
|
|
640
|
-
custom:
|
|
641
|
-
type:
|
|
642
|
-
alias:
|
|
808
|
+
var joinDataSchema = z4.object({
|
|
809
|
+
table: z4.string(),
|
|
810
|
+
localColumnName: z4.string().optional(),
|
|
811
|
+
foreignColumnName: z4.string().optional(),
|
|
812
|
+
custom: z4.string().optional(),
|
|
813
|
+
type: z4.enum(["LEFT", "INNER"]),
|
|
814
|
+
alias: z4.string().optional()
|
|
643
815
|
}).strict();
|
|
644
|
-
var
|
|
645
|
-
|
|
646
|
-
|
|
816
|
+
var requestDataSchema = z4.object({
|
|
817
|
+
name: z4.string(),
|
|
818
|
+
required: z4.boolean(),
|
|
819
|
+
validator: z4.array(validatorDataSchema)
|
|
647
820
|
}).strict();
|
|
648
|
-
var
|
|
649
|
-
name:
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
subquery: z2.object({
|
|
657
|
-
table: z2.string(),
|
|
658
|
-
joins: z2.array(joinDataSchema),
|
|
659
|
-
where: z2.array(whereDataSchema),
|
|
660
|
-
properties: z2.array(z2.lazy(() => responseDataSchema)),
|
|
821
|
+
var responseDataSchema = z4.object({
|
|
822
|
+
name: z4.string(),
|
|
823
|
+
selector: z4.string().optional(),
|
|
824
|
+
subquery: z4.object({
|
|
825
|
+
table: z4.string(),
|
|
826
|
+
joins: z4.array(joinDataSchema),
|
|
827
|
+
where: z4.array(whereDataSchema),
|
|
828
|
+
properties: z4.array(z4.lazy(() => responseDataSchema)),
|
|
661
829
|
// Explicit type for the lazy schema
|
|
662
830
|
groupBy: groupBySchema.optional(),
|
|
663
831
|
orderBy: orderBySchema.optional()
|
|
664
832
|
}).optional()
|
|
665
833
|
}).strict();
|
|
666
|
-
var routeDataBaseSchema =
|
|
667
|
-
method:
|
|
668
|
-
name:
|
|
669
|
-
description:
|
|
670
|
-
path:
|
|
671
|
-
roles:
|
|
834
|
+
var routeDataBaseSchema = z4.object({
|
|
835
|
+
method: z4.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
836
|
+
name: z4.string(),
|
|
837
|
+
description: z4.string(),
|
|
838
|
+
path: z4.string(),
|
|
839
|
+
roles: z4.array(z4.string())
|
|
672
840
|
}).strict();
|
|
673
841
|
var standardRouteSchema = routeDataBaseSchema.extend({
|
|
674
|
-
type:
|
|
675
|
-
table:
|
|
676
|
-
joins:
|
|
677
|
-
assignments:
|
|
678
|
-
where:
|
|
679
|
-
request:
|
|
680
|
-
response:
|
|
842
|
+
type: z4.enum(["ONE", "ARRAY", "PAGED"]),
|
|
843
|
+
table: z4.string(),
|
|
844
|
+
joins: z4.array(joinDataSchema),
|
|
845
|
+
assignments: z4.array(assignmentDataSchema),
|
|
846
|
+
where: z4.array(whereDataSchema),
|
|
847
|
+
request: z4.array(requestDataSchema),
|
|
848
|
+
response: z4.array(responseDataSchema),
|
|
681
849
|
groupBy: groupBySchema.optional(),
|
|
682
850
|
orderBy: orderBySchema.optional()
|
|
683
851
|
}).strict();
|
|
684
852
|
var customRouteSchema = routeDataBaseSchema.extend({
|
|
685
|
-
type:
|
|
686
|
-
responseType:
|
|
687
|
-
requestType:
|
|
688
|
-
request:
|
|
689
|
-
|
|
853
|
+
type: z4.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
|
|
854
|
+
responseType: z4.union([z4.string(), z4.enum(["string", "number", "boolean"])]),
|
|
855
|
+
requestType: z4.string().optional(),
|
|
856
|
+
request: z4.array(requestDataSchema).optional(),
|
|
857
|
+
table: z4.undefined(),
|
|
858
|
+
joins: z4.undefined(),
|
|
859
|
+
assignments: z4.undefined(),
|
|
860
|
+
fileUploadType: z4.enum(["SINGLE", "MULTIPLE"]).optional()
|
|
690
861
|
}).strict();
|
|
691
|
-
var postgresColumnNumericTypesSchema =
|
|
862
|
+
var postgresColumnNumericTypesSchema = z4.enum([
|
|
692
863
|
"SMALLINT",
|
|
693
864
|
// 2 bytes, -32,768 to 32,767
|
|
694
865
|
"INTEGER",
|
|
@@ -708,7 +879,7 @@ var postgresColumnNumericTypesSchema = z2.enum([
|
|
|
708
879
|
"BIGSERIAL"
|
|
709
880
|
// auto-incrementing big integer
|
|
710
881
|
]);
|
|
711
|
-
var postgresColumnStringTypesSchema =
|
|
882
|
+
var postgresColumnStringTypesSchema = z4.enum([
|
|
712
883
|
"CHAR",
|
|
713
884
|
// fixed-length, blank-padded
|
|
714
885
|
"VARCHAR",
|
|
@@ -718,7 +889,7 @@ var postgresColumnStringTypesSchema = z2.enum([
|
|
|
718
889
|
"BYTEA"
|
|
719
890
|
// binary data
|
|
720
891
|
]);
|
|
721
|
-
var postgresColumnDateTypesSchema =
|
|
892
|
+
var postgresColumnDateTypesSchema = z4.enum([
|
|
722
893
|
"DATE",
|
|
723
894
|
// calendar date (year, month, day)
|
|
724
895
|
"TIMESTAMP",
|
|
@@ -730,7 +901,7 @@ var postgresColumnDateTypesSchema = z2.enum([
|
|
|
730
901
|
"INTERVAL"
|
|
731
902
|
// time span
|
|
732
903
|
]);
|
|
733
|
-
var mariaDbColumnNumericTypesSchema =
|
|
904
|
+
var mariaDbColumnNumericTypesSchema = z4.enum([
|
|
734
905
|
"BOOLEAN",
|
|
735
906
|
// 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
|
|
736
907
|
"TINYINT",
|
|
@@ -750,7 +921,7 @@ var mariaDbColumnNumericTypesSchema = z2.enum([
|
|
|
750
921
|
"DOUBLE"
|
|
751
922
|
// 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.
|
|
752
923
|
]);
|
|
753
|
-
var mariaDbColumnStringTypesSchema =
|
|
924
|
+
var mariaDbColumnStringTypesSchema = z4.enum([
|
|
754
925
|
"CHAR",
|
|
755
926
|
// 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.
|
|
756
927
|
"VARCHAR",
|
|
@@ -776,7 +947,7 @@ var mariaDbColumnStringTypesSchema = z2.enum([
|
|
|
776
947
|
"ENUM"
|
|
777
948
|
// Enum type
|
|
778
949
|
]);
|
|
779
|
-
var mariaDbColumnDateTypesSchema =
|
|
950
|
+
var mariaDbColumnDateTypesSchema = z4.enum([
|
|
780
951
|
"DATE",
|
|
781
952
|
// 4-bytes Date has year, month, and day.
|
|
782
953
|
"DATETIME",
|
|
@@ -786,9 +957,9 @@ var mariaDbColumnDateTypesSchema = z2.enum([
|
|
|
786
957
|
"TIMESTAMP"
|
|
787
958
|
// 4-bytes Values are stored as the number of seconds since 1970-01-01 00:00:00 UTC, and optionally microseconds.
|
|
788
959
|
]);
|
|
789
|
-
var columnDataSchema =
|
|
790
|
-
name:
|
|
791
|
-
type:
|
|
960
|
+
var columnDataSchema = z4.object({
|
|
961
|
+
name: z4.string(),
|
|
962
|
+
type: z4.union([
|
|
792
963
|
postgresColumnNumericTypesSchema,
|
|
793
964
|
postgresColumnStringTypesSchema,
|
|
794
965
|
postgresColumnDateTypesSchema,
|
|
@@ -796,24 +967,24 @@ var columnDataSchema = z2.object({
|
|
|
796
967
|
mariaDbColumnStringTypesSchema,
|
|
797
968
|
mariaDbColumnDateTypesSchema
|
|
798
969
|
]),
|
|
799
|
-
isNullable:
|
|
800
|
-
roles:
|
|
801
|
-
comment:
|
|
802
|
-
default:
|
|
803
|
-
value:
|
|
804
|
-
isPrimary:
|
|
805
|
-
isUnique:
|
|
806
|
-
hasAutoIncrement:
|
|
807
|
-
length:
|
|
970
|
+
isNullable: z4.boolean(),
|
|
971
|
+
roles: z4.array(z4.string()),
|
|
972
|
+
comment: z4.string().optional(),
|
|
973
|
+
default: z4.string().optional(),
|
|
974
|
+
value: z4.string().optional(),
|
|
975
|
+
isPrimary: z4.boolean().optional(),
|
|
976
|
+
isUnique: z4.boolean().optional(),
|
|
977
|
+
hasAutoIncrement: z4.boolean().optional(),
|
|
978
|
+
length: z4.number().optional()
|
|
808
979
|
}).strict();
|
|
809
|
-
var indexDataSchema =
|
|
810
|
-
name:
|
|
811
|
-
columns:
|
|
812
|
-
isUnique:
|
|
813
|
-
isPrimaryKey:
|
|
814
|
-
order:
|
|
980
|
+
var indexDataSchema = z4.object({
|
|
981
|
+
name: z4.string(),
|
|
982
|
+
columns: z4.array(z4.string()),
|
|
983
|
+
isUnique: z4.boolean(),
|
|
984
|
+
isPrimaryKey: z4.boolean(),
|
|
985
|
+
order: z4.enum(["ASC", "DESC"])
|
|
815
986
|
}).strict();
|
|
816
|
-
var foreignKeyActionsSchema =
|
|
987
|
+
var foreignKeyActionsSchema = z4.enum([
|
|
817
988
|
"CASCADE",
|
|
818
989
|
// CASCADE action for foreign keys
|
|
819
990
|
"SET NULL",
|
|
@@ -825,38 +996,38 @@ var foreignKeyActionsSchema = z2.enum([
|
|
|
825
996
|
"SET DEFAULT"
|
|
826
997
|
// SET DEFAULT action for foreign keys
|
|
827
998
|
]);
|
|
828
|
-
var foreignKeyDataSchema =
|
|
829
|
-
name:
|
|
830
|
-
column:
|
|
831
|
-
refTable:
|
|
832
|
-
refColumn:
|
|
999
|
+
var foreignKeyDataSchema = z4.object({
|
|
1000
|
+
name: z4.string(),
|
|
1001
|
+
column: z4.string(),
|
|
1002
|
+
refTable: z4.string(),
|
|
1003
|
+
refColumn: z4.string(),
|
|
833
1004
|
onDelete: foreignKeyActionsSchema,
|
|
834
1005
|
onUpdate: foreignKeyActionsSchema
|
|
835
1006
|
}).strict();
|
|
836
|
-
var checkConstraintDataSchema =
|
|
837
|
-
name:
|
|
838
|
-
check:
|
|
1007
|
+
var checkConstraintDataSchema = z4.object({
|
|
1008
|
+
name: z4.string(),
|
|
1009
|
+
check: z4.string()
|
|
839
1010
|
}).strict();
|
|
840
|
-
var tableDataSchema =
|
|
841
|
-
name:
|
|
842
|
-
columns:
|
|
843
|
-
indexes:
|
|
844
|
-
foreignKeys:
|
|
845
|
-
checkConstraints:
|
|
846
|
-
roles:
|
|
1011
|
+
var tableDataSchema = z4.object({
|
|
1012
|
+
name: z4.string(),
|
|
1013
|
+
columns: z4.array(columnDataSchema),
|
|
1014
|
+
indexes: z4.array(indexDataSchema),
|
|
1015
|
+
foreignKeys: z4.array(foreignKeyDataSchema),
|
|
1016
|
+
checkConstraints: z4.array(checkConstraintDataSchema),
|
|
1017
|
+
roles: z4.array(z4.string())
|
|
847
1018
|
}).strict();
|
|
848
|
-
var endpointDataSchema =
|
|
849
|
-
name:
|
|
850
|
-
description:
|
|
851
|
-
baseUrl:
|
|
852
|
-
routes:
|
|
1019
|
+
var endpointDataSchema = z4.object({
|
|
1020
|
+
name: z4.string(),
|
|
1021
|
+
description: z4.string(),
|
|
1022
|
+
baseUrl: z4.string(),
|
|
1023
|
+
routes: z4.array(z4.union([standardRouteSchema, customRouteSchema]))
|
|
853
1024
|
}).strict();
|
|
854
|
-
var resturaZodSchema =
|
|
855
|
-
database:
|
|
856
|
-
endpoints:
|
|
857
|
-
globalParams:
|
|
858
|
-
roles:
|
|
859
|
-
customTypes:
|
|
1025
|
+
var resturaZodSchema = z4.object({
|
|
1026
|
+
database: z4.array(tableDataSchema),
|
|
1027
|
+
endpoints: z4.array(endpointDataSchema),
|
|
1028
|
+
globalParams: z4.array(z4.string()),
|
|
1029
|
+
roles: z4.array(z4.string()),
|
|
1030
|
+
customTypes: z4.string()
|
|
860
1031
|
}).strict();
|
|
861
1032
|
async function isSchemaValid(schemaToCheck) {
|
|
862
1033
|
try {
|
|
@@ -916,7 +1087,7 @@ function convertTable(table) {
|
|
|
916
1087
|
}
|
|
917
1088
|
|
|
918
1089
|
// src/restura/middleware/authenticateUser.ts
|
|
919
|
-
|
|
1090
|
+
function authenticateUser(applicationAuthenticateHandler) {
|
|
920
1091
|
return (req, res, next) => {
|
|
921
1092
|
applicationAuthenticateHandler(
|
|
922
1093
|
req,
|
|
@@ -931,6 +1102,513 @@ async function authenticateUser(applicationAuthenticateHandler) {
|
|
|
931
1102
|
};
|
|
932
1103
|
}
|
|
933
1104
|
|
|
1105
|
+
// src/restura/customTypeValidationGenerator.ts
|
|
1106
|
+
import fs from "fs";
|
|
1107
|
+
import * as TJS from "typescript-json-schema";
|
|
1108
|
+
import path, { resolve } from "path";
|
|
1109
|
+
import tmp from "tmp";
|
|
1110
|
+
import * as process2 from "process";
|
|
1111
|
+
function customTypeValidationGenerator(currentSchema) {
|
|
1112
|
+
const schemaObject = {};
|
|
1113
|
+
const customInterfaceNames = currentSchema.customTypes.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
|
|
1114
|
+
if (!customInterfaceNames) return {};
|
|
1115
|
+
const temporaryFile = tmp.fileSync({ mode: 420, prefix: "prefix-", postfix: ".ts" });
|
|
1116
|
+
fs.writeFileSync(temporaryFile.name, currentSchema.customTypes);
|
|
1117
|
+
const compilerOptions = {
|
|
1118
|
+
strictNullChecks: true,
|
|
1119
|
+
skipLibCheck: true
|
|
1120
|
+
};
|
|
1121
|
+
const program = TJS.getProgramFromFiles(
|
|
1122
|
+
[
|
|
1123
|
+
resolve(temporaryFile.name),
|
|
1124
|
+
// find a way to remove
|
|
1125
|
+
path.join(process2.cwd(), "src/@types/models.d.ts"),
|
|
1126
|
+
path.join(process2.cwd(), "src/@types/api.d.ts")
|
|
1127
|
+
],
|
|
1128
|
+
compilerOptions
|
|
1129
|
+
);
|
|
1130
|
+
customInterfaceNames.forEach((item) => {
|
|
1131
|
+
const ddlSchema = TJS.generateSchema(program, item, {
|
|
1132
|
+
required: true
|
|
1133
|
+
});
|
|
1134
|
+
schemaObject[item] = ddlSchema || {};
|
|
1135
|
+
});
|
|
1136
|
+
temporaryFile.removeCallback();
|
|
1137
|
+
return schemaObject;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// src/restura/sql/SqlEngine.ts
|
|
1141
|
+
import { ObjectUtils as ObjectUtils3 } from "@redskytech/core-utils";
|
|
1142
|
+
var SqlEngine = class {
|
|
1143
|
+
async runQueryForRoute(req, routeData, schema) {
|
|
1144
|
+
if (!this.doesRoleHavePermissionToTable(req.requesterDetails.role, schema, routeData.table))
|
|
1145
|
+
throw new RsError("UNAUTHORIZED", "You do not have permission to access this table");
|
|
1146
|
+
switch (routeData.method) {
|
|
1147
|
+
case "POST":
|
|
1148
|
+
return this.executeCreateRequest(req, routeData, schema);
|
|
1149
|
+
case "GET":
|
|
1150
|
+
return this.executeGetRequest(req, routeData, schema);
|
|
1151
|
+
case "PUT":
|
|
1152
|
+
case "PATCH":
|
|
1153
|
+
return this.executeUpdateRequest(req, routeData, schema);
|
|
1154
|
+
case "DELETE":
|
|
1155
|
+
return this.executeDeleteRequest(req, routeData, schema);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
getTableSchema(schema, tableName) {
|
|
1159
|
+
const tableSchema = schema.database.find((item) => item.name === tableName);
|
|
1160
|
+
if (!tableSchema) throw new RsError("SCHEMA_ERROR", `Table ${tableName} not found in schema`);
|
|
1161
|
+
return tableSchema;
|
|
1162
|
+
}
|
|
1163
|
+
doesRoleHavePermissionToColumn(role, schema, item, joins) {
|
|
1164
|
+
if (item.selector) {
|
|
1165
|
+
let tableName = item.selector.split(".")[0];
|
|
1166
|
+
const columnName = item.selector.split(".")[1];
|
|
1167
|
+
let tableSchema = schema.database.find((item2) => item2.name === tableName);
|
|
1168
|
+
if (!tableSchema) {
|
|
1169
|
+
const join = joins.find((join2) => join2.alias === tableName);
|
|
1170
|
+
if (!join) throw new RsError("SCHEMA_ERROR", `Table ${tableName} not found in schema`);
|
|
1171
|
+
tableName = join.table;
|
|
1172
|
+
tableSchema = schema.database.find((item2) => item2.name === tableName);
|
|
1173
|
+
}
|
|
1174
|
+
if (!tableSchema) throw new RsError("SCHEMA_ERROR", `Table ${tableName} not found in schema`);
|
|
1175
|
+
const columnSchema = tableSchema.columns.find((item2) => item2.name === columnName);
|
|
1176
|
+
if (!columnSchema)
|
|
1177
|
+
throw new RsError("SCHEMA_ERROR", `Column ${columnName} not found in table ${tableName}`);
|
|
1178
|
+
const doesColumnHaveRoles = ObjectUtils3.isArrayWithData(columnSchema.roles);
|
|
1179
|
+
if (!doesColumnHaveRoles) return true;
|
|
1180
|
+
if (!role) return false;
|
|
1181
|
+
return columnSchema.roles.includes(role);
|
|
1182
|
+
}
|
|
1183
|
+
if (item.subquery) {
|
|
1184
|
+
return ObjectUtils3.isArrayWithData(
|
|
1185
|
+
item.subquery.properties.filter((nestedItem) => {
|
|
1186
|
+
return this.doesRoleHavePermissionToColumn(role, schema, nestedItem, joins);
|
|
1187
|
+
})
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
doesRoleHavePermissionToTable(userRole, schema, tableName) {
|
|
1193
|
+
const tableSchema = this.getTableSchema(schema, tableName);
|
|
1194
|
+
const doesTableHaveRoles = ObjectUtils3.isArrayWithData(tableSchema.roles);
|
|
1195
|
+
if (!doesTableHaveRoles) return true;
|
|
1196
|
+
if (!userRole) return false;
|
|
1197
|
+
return tableSchema.roles.includes(userRole);
|
|
1198
|
+
}
|
|
1199
|
+
replaceParamKeywords(value, routeData, req, sqlParams) {
|
|
1200
|
+
let returnValue = value;
|
|
1201
|
+
returnValue = this.replaceLocalParamKeywords(returnValue, routeData, req, sqlParams);
|
|
1202
|
+
returnValue = this.replaceGlobalParamKeywords(returnValue, routeData, req, sqlParams);
|
|
1203
|
+
return returnValue;
|
|
1204
|
+
}
|
|
1205
|
+
replaceLocalParamKeywords(value, routeData, req, sqlParams) {
|
|
1206
|
+
var _a;
|
|
1207
|
+
if (!routeData.request) return value;
|
|
1208
|
+
const data = req.data;
|
|
1209
|
+
if (typeof value === "string") {
|
|
1210
|
+
(_a = value.match(/\$[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a.forEach((param) => {
|
|
1211
|
+
const requestParam = routeData.request.find((item) => {
|
|
1212
|
+
return item.name === param.replace("$", "");
|
|
1213
|
+
});
|
|
1214
|
+
if (!requestParam)
|
|
1215
|
+
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
1216
|
+
sqlParams.push(data[requestParam.name]);
|
|
1217
|
+
});
|
|
1218
|
+
return value.replace(new RegExp(/\$[a-zA-Z][a-zA-Z0-9_]+/g), "?");
|
|
1219
|
+
}
|
|
1220
|
+
return value;
|
|
1221
|
+
}
|
|
1222
|
+
replaceGlobalParamKeywords(value, routeData, req, sqlParams) {
|
|
1223
|
+
var _a;
|
|
1224
|
+
if (typeof value === "string") {
|
|
1225
|
+
(_a = value.match(/#[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a.forEach((param) => {
|
|
1226
|
+
param = param.replace("#", "");
|
|
1227
|
+
const globalParamValue = req.requesterDetails[param];
|
|
1228
|
+
if (!globalParamValue)
|
|
1229
|
+
throw new RsError("SCHEMA_ERROR", `Invalid global keyword clause in route ${routeData.name}`);
|
|
1230
|
+
sqlParams.push(globalParamValue);
|
|
1231
|
+
});
|
|
1232
|
+
return value.replace(new RegExp(/#[a-zA-Z][a-zA-Z0-9_]+/g), "?");
|
|
1233
|
+
}
|
|
1234
|
+
return value;
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
// src/restura/sql/filterSqlParser.ts
|
|
1239
|
+
import peg from "pegjs";
|
|
1240
|
+
var filterSqlGrammar = `
|
|
1241
|
+
start = expressionList
|
|
1242
|
+
|
|
1243
|
+
expressionList =
|
|
1244
|
+
leftExpression:expression operator:operator rightExpression:expressionList
|
|
1245
|
+
{ return \`\${leftExpression} \${operator} \${rightExpression}\`;}
|
|
1246
|
+
/ expression
|
|
1247
|
+
|
|
1248
|
+
expression =
|
|
1249
|
+
negate:negate?"(" "column:" column:column ","? value:value? ","? type:type? ")"
|
|
1250
|
+
{return \`\${negate? "!" : ""}(\${type? type(column, value) : \`\${column} = \${mysql.escape(value)}\`})\`;}
|
|
1251
|
+
/
|
|
1252
|
+
negate:negate?"("expression:expressionList")" { return \`\${negate? "!" : ""}(\${expression})\`; }
|
|
1253
|
+
|
|
1254
|
+
negate = "!"
|
|
1255
|
+
|
|
1256
|
+
operator = "and"i / "or"i
|
|
1257
|
+
|
|
1258
|
+
column = left:text "." right:text { return \`\${mysql.escapeId(left)}.\${mysql.escapeId(right)}\`; }
|
|
1259
|
+
/
|
|
1260
|
+
text:text { return mysql.escapeId(text); }
|
|
1261
|
+
|
|
1262
|
+
|
|
1263
|
+
text = text:[ a-z0-9-_:.@]i+ { return text.join("");}
|
|
1264
|
+
|
|
1265
|
+
type = "type:" type:typeString { return type; }
|
|
1266
|
+
typeString = text:"startsWith" { return function(column, value) { return \`\${column} LIKE '\${mysql.escape(value).slice(1,-1)}%'\`; } } /
|
|
1267
|
+
text:"endsWith" { return function(column, value) { return \`\${column} LIKE '%\${mysql.escape(value).slice(1,-1)}'\`; } } /
|
|
1268
|
+
text:"contains" { return function(column, value) { return \`\${column} LIKE '%\${mysql.escape(value).slice(1,-1)}%'\`; } } /
|
|
1269
|
+
text:"exact" { return function(column, value) { return \`\${column} = '\${mysql.escape(value).slice(1,-1)}'\`; } } /
|
|
1270
|
+
text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${mysql.escape(value).slice(1,-1)}'\`; } } /
|
|
1271
|
+
text:"greaterThan" { return function(column, value) { return \`\${column} > '\${mysql.escape(value).slice(1,-1)}'\`; } } /
|
|
1272
|
+
text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${mysql.escape(value).slice(1,-1)}'\`; } } /
|
|
1273
|
+
text:"lessThan" { return function(column, value) { return \`\${column} < '\${mysql.escape(value).slice(1,-1)}'\`; } } /
|
|
1274
|
+
text:"isNull" { return function(column, value) { return \`isNull(\${column})\`; } }
|
|
1275
|
+
|
|
1276
|
+
value = "value:" value:text { return value; }
|
|
1277
|
+
|
|
1278
|
+
`;
|
|
1279
|
+
var filterSqlParser = peg.generate(filterSqlGrammar, {
|
|
1280
|
+
format: "commonjs"
|
|
1281
|
+
// dependencies: { mysql: 'mysql' } // todo: figure out a better way to escape values depending on the database type
|
|
1282
|
+
});
|
|
1283
|
+
var filterSqlParser_default = filterSqlParser;
|
|
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
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// src/restura/sql/PsqlEngine.ts
|
|
1334
|
+
var PsqlEngine = class extends SqlEngine {
|
|
1335
|
+
constructor(psqlConnectionPool) {
|
|
1336
|
+
super();
|
|
1337
|
+
this.psqlConnectionPool = psqlConnectionPool;
|
|
1338
|
+
}
|
|
1339
|
+
async diffDatabaseToSchema(schema) {
|
|
1340
|
+
console.log(schema);
|
|
1341
|
+
return Promise.resolve("");
|
|
1342
|
+
}
|
|
1343
|
+
generateDatabaseSchemaFromSchema(schema) {
|
|
1344
|
+
console.log(schema);
|
|
1345
|
+
return "";
|
|
1346
|
+
}
|
|
1347
|
+
createNestedSelect(req, schema, item, routeData, userRole, sqlParams) {
|
|
1348
|
+
console.log(req, schema, item, routeData, userRole, sqlParams);
|
|
1349
|
+
return "";
|
|
1350
|
+
}
|
|
1351
|
+
async executeCreateRequest(req, routeData, schema) {
|
|
1352
|
+
const sqlParams = [];
|
|
1353
|
+
const parameterObj = {};
|
|
1354
|
+
(routeData.assignments || []).forEach((assignment) => {
|
|
1355
|
+
parameterObj[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
|
|
1356
|
+
});
|
|
1357
|
+
const query = insertObjectQuery(routeData.table, __spreadValues(__spreadValues({}, req.data), parameterObj));
|
|
1358
|
+
const createdItem = await this.psqlConnectionPool.queryOne(query, sqlParams);
|
|
1359
|
+
const insertId = createdItem == null ? void 0 : createdItem.id;
|
|
1360
|
+
const whereData = [
|
|
1361
|
+
{
|
|
1362
|
+
tableName: routeData.table,
|
|
1363
|
+
value: insertId,
|
|
1364
|
+
columnName: "id",
|
|
1365
|
+
operator: "="
|
|
1366
|
+
}
|
|
1367
|
+
];
|
|
1368
|
+
req.data = { id: insertId };
|
|
1369
|
+
return this.executeGetRequest(req, __spreadProps(__spreadValues({}, routeData), { where: whereData }), schema);
|
|
1370
|
+
}
|
|
1371
|
+
async executeGetRequest(req, routeData, schema) {
|
|
1372
|
+
const DEFAULT_PAGED_PAGE_NUMBER = 0;
|
|
1373
|
+
const DEFAULT_PAGED_PER_PAGE_NUMBER = 25;
|
|
1374
|
+
const sqlParams = [];
|
|
1375
|
+
const userRole = req.requesterDetails.role;
|
|
1376
|
+
let sqlStatement = "";
|
|
1377
|
+
const selectColumns = [];
|
|
1378
|
+
routeData.response.forEach((item) => {
|
|
1379
|
+
if (item.subquery || this.doesRoleHavePermissionToColumn(userRole, schema, item, routeData.joins))
|
|
1380
|
+
selectColumns.push(item);
|
|
1381
|
+
});
|
|
1382
|
+
if (!selectColumns.length) throw new RsError("UNAUTHORIZED", `You do not have permission to access this data.`);
|
|
1383
|
+
let selectStatement = "SELECT \n";
|
|
1384
|
+
selectStatement += ` ${selectColumns.map((item) => {
|
|
1385
|
+
if (item.subquery) {
|
|
1386
|
+
return `${this.createNestedSelect(req, schema, item, routeData, userRole, sqlParams)} AS ${item.name}`;
|
|
1387
|
+
}
|
|
1388
|
+
return `${escapeColumnName(item.selector)} AS ${escapeColumnName(item.name)}`;
|
|
1389
|
+
}).join(",\n ")}
|
|
1390
|
+
`;
|
|
1391
|
+
sqlStatement += `FROM "${routeData.table}"
|
|
1392
|
+
`;
|
|
1393
|
+
sqlStatement += this.generateJoinStatements(
|
|
1394
|
+
req,
|
|
1395
|
+
routeData.joins,
|
|
1396
|
+
routeData.table,
|
|
1397
|
+
routeData,
|
|
1398
|
+
schema,
|
|
1399
|
+
userRole,
|
|
1400
|
+
sqlParams
|
|
1401
|
+
);
|
|
1402
|
+
sqlStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
1403
|
+
let groupByOrderByStatement = this.generateGroupBy(routeData);
|
|
1404
|
+
groupByOrderByStatement += this.generateOrderBy(req, routeData);
|
|
1405
|
+
if (routeData.type === "ONE") {
|
|
1406
|
+
return this.psqlConnectionPool.queryOne(
|
|
1407
|
+
`${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
|
|
1408
|
+
sqlParams
|
|
1409
|
+
);
|
|
1410
|
+
} else if (routeData.type === "ARRAY") {
|
|
1411
|
+
return this.psqlConnectionPool.runQuery(
|
|
1412
|
+
`${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
|
|
1413
|
+
sqlParams
|
|
1414
|
+
);
|
|
1415
|
+
} else if (routeData.type === "PAGED") {
|
|
1416
|
+
const data = req.data;
|
|
1417
|
+
const pageResults = await this.psqlConnectionPool.runQuery(
|
|
1418
|
+
`${selectStatement}${sqlStatement}${groupByOrderByStatement} LIMIT ? OFFSET ?;SELECT COUNT(${routeData.groupBy ? `DISTINCT ${routeData.groupBy.tableName}.${routeData.groupBy.columnName}` : "*"}) AS total
|
|
1419
|
+
${sqlStatement};`,
|
|
1420
|
+
[
|
|
1421
|
+
...sqlParams,
|
|
1422
|
+
data.perPage || DEFAULT_PAGED_PER_PAGE_NUMBER,
|
|
1423
|
+
(data.page - 1) * data.perPage || DEFAULT_PAGED_PAGE_NUMBER,
|
|
1424
|
+
...sqlParams
|
|
1425
|
+
]
|
|
1426
|
+
);
|
|
1427
|
+
let total = 0;
|
|
1428
|
+
if (ObjectUtils4.isArrayWithData(pageResults)) {
|
|
1429
|
+
total = pageResults[1][0].total;
|
|
1430
|
+
}
|
|
1431
|
+
return { data: pageResults[0], total };
|
|
1432
|
+
} else {
|
|
1433
|
+
throw new RsError("UNKNOWN_ERROR", "Unknown route type.");
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
async executeUpdateRequest(req, routeData, schema) {
|
|
1437
|
+
const sqlParams = [];
|
|
1438
|
+
const _a = req.body, { id } = _a, bodyNoId = __objRest(_a, ["id"]);
|
|
1439
|
+
const table = schema.database.find((item) => {
|
|
1440
|
+
return item.name === routeData.table;
|
|
1441
|
+
});
|
|
1442
|
+
if (!table) throw new RsError("UNKNOWN_ERROR", "Unknown table.");
|
|
1443
|
+
if (table.columns.find((column) => column.name === "modifiedOn")) {
|
|
1444
|
+
bodyNoId.modifiedOn = (/* @__PURE__ */ new Date()).toISOString();
|
|
1445
|
+
}
|
|
1446
|
+
for (const assignment of routeData.assignments) {
|
|
1447
|
+
const column = table.columns.find((column2) => column2.name === assignment.name);
|
|
1448
|
+
if (!column) continue;
|
|
1449
|
+
const assignmentWithPrefix = escapeColumnName(`${routeData.table}.${assignment.name}`);
|
|
1450
|
+
if (SqlUtils.convertDatabaseTypeToTypescript(column.type) === "number")
|
|
1451
|
+
bodyNoId[assignmentWithPrefix] = Number(assignment.value);
|
|
1452
|
+
else bodyNoId[assignmentWithPrefix] = assignment.value;
|
|
1453
|
+
}
|
|
1454
|
+
const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
1455
|
+
const query = updateObjectQuery(routeData.table, bodyNoId, whereClause);
|
|
1456
|
+
await this.psqlConnectionPool.queryOne(query, [...sqlParams]);
|
|
1457
|
+
return this.executeGetRequest(req, routeData, schema);
|
|
1458
|
+
}
|
|
1459
|
+
async executeDeleteRequest(req, routeData, schema) {
|
|
1460
|
+
const sqlParams = [];
|
|
1461
|
+
const joinStatement = this.generateJoinStatements(
|
|
1462
|
+
req,
|
|
1463
|
+
routeData.joins,
|
|
1464
|
+
routeData.table,
|
|
1465
|
+
routeData,
|
|
1466
|
+
schema,
|
|
1467
|
+
req.requesterDetails.role,
|
|
1468
|
+
sqlParams
|
|
1469
|
+
);
|
|
1470
|
+
let deleteStatement = `DELETE
|
|
1471
|
+
FROM "${routeData.table}" ${joinStatement}`;
|
|
1472
|
+
deleteStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
1473
|
+
deleteStatement += ";";
|
|
1474
|
+
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams);
|
|
1475
|
+
return true;
|
|
1476
|
+
}
|
|
1477
|
+
generateJoinStatements(req, joins, baseTable, routeData, schema, userRole, sqlParams) {
|
|
1478
|
+
console.log(req, joins, baseTable, routeData, schema, userRole, sqlParams);
|
|
1479
|
+
return "";
|
|
1480
|
+
}
|
|
1481
|
+
getTableSchema(schema, tableName) {
|
|
1482
|
+
console.log(schema, tableName);
|
|
1483
|
+
return {};
|
|
1484
|
+
}
|
|
1485
|
+
generateGroupBy(routeData) {
|
|
1486
|
+
let groupBy = "";
|
|
1487
|
+
if (routeData.groupBy) {
|
|
1488
|
+
groupBy = `GROUP BY ${escapeColumnName(routeData.groupBy.tableName)}.${escapeColumnName(routeData.groupBy.columnName)}
|
|
1489
|
+
`;
|
|
1490
|
+
}
|
|
1491
|
+
return groupBy;
|
|
1492
|
+
}
|
|
1493
|
+
generateOrderBy(req, routeData) {
|
|
1494
|
+
let orderBy = "";
|
|
1495
|
+
const orderOptions = {
|
|
1496
|
+
ASC: "ASC",
|
|
1497
|
+
DESC: "DESC"
|
|
1498
|
+
};
|
|
1499
|
+
const data = req.data;
|
|
1500
|
+
if (routeData.type === "PAGED" && "sortBy" in data) {
|
|
1501
|
+
const sortOrder = orderOptions[data.sortOrder] || "ASC";
|
|
1502
|
+
orderBy = `ORDER BY ${escapeColumnName(data.sortBy)} ${sortOrder}
|
|
1503
|
+
`;
|
|
1504
|
+
} else if (routeData.orderBy) {
|
|
1505
|
+
const sortOrder = orderOptions[routeData.orderBy.order] || "ASC";
|
|
1506
|
+
orderBy = `ORDER BY ${escapeColumnName(routeData.orderBy.tableName)}.${escapeColumnName(routeData.orderBy.columnName)} ${sortOrder}
|
|
1507
|
+
`;
|
|
1508
|
+
}
|
|
1509
|
+
return orderBy;
|
|
1510
|
+
}
|
|
1511
|
+
generateWhereClause(req, where, routeData, sqlParams) {
|
|
1512
|
+
let whereClause = "";
|
|
1513
|
+
where.forEach((item, index) => {
|
|
1514
|
+
if (index === 0) whereClause = "WHERE ";
|
|
1515
|
+
if (item.custom) {
|
|
1516
|
+
whereClause += this.replaceParamKeywords(item.custom, routeData, req, sqlParams);
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
if (item.operator === void 0 || item.value === void 0 || item.columnName === void 0 || item.tableName === void 0)
|
|
1520
|
+
throw new RsError(
|
|
1521
|
+
"SCHEMA_ERROR",
|
|
1522
|
+
`Invalid where clause in route ${routeData.name}, missing required fields if not custom`
|
|
1523
|
+
);
|
|
1524
|
+
let operator = item.operator;
|
|
1525
|
+
if (operator === "LIKE") {
|
|
1526
|
+
sqlParams[sqlParams.length - 1] = `%${sqlParams[sqlParams.length - 1]}%`;
|
|
1527
|
+
} else if (operator === "STARTS WITH") {
|
|
1528
|
+
operator = "LIKE";
|
|
1529
|
+
sqlParams[sqlParams.length - 1] = `${sqlParams[sqlParams.length - 1]}%`;
|
|
1530
|
+
} else if (operator === "ENDS WITH") {
|
|
1531
|
+
operator = "LIKE";
|
|
1532
|
+
sqlParams[sqlParams.length - 1] = `%${sqlParams[sqlParams.length - 1]}`;
|
|
1533
|
+
}
|
|
1534
|
+
const replacedValue = this.replaceParamKeywords(item.value, routeData, req, sqlParams);
|
|
1535
|
+
const escapedValue = SQL`${replacedValue}`;
|
|
1536
|
+
whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator} ${["IN", "NOT IN"].includes(operator) ? `(${escapedValue})` : escapedValue}
|
|
1537
|
+
`;
|
|
1538
|
+
});
|
|
1539
|
+
const data = req.data;
|
|
1540
|
+
if (routeData.type === "PAGED" && !!(data == null ? void 0 : data.filter)) {
|
|
1541
|
+
let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
1542
|
+
const requestParam = routeData.request.find((item) => {
|
|
1543
|
+
return item.name === value.replace("$", "");
|
|
1544
|
+
});
|
|
1545
|
+
if (!requestParam)
|
|
1546
|
+
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
1547
|
+
return data[requestParam.name];
|
|
1548
|
+
});
|
|
1549
|
+
statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
1550
|
+
const requestParam = routeData.request.find((item) => {
|
|
1551
|
+
return item.name === value.replace("#", "");
|
|
1552
|
+
});
|
|
1553
|
+
if (!requestParam)
|
|
1554
|
+
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
1555
|
+
return data[requestParam.name];
|
|
1556
|
+
});
|
|
1557
|
+
statement = filterSqlParser_default.parse(statement);
|
|
1558
|
+
if (whereClause.startsWith("WHERE")) {
|
|
1559
|
+
whereClause += ` AND (${statement})
|
|
1560
|
+
`;
|
|
1561
|
+
} else {
|
|
1562
|
+
whereClause += `WHERE ${statement}
|
|
1563
|
+
`;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
return whereClause;
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
// src/restura/restura.ts
|
|
1571
|
+
import { types } from "pg";
|
|
1572
|
+
|
|
1573
|
+
// src/restura/sql/PsqlPool.ts
|
|
1574
|
+
import { Pool } from "pg";
|
|
1575
|
+
var PsqlPool = class {
|
|
1576
|
+
constructor(poolConfig) {
|
|
1577
|
+
this.poolConfig = poolConfig;
|
|
1578
|
+
this.pool = new Pool(poolConfig);
|
|
1579
|
+
}
|
|
1580
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1581
|
+
async queryOne(query, options) {
|
|
1582
|
+
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1583
|
+
try {
|
|
1584
|
+
const response = await this.pool.query(formattedQuery, options);
|
|
1585
|
+
return response.rows[0];
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
console.error(error, query, options);
|
|
1588
|
+
if ((error == null ? void 0 : error.routine) === "_bt_check_unique") {
|
|
1589
|
+
throw new RsError("DUPLICATE", error.message);
|
|
1590
|
+
}
|
|
1591
|
+
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1595
|
+
async runQuery(query, options) {
|
|
1596
|
+
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1597
|
+
const queryUpdated = query.replace(/[\t\n]/g, " ");
|
|
1598
|
+
console.log(queryUpdated, options);
|
|
1599
|
+
try {
|
|
1600
|
+
const response = await this.pool.query(formattedQuery, options);
|
|
1601
|
+
return response.rows;
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
console.error(error, query, options);
|
|
1604
|
+
if ((error == null ? void 0 : error.routine) === "_bt_check_unique") {
|
|
1605
|
+
throw new RsError("DUPLICATE", error.message);
|
|
1606
|
+
}
|
|
1607
|
+
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
|
|
934
1612
|
// src/restura/restura.ts
|
|
935
1613
|
var ResturaEngine = class {
|
|
936
1614
|
constructor() {
|
|
@@ -942,14 +1620,16 @@ var ResturaEngine = class {
|
|
|
942
1620
|
DELETE: []
|
|
943
1621
|
};
|
|
944
1622
|
}
|
|
945
|
-
// private customTypeValidation!: ValidationDictionary;
|
|
946
1623
|
/**
|
|
947
1624
|
* Initializes the Restura engine with the provided Express application.
|
|
948
1625
|
*
|
|
949
1626
|
* @param app - The Express application instance to initialize with Restura.
|
|
950
1627
|
* @returns A promise that resolves when the initialization is complete.
|
|
951
1628
|
*/
|
|
952
|
-
async init(app, authenticationHandler) {
|
|
1629
|
+
async init(app, authenticationHandler, psqlConnectionPool) {
|
|
1630
|
+
this.psqlConnectionPool = psqlConnectionPool;
|
|
1631
|
+
this.psqlEngine = new PsqlEngine(this.psqlConnectionPool);
|
|
1632
|
+
setupPgReturnTypes();
|
|
953
1633
|
this.resturaConfig = config2.validate("restura", resturaConfigSchema);
|
|
954
1634
|
this.authenticationHandler = authenticationHandler;
|
|
955
1635
|
app.use(compression());
|
|
@@ -958,7 +1638,7 @@ var ResturaEngine = class {
|
|
|
958
1638
|
app.use(cookieParser());
|
|
959
1639
|
app.disable("x-powered-by");
|
|
960
1640
|
app.use("/", addApiResponseFunctions);
|
|
961
|
-
app.use("/api/", authenticateUser);
|
|
1641
|
+
app.use("/api/", authenticateUser(this.authenticationHandler));
|
|
962
1642
|
app.use("/restura", this.resturaAuthentication);
|
|
963
1643
|
app.put(
|
|
964
1644
|
"/restura/v1/schema",
|
|
@@ -974,7 +1654,7 @@ var ResturaEngine = class {
|
|
|
974
1654
|
app.get("/restura/v1/schema/types", this.getSchemaAndTypes);
|
|
975
1655
|
this.expressApp = app;
|
|
976
1656
|
await this.reloadEndpoints();
|
|
977
|
-
this.validateGeneratedTypesFolder();
|
|
1657
|
+
await this.validateGeneratedTypesFolder();
|
|
978
1658
|
logger.info("Restura Engine Initialized");
|
|
979
1659
|
}
|
|
980
1660
|
/**
|
|
@@ -1013,7 +1693,7 @@ var ResturaEngine = class {
|
|
|
1013
1693
|
* @returns A promise that resolves when the API has been successfully generated and written to the output file.
|
|
1014
1694
|
*/
|
|
1015
1695
|
async generateApiFromSchema(outputFile, providedSchema) {
|
|
1016
|
-
|
|
1696
|
+
fs2.writeFileSync(
|
|
1017
1697
|
outputFile,
|
|
1018
1698
|
await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
|
|
1019
1699
|
);
|
|
@@ -1026,7 +1706,7 @@ var ResturaEngine = class {
|
|
|
1026
1706
|
* @returns A promise that resolves when the model has been successfully written to the output file.
|
|
1027
1707
|
*/
|
|
1028
1708
|
async generateModelFromSchema(outputFile, providedSchema) {
|
|
1029
|
-
|
|
1709
|
+
fs2.writeFileSync(
|
|
1030
1710
|
outputFile,
|
|
1031
1711
|
await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
|
|
1032
1712
|
);
|
|
@@ -1038,12 +1718,12 @@ var ResturaEngine = class {
|
|
|
1038
1718
|
* @throws {Error} If the schema file is missing or the schema is not valid.
|
|
1039
1719
|
*/
|
|
1040
1720
|
async getLatestFileSystemSchema() {
|
|
1041
|
-
if (!
|
|
1721
|
+
if (!fs2.existsSync(this.resturaConfig.schemaFilePath)) {
|
|
1042
1722
|
logger.error(`Missing restura schema file, expected path: ${this.resturaConfig.schemaFilePath}`);
|
|
1043
1723
|
throw new Error("Missing restura schema file");
|
|
1044
1724
|
}
|
|
1045
|
-
const schemaFileData =
|
|
1046
|
-
const schema =
|
|
1725
|
+
const schemaFileData = fs2.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
|
|
1726
|
+
const schema = ObjectUtils5.safeParse(schemaFileData);
|
|
1047
1727
|
const isValid = await isSchemaValid(schema);
|
|
1048
1728
|
if (!isValid) {
|
|
1049
1729
|
logger.error("Schema is not valid");
|
|
@@ -1063,9 +1743,9 @@ var ResturaEngine = class {
|
|
|
1063
1743
|
async getHashes(providedSchema) {
|
|
1064
1744
|
var _a, _b, _c, _d;
|
|
1065
1745
|
const schemaHash = await this.generateHashForSchema(providedSchema);
|
|
1066
|
-
const apiFile =
|
|
1746
|
+
const apiFile = fs2.readFileSync(path2.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
|
|
1067
1747
|
const apiCreatedSchemaHash = (_b = (_a = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a[1]) != null ? _b : "";
|
|
1068
|
-
const modelFile =
|
|
1748
|
+
const modelFile = fs2.readFileSync(path2.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
|
|
1069
1749
|
const modelCreatedSchemaHash = (_d = (_c = modelFile.toString().match(/\((.*)\)/)) == null ? void 0 : _c[1]) != null ? _d : "";
|
|
1070
1750
|
return {
|
|
1071
1751
|
schemaHash,
|
|
@@ -1075,6 +1755,7 @@ var ResturaEngine = class {
|
|
|
1075
1755
|
}
|
|
1076
1756
|
async reloadEndpoints() {
|
|
1077
1757
|
this.schema = await this.getLatestFileSystemSchema();
|
|
1758
|
+
this.customTypeValidation = customTypeValidationGenerator(this.schema);
|
|
1078
1759
|
this.resturaRouter = express.Router();
|
|
1079
1760
|
this.resetPublicEndpoints();
|
|
1080
1761
|
let routeCount = 0;
|
|
@@ -1100,27 +1781,27 @@ var ResturaEngine = class {
|
|
|
1100
1781
|
logger.info(`Restura loaded (${routeCount}) endpoint${routeCount > 1 ? "s" : ""}`);
|
|
1101
1782
|
}
|
|
1102
1783
|
async validateGeneratedTypesFolder() {
|
|
1103
|
-
if (!
|
|
1104
|
-
|
|
1784
|
+
if (!fs2.existsSync(this.resturaConfig.generatedTypesPath)) {
|
|
1785
|
+
fs2.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
|
|
1105
1786
|
}
|
|
1106
|
-
const hasApiFile =
|
|
1107
|
-
const hasModelsFile =
|
|
1787
|
+
const hasApiFile = fs2.existsSync(path2.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
|
|
1788
|
+
const hasModelsFile = fs2.existsSync(path2.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
|
|
1108
1789
|
if (!hasApiFile) {
|
|
1109
|
-
await this.generateApiFromSchema(
|
|
1790
|
+
await this.generateApiFromSchema(path2.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1110
1791
|
}
|
|
1111
1792
|
if (!hasModelsFile) {
|
|
1112
1793
|
await this.generateModelFromSchema(
|
|
1113
|
-
|
|
1794
|
+
path2.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1114
1795
|
this.schema
|
|
1115
1796
|
);
|
|
1116
1797
|
}
|
|
1117
1798
|
const hashes = await this.getHashes(this.schema);
|
|
1118
1799
|
if (hashes.schemaHash !== hashes.apiCreatedSchemaHash) {
|
|
1119
|
-
await this.generateApiFromSchema(
|
|
1800
|
+
await this.generateApiFromSchema(path2.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1120
1801
|
}
|
|
1121
1802
|
if (hashes.schemaHash !== hashes.modelCreatedSchemaHash) {
|
|
1122
1803
|
await this.generateModelFromSchema(
|
|
1123
|
-
|
|
1804
|
+
path2.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1124
1805
|
this.schema
|
|
1125
1806
|
);
|
|
1126
1807
|
}
|
|
@@ -1150,9 +1831,9 @@ var ResturaEngine = class {
|
|
|
1150
1831
|
}
|
|
1151
1832
|
}
|
|
1152
1833
|
async updateTypes() {
|
|
1153
|
-
await this.generateApiFromSchema(
|
|
1834
|
+
await this.generateApiFromSchema(path2.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
|
|
1154
1835
|
await this.generateModelFromSchema(
|
|
1155
|
-
|
|
1836
|
+
path2.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1156
1837
|
this.schema
|
|
1157
1838
|
);
|
|
1158
1839
|
}
|
|
@@ -1174,6 +1855,14 @@ var ResturaEngine = class {
|
|
|
1174
1855
|
try {
|
|
1175
1856
|
const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
|
|
1176
1857
|
this.validateAuthorization(req, routeData);
|
|
1858
|
+
validateRequestParams(req, routeData, this.customTypeValidation);
|
|
1859
|
+
const data = await this.psqlEngine.runQueryForRoute(
|
|
1860
|
+
req,
|
|
1861
|
+
routeData,
|
|
1862
|
+
this.schema
|
|
1863
|
+
);
|
|
1864
|
+
if (routeData.type === "PAGED") res.sendNoWrap(data);
|
|
1865
|
+
else res.sendData(data);
|
|
1177
1866
|
} catch (e) {
|
|
1178
1867
|
next(e);
|
|
1179
1868
|
}
|
|
@@ -1233,7 +1922,7 @@ var ResturaEngine = class {
|
|
|
1233
1922
|
printWidth: 120,
|
|
1234
1923
|
singleQuote: true
|
|
1235
1924
|
}));
|
|
1236
|
-
|
|
1925
|
+
fs2.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
|
|
1237
1926
|
}
|
|
1238
1927
|
resetPublicEndpoints() {
|
|
1239
1928
|
this.publicEndpoints = {
|
|
@@ -1250,13 +1939,13 @@ var ResturaEngine = class {
|
|
|
1250
1939
|
if (!routeData.roles.includes(role))
|
|
1251
1940
|
throw new RsError("UNAUTHORIZED", "Not authorized to access this endpoint");
|
|
1252
1941
|
}
|
|
1253
|
-
getRouteData(method, baseUrl,
|
|
1942
|
+
getRouteData(method, baseUrl, path3) {
|
|
1254
1943
|
const endpoint = this.schema.endpoints.find((item) => {
|
|
1255
1944
|
return item.baseUrl === baseUrl;
|
|
1256
1945
|
});
|
|
1257
1946
|
if (!endpoint) throw new RsError("NOT_FOUND", "Route not found");
|
|
1258
1947
|
const route = endpoint.routes.find((item) => {
|
|
1259
|
-
return item.method === method && item.path ===
|
|
1948
|
+
return item.method === method && item.path === path3;
|
|
1260
1949
|
});
|
|
1261
1950
|
if (!route) throw new RsError("NOT_FOUND", "Route not found");
|
|
1262
1951
|
return route;
|
|
@@ -1283,8 +1972,20 @@ __decorateClass([
|
|
|
1283
1972
|
__decorateClass([
|
|
1284
1973
|
boundMethod
|
|
1285
1974
|
], ResturaEngine.prototype, "isCustomRoute", 1);
|
|
1975
|
+
var setupPgReturnTypes = () => {
|
|
1976
|
+
const TIMESTAMPTZ_OID = 1184;
|
|
1977
|
+
types.setTypeParser(TIMESTAMPTZ_OID, (val) => {
|
|
1978
|
+
return val === null ? null : new Date(val).toISOString();
|
|
1979
|
+
});
|
|
1980
|
+
const BIGINT_OID = 20;
|
|
1981
|
+
types.setTypeParser(BIGINT_OID, (val) => {
|
|
1982
|
+
return val === null ? null : Number(val);
|
|
1983
|
+
});
|
|
1984
|
+
};
|
|
1985
|
+
setupPgReturnTypes();
|
|
1286
1986
|
var restura = new ResturaEngine();
|
|
1287
1987
|
export {
|
|
1988
|
+
PsqlPool,
|
|
1288
1989
|
logger,
|
|
1289
1990
|
restura
|
|
1290
1991
|
};
|