@restura/core 1.4.0 → 1.6.0
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.ts +159 -138
- package/dist/index.js +331 -250
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -878,7 +878,7 @@ declare namespace Restura {
|
|
|
878
878
|
}
|
|
879
879
|
|
|
880
880
|
// src/restura/restura.ts
|
|
881
|
-
import { ObjectUtils as
|
|
881
|
+
import { ObjectUtils as ObjectUtils4, StringUtils as StringUtils3 } from "@redskytech/core-utils";
|
|
882
882
|
import { config as config2 } from "@restura/internal";
|
|
883
883
|
|
|
884
884
|
// ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
|
|
@@ -1093,7 +1093,120 @@ import fs2 from "fs";
|
|
|
1093
1093
|
import path2, { resolve } from "path";
|
|
1094
1094
|
import tmp from "tmp";
|
|
1095
1095
|
import * as TJS from "typescript-json-schema";
|
|
1096
|
-
|
|
1096
|
+
|
|
1097
|
+
// src/restura/generators/schemaGeneratorUtils.ts
|
|
1098
|
+
function buildRouteSchema(requestParams) {
|
|
1099
|
+
const properties = {};
|
|
1100
|
+
const required = [];
|
|
1101
|
+
for (const param of requestParams) {
|
|
1102
|
+
if (param.required) {
|
|
1103
|
+
required.push(param.name);
|
|
1104
|
+
}
|
|
1105
|
+
const propertySchema = buildPropertySchemaFromRequest(param);
|
|
1106
|
+
properties[param.name] = propertySchema;
|
|
1107
|
+
}
|
|
1108
|
+
return {
|
|
1109
|
+
type: "object",
|
|
1110
|
+
properties,
|
|
1111
|
+
...required.length > 0 && { required },
|
|
1112
|
+
// Only include if not empty
|
|
1113
|
+
additionalProperties: false
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
function buildPropertySchemaFromRequest(param) {
|
|
1117
|
+
const propertySchema = {};
|
|
1118
|
+
const typeCheckValidator = param.validator.find((v) => v.type === "TYPE_CHECK");
|
|
1119
|
+
const oneOfValidator = param.validator.find((v) => v.type === "ONE_OF");
|
|
1120
|
+
if (oneOfValidator && Array.isArray(oneOfValidator.value)) {
|
|
1121
|
+
propertySchema.enum = oneOfValidator.value;
|
|
1122
|
+
if (!typeCheckValidator && oneOfValidator.value.length > 0) {
|
|
1123
|
+
const firstValue = oneOfValidator.value[0];
|
|
1124
|
+
propertySchema.type = typeof firstValue === "number" ? "number" : "string";
|
|
1125
|
+
return propertySchema;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (!typeCheckValidator) {
|
|
1129
|
+
return propertySchema;
|
|
1130
|
+
}
|
|
1131
|
+
const typeValue = typeCheckValidator.value;
|
|
1132
|
+
if (typeof typeValue === "string" && typeValue.endsWith("[]")) {
|
|
1133
|
+
const itemType = typeValue.replace("[]", "");
|
|
1134
|
+
if (param.isNullable) {
|
|
1135
|
+
propertySchema.type = ["array", "null"];
|
|
1136
|
+
} else {
|
|
1137
|
+
propertySchema.type = "array";
|
|
1138
|
+
}
|
|
1139
|
+
propertySchema.items = {
|
|
1140
|
+
type: mapTypeToJsonSchemaType(itemType)
|
|
1141
|
+
};
|
|
1142
|
+
applyArrayValidators(propertySchema, param);
|
|
1143
|
+
} else {
|
|
1144
|
+
if (param.isNullable) {
|
|
1145
|
+
propertySchema.type = [mapTypeToJsonSchemaType(typeValue), "null"];
|
|
1146
|
+
} else {
|
|
1147
|
+
propertySchema.type = mapTypeToJsonSchemaType(typeValue);
|
|
1148
|
+
}
|
|
1149
|
+
const type = propertySchema.type;
|
|
1150
|
+
const isNumericType = type === "number" || type === "integer" || Array.isArray(type) && (type.includes("number") || type.includes("integer"));
|
|
1151
|
+
const isStringType = type === "string" || Array.isArray(type) && type.includes("string");
|
|
1152
|
+
if (isNumericType) {
|
|
1153
|
+
applyNumericValidators(propertySchema, param);
|
|
1154
|
+
} else if (isStringType) {
|
|
1155
|
+
applyStringValidators(propertySchema, param);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return propertySchema;
|
|
1159
|
+
}
|
|
1160
|
+
function mapTypeToJsonSchemaType(type) {
|
|
1161
|
+
if (typeof type !== "string") {
|
|
1162
|
+
throw new Error(`Invalid type for JSON Schema mapping: ${type}`);
|
|
1163
|
+
}
|
|
1164
|
+
switch (type) {
|
|
1165
|
+
case "number":
|
|
1166
|
+
return "number";
|
|
1167
|
+
case "string":
|
|
1168
|
+
return "string";
|
|
1169
|
+
case "boolean":
|
|
1170
|
+
return "boolean";
|
|
1171
|
+
case "object":
|
|
1172
|
+
return "object";
|
|
1173
|
+
default:
|
|
1174
|
+
throw new Error(`Unknown type: ${type}`);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
function applyNumericValidators(propertySchema, param) {
|
|
1178
|
+
for (const validator of param.validator) {
|
|
1179
|
+
if (validator.type === "MIN" && typeof validator.value === "number") {
|
|
1180
|
+
propertySchema.minimum = validator.value;
|
|
1181
|
+
}
|
|
1182
|
+
if (validator.type === "MAX" && typeof validator.value === "number") {
|
|
1183
|
+
propertySchema.maximum = validator.value;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
function applyStringValidators(propertySchema, param) {
|
|
1188
|
+
for (const validator of param.validator) {
|
|
1189
|
+
if (validator.type === "MIN" && typeof validator.value === "number") {
|
|
1190
|
+
propertySchema.minLength = validator.value;
|
|
1191
|
+
}
|
|
1192
|
+
if (validator.type === "MAX" && typeof validator.value === "number") {
|
|
1193
|
+
propertySchema.maxLength = validator.value;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
function applyArrayValidators(propertySchema, param) {
|
|
1198
|
+
for (const validator of param.validator) {
|
|
1199
|
+
if (validator.type === "MIN" && typeof validator.value === "number") {
|
|
1200
|
+
propertySchema.minItems = validator.value;
|
|
1201
|
+
}
|
|
1202
|
+
if (validator.type === "MAX" && typeof validator.value === "number") {
|
|
1203
|
+
propertySchema.maxItems = validator.value;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// src/restura/generators/customTypeValidationGenerator.ts
|
|
1209
|
+
function customTypeValidationGenerator(currentSchema, ignoreGeneratedTypes = false) {
|
|
1097
1210
|
const schemaObject = {};
|
|
1098
1211
|
const customInterfaceNames = currentSchema.customTypes.map((customType) => {
|
|
1099
1212
|
const matches = customType.match(/(?<=interface\s)(\w+)|(?<=type\s)(\w+)/g);
|
|
@@ -1111,9 +1224,11 @@ function customTypeValidationGenerator(currentSchema) {
|
|
|
1111
1224
|
const program = TJS.getProgramFromFiles(
|
|
1112
1225
|
[
|
|
1113
1226
|
resolve(temporaryFile.name),
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1227
|
+
...ignoreGeneratedTypes ? [] : [
|
|
1228
|
+
path2.join(restura.resturaConfig.generatedTypesPath, "restura.d.ts"),
|
|
1229
|
+
path2.join(restura.resturaConfig.generatedTypesPath, "models.d.ts"),
|
|
1230
|
+
path2.join(restura.resturaConfig.generatedTypesPath, "api.d.ts")
|
|
1231
|
+
]
|
|
1117
1232
|
],
|
|
1118
1233
|
compilerOptions
|
|
1119
1234
|
);
|
|
@@ -1124,6 +1239,35 @@ function customTypeValidationGenerator(currentSchema) {
|
|
|
1124
1239
|
schemaObject[item] = ddlSchema || {};
|
|
1125
1240
|
});
|
|
1126
1241
|
temporaryFile.removeCallback();
|
|
1242
|
+
for (const endpoint of currentSchema.endpoints) {
|
|
1243
|
+
for (const route of endpoint.routes) {
|
|
1244
|
+
if (route.type !== "CUSTOM_ONE" && route.type !== "CUSTOM_ARRAY" && route.type !== "CUSTOM_PAGED") continue;
|
|
1245
|
+
if (!route.request || !Array.isArray(route.request)) continue;
|
|
1246
|
+
const routeKey = `${route.method}:${route.path}`;
|
|
1247
|
+
schemaObject[routeKey] = buildRouteSchema(route.request);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return schemaObject;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/restura/generators/standardTypeValidationGenerator.ts
|
|
1254
|
+
function standardTypeValidationGenerator(currentSchema) {
|
|
1255
|
+
const schemaObject = {};
|
|
1256
|
+
for (const endpoint of currentSchema.endpoints) {
|
|
1257
|
+
for (const route of endpoint.routes) {
|
|
1258
|
+
if (route.type !== "ONE" && route.type !== "ARRAY" && route.type !== "PAGED") continue;
|
|
1259
|
+
const routeKey = `${route.method}:${route.path}`;
|
|
1260
|
+
if (!route.request || route.request.length === 0) {
|
|
1261
|
+
schemaObject[routeKey] = {
|
|
1262
|
+
type: "object",
|
|
1263
|
+
properties: {},
|
|
1264
|
+
additionalProperties: false
|
|
1265
|
+
};
|
|
1266
|
+
} else {
|
|
1267
|
+
schemaObject[routeKey] = buildRouteSchema(route.request);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1127
1271
|
return schemaObject;
|
|
1128
1272
|
}
|
|
1129
1273
|
|
|
@@ -1156,6 +1300,18 @@ function addApiResponseFunctions(req, res, next) {
|
|
|
1156
1300
|
next();
|
|
1157
1301
|
}
|
|
1158
1302
|
|
|
1303
|
+
// src/restura/middleware/addDeprecationResponse.ts
|
|
1304
|
+
function addDeprecationResponse(req, res, next) {
|
|
1305
|
+
const deprecation = req.routeData?.deprecation;
|
|
1306
|
+
if (deprecation) {
|
|
1307
|
+
const { date, message } = deprecation;
|
|
1308
|
+
const dateObject = new Date(date);
|
|
1309
|
+
res.set("Deprecation", `@${dateObject.getTime().toString()}`);
|
|
1310
|
+
res.set("Deprecation-Message", message ?? "This endpoint is deprecated and will be removed in the future.");
|
|
1311
|
+
}
|
|
1312
|
+
next();
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1159
1315
|
// src/restura/middleware/authenticateRequester.ts
|
|
1160
1316
|
function authenticateRequester(applicationAuthenticateHandler) {
|
|
1161
1317
|
return (req, res, next) => {
|
|
@@ -1278,6 +1434,10 @@ var routeDataBaseSchema = z3.object({
|
|
|
1278
1434
|
name: z3.string(),
|
|
1279
1435
|
description: z3.string(),
|
|
1280
1436
|
path: z3.string(),
|
|
1437
|
+
deprecation: z3.object({
|
|
1438
|
+
date: z3.iso.datetime(),
|
|
1439
|
+
message: z3.string().optional()
|
|
1440
|
+
}).optional(),
|
|
1281
1441
|
roles: z3.array(z3.string()),
|
|
1282
1442
|
scopes: z3.array(z3.string())
|
|
1283
1443
|
}).strict();
|
|
@@ -1430,7 +1590,8 @@ var indexDataSchema = z3.object({
|
|
|
1430
1590
|
columns: z3.array(z3.string()),
|
|
1431
1591
|
isUnique: z3.boolean(),
|
|
1432
1592
|
isPrimaryKey: z3.boolean(),
|
|
1433
|
-
order: z3.enum(["ASC", "DESC"])
|
|
1593
|
+
order: z3.enum(["ASC", "DESC"]),
|
|
1594
|
+
where: z3.string().optional()
|
|
1434
1595
|
}).strict();
|
|
1435
1596
|
var foreignKeyActionsSchema = z3.enum([
|
|
1436
1597
|
"CASCADE",
|
|
@@ -1496,174 +1657,41 @@ async function isSchemaValid(schemaToCheck) {
|
|
|
1496
1657
|
}
|
|
1497
1658
|
|
|
1498
1659
|
// src/restura/validators/requestValidator.ts
|
|
1499
|
-
import { ObjectUtils as ObjectUtils2 } from "@redskytech/core-utils";
|
|
1500
1660
|
import jsonschema from "jsonschema";
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
} else {
|
|
1511
|
-
return variable;
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
function sortObjectKeysAlphabetically(obj) {
|
|
1515
|
-
if (Array.isArray(obj)) {
|
|
1516
|
-
return obj.map(sortObjectKeysAlphabetically);
|
|
1517
|
-
} else if (obj !== null && typeof obj === "object") {
|
|
1518
|
-
return Object.keys(obj).sort().reduce((sorted, key) => {
|
|
1519
|
-
sorted[key] = sortObjectKeysAlphabetically(obj[key]);
|
|
1520
|
-
return sorted;
|
|
1521
|
-
}, {});
|
|
1522
|
-
}
|
|
1523
|
-
return obj;
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
// src/restura/validators/requestValidator.ts
|
|
1527
|
-
function requestValidator(req, routeData, validationSchema) {
|
|
1528
|
-
const requestData = getRequestData(req);
|
|
1529
|
-
req.data = requestData;
|
|
1530
|
-
if (routeData.request === void 0) {
|
|
1531
|
-
if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
|
|
1532
|
-
throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
|
|
1661
|
+
function requestValidator(req, routeData, customValidationSchema, standardValidationSchema) {
|
|
1662
|
+
let schemaForCoercion;
|
|
1663
|
+
if (routeData.type === "ONE" || routeData.type === "ARRAY" || routeData.type === "PAGED") {
|
|
1664
|
+
const routeKey = `${routeData.method}:${routeData.path}`;
|
|
1665
|
+
schemaForCoercion = standardValidationSchema[routeKey];
|
|
1666
|
+
if (!schemaForCoercion) {
|
|
1667
|
+
throw new RsError("BAD_REQUEST", `No schema found for standard request route: ${routeKey}.`);
|
|
1668
|
+
}
|
|
1669
|
+
} else if (routeData.type === "CUSTOM_ONE" || routeData.type === "CUSTOM_ARRAY" || routeData.type === "CUSTOM_PAGED") {
|
|
1533
1670
|
if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
|
|
1534
|
-
if (!routeData.requestType
|
|
1535
|
-
|
|
1536
|
-
const
|
|
1537
|
-
const
|
|
1671
|
+
if (!routeData.requestType && !routeData.request)
|
|
1672
|
+
throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
|
|
1673
|
+
const routeKey = `${routeData.method}:${routeData.path}`;
|
|
1674
|
+
const currentInterface = customValidationSchema[routeData.requestType || routeKey];
|
|
1675
|
+
schemaForCoercion = {
|
|
1538
1676
|
...currentInterface,
|
|
1539
1677
|
additionalProperties: false
|
|
1540
1678
|
};
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
throw new RsError(
|
|
1544
|
-
"BAD_REQUEST",
|
|
1545
|
-
`Request custom setup has failed the following check: (${executeValidation.errors})`
|
|
1546
|
-
);
|
|
1547
|
-
}
|
|
1548
|
-
return;
|
|
1549
|
-
}
|
|
1550
|
-
Object.keys(req.data).forEach((requestParamName) => {
|
|
1551
|
-
const requestParam = routeData.request.find((param) => param.name === requestParamName);
|
|
1552
|
-
if (!requestParam) {
|
|
1553
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
|
|
1554
|
-
}
|
|
1555
|
-
});
|
|
1556
|
-
routeData.request.forEach((requestParam) => {
|
|
1557
|
-
const requestValue = requestData[requestParam.name];
|
|
1558
|
-
if (requestParam.required && requestValue === void 0)
|
|
1559
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
|
|
1560
|
-
else if (!requestParam.required && requestValue === void 0) return;
|
|
1561
|
-
validateRequestSingleParam(requestValue, requestParam);
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
function validateRequestSingleParam(requestValue, requestParam) {
|
|
1565
|
-
if (requestParam.isNullable && requestValue === null) return;
|
|
1566
|
-
requestParam.validator.forEach((validator) => {
|
|
1567
|
-
switch (validator.type) {
|
|
1568
|
-
case "TYPE_CHECK":
|
|
1569
|
-
performTypeCheck(requestValue, validator, requestParam.name);
|
|
1570
|
-
break;
|
|
1571
|
-
case "MIN":
|
|
1572
|
-
performMinCheck(requestValue, validator, requestParam.name);
|
|
1573
|
-
break;
|
|
1574
|
-
case "MAX":
|
|
1575
|
-
performMaxCheck(requestValue, validator, requestParam.name);
|
|
1576
|
-
break;
|
|
1577
|
-
case "ONE_OF":
|
|
1578
|
-
performOneOfCheck(requestValue, validator, requestParam.name);
|
|
1579
|
-
break;
|
|
1580
|
-
}
|
|
1581
|
-
});
|
|
1582
|
-
}
|
|
1583
|
-
function isValidType(type, requestValue) {
|
|
1584
|
-
try {
|
|
1585
|
-
expectValidType(type, requestValue);
|
|
1586
|
-
return true;
|
|
1587
|
-
} catch {
|
|
1588
|
-
return false;
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
function expectValidType(type, requestValue) {
|
|
1592
|
-
if (type === "number") {
|
|
1593
|
-
return z4.number().parse(requestValue);
|
|
1594
|
-
}
|
|
1595
|
-
if (type === "string") {
|
|
1596
|
-
return z4.string().parse(requestValue);
|
|
1597
|
-
}
|
|
1598
|
-
if (type === "boolean") {
|
|
1599
|
-
return z4.boolean().parse(requestValue);
|
|
1600
|
-
}
|
|
1601
|
-
if (type === "string[]") {
|
|
1602
|
-
return z4.array(z4.string()).parse(requestValue);
|
|
1603
|
-
}
|
|
1604
|
-
if (type === "number[]") {
|
|
1605
|
-
return z4.array(z4.number()).parse(requestValue);
|
|
1606
|
-
}
|
|
1607
|
-
if (type === "any[]") {
|
|
1608
|
-
return z4.array(z4.any()).parse(requestValue);
|
|
1609
|
-
}
|
|
1610
|
-
if (type === "object") {
|
|
1611
|
-
return z4.object({}).strict().parse(requestValue);
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
function performTypeCheck(requestValue, validator, requestParamName) {
|
|
1615
|
-
if (!isValidType(validator.value, requestValue)) {
|
|
1616
|
-
throw new RsError(
|
|
1617
|
-
"BAD_REQUEST",
|
|
1618
|
-
`Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
|
|
1619
|
-
);
|
|
1679
|
+
} else {
|
|
1680
|
+
throw new RsError("BAD_REQUEST", `Invalid route type: ${routeData.type}`);
|
|
1620
1681
|
}
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1682
|
+
const requestData = getRequestData(req, schemaForCoercion);
|
|
1683
|
+
req.data = requestData;
|
|
1684
|
+
const validator = new jsonschema.Validator();
|
|
1685
|
+
const executeValidation = validator.validate(req.data, schemaForCoercion);
|
|
1686
|
+
if (!executeValidation.valid) {
|
|
1687
|
+
const errorMessages = executeValidation.errors.map((err) => {
|
|
1688
|
+
const property = err.property.replace("instance.", "");
|
|
1689
|
+
return `${property}: ${err.message}`;
|
|
1690
|
+
}).join(", ");
|
|
1691
|
+
throw new RsError("BAD_REQUEST", `Request validation failed: ${errorMessages}`);
|
|
1625
1692
|
}
|
|
1626
1693
|
}
|
|
1627
|
-
function
|
|
1628
|
-
if (!isValueNumber(requestValue))
|
|
1629
|
-
throw new RsError(
|
|
1630
|
-
"BAD_REQUEST",
|
|
1631
|
-
`Request param (${requestParamName}) with value (${requestValue}) is not of type number`
|
|
1632
|
-
);
|
|
1633
|
-
if (!isValueNumber(validator.value))
|
|
1634
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
|
|
1635
|
-
}
|
|
1636
|
-
function performMinCheck(requestValue, validator, requestParamName) {
|
|
1637
|
-
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
1638
|
-
if (requestValue < validator.value)
|
|
1639
|
-
throw new RsError(
|
|
1640
|
-
"BAD_REQUEST",
|
|
1641
|
-
`Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
|
|
1642
|
-
);
|
|
1643
|
-
}
|
|
1644
|
-
function performMaxCheck(requestValue, validator, requestParamName) {
|
|
1645
|
-
expectOnlyNumbers(requestValue, validator, requestParamName);
|
|
1646
|
-
if (requestValue > validator.value)
|
|
1647
|
-
throw new RsError(
|
|
1648
|
-
"BAD_REQUEST",
|
|
1649
|
-
`Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
|
|
1650
|
-
);
|
|
1651
|
-
}
|
|
1652
|
-
function performOneOfCheck(requestValue, validator, requestParamName) {
|
|
1653
|
-
if (!ObjectUtils2.isArrayWithData(validator.value))
|
|
1654
|
-
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
|
|
1655
|
-
if (typeof requestValue === "object")
|
|
1656
|
-
throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
|
|
1657
|
-
if (!validator.value.includes(requestValue))
|
|
1658
|
-
throw new RsError(
|
|
1659
|
-
"BAD_REQUEST",
|
|
1660
|
-
`Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
|
|
1661
|
-
);
|
|
1662
|
-
}
|
|
1663
|
-
function isValueNumber(value) {
|
|
1664
|
-
return !isNaN(Number(value));
|
|
1665
|
-
}
|
|
1666
|
-
function getRequestData(req) {
|
|
1694
|
+
function getRequestData(req, schema) {
|
|
1667
1695
|
let body = "";
|
|
1668
1696
|
if (req.method === "GET" || req.method === "DELETE") {
|
|
1669
1697
|
body = "query";
|
|
@@ -1671,50 +1699,74 @@ function getRequestData(req) {
|
|
|
1671
1699
|
body = "body";
|
|
1672
1700
|
}
|
|
1673
1701
|
const bodyData = req[body];
|
|
1674
|
-
if (bodyData && body === "query") {
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1702
|
+
if (bodyData && body === "query" && schema) {
|
|
1703
|
+
return coerceBySchema(bodyData, schema);
|
|
1704
|
+
}
|
|
1705
|
+
return bodyData;
|
|
1706
|
+
}
|
|
1707
|
+
function coerceBySchema(data, schema) {
|
|
1708
|
+
const normalized = {};
|
|
1709
|
+
const properties = schema.properties || {};
|
|
1710
|
+
for (const attr in data) {
|
|
1711
|
+
const cleanAttr = attr.replace(/\[\]$/, "");
|
|
1712
|
+
const isArrayNotation = attr.includes("[]");
|
|
1713
|
+
let value = data[attr];
|
|
1714
|
+
const propertySchema = properties[cleanAttr];
|
|
1715
|
+
if (isArrayNotation && !Array.isArray(value)) {
|
|
1716
|
+
value = [value];
|
|
1717
|
+
}
|
|
1718
|
+
if (!propertySchema) {
|
|
1719
|
+
normalized[cleanAttr] = value;
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
if (Array.isArray(value)) {
|
|
1723
|
+
const itemSchema = Array.isArray(propertySchema.items) ? propertySchema.items[0] : propertySchema.items || { type: "string" };
|
|
1724
|
+
normalized[cleanAttr] = value.map((item) => coerceValue(item, itemSchema));
|
|
1725
|
+
} else {
|
|
1726
|
+
normalized[cleanAttr] = coerceValue(value, propertySchema);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
return normalized;
|
|
1730
|
+
}
|
|
1731
|
+
function coerceValue(value, propertySchema) {
|
|
1732
|
+
if (value === void 0 || value === null) {
|
|
1733
|
+
return value;
|
|
1734
|
+
}
|
|
1735
|
+
const targetType = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
|
|
1736
|
+
if (value === "") {
|
|
1737
|
+
return targetType === "string" ? "" : void 0;
|
|
1738
|
+
}
|
|
1739
|
+
switch (targetType) {
|
|
1740
|
+
case "number":
|
|
1741
|
+
case "integer":
|
|
1742
|
+
const num = Number(value);
|
|
1743
|
+
return isNaN(num) ? value : num;
|
|
1744
|
+
case "boolean":
|
|
1745
|
+
if (value === "true") return true;
|
|
1746
|
+
if (value === "false") return false;
|
|
1747
|
+
if (typeof value === "string") {
|
|
1748
|
+
return value === "true" || value === "1";
|
|
1679
1749
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
return
|
|
1689
|
-
});
|
|
1690
|
-
normalizedData[cleanAttr] = parsedList;
|
|
1691
|
-
} else {
|
|
1692
|
-
let value = bodyData[attr];
|
|
1693
|
-
if (value === "true") {
|
|
1694
|
-
value = true;
|
|
1695
|
-
} else if (value === "false") {
|
|
1696
|
-
value = false;
|
|
1697
|
-
} else if (value === void 0) {
|
|
1698
|
-
value = void 0;
|
|
1699
|
-
} else if (value === "") {
|
|
1700
|
-
value = "";
|
|
1701
|
-
} else {
|
|
1702
|
-
value = ObjectUtils2.safeParse(value);
|
|
1703
|
-
if (!isNaN(Number(value))) {
|
|
1704
|
-
value = Number(value);
|
|
1705
|
-
}
|
|
1750
|
+
return Boolean(value);
|
|
1751
|
+
case "string":
|
|
1752
|
+
return String(value);
|
|
1753
|
+
case "object":
|
|
1754
|
+
if (typeof value === "string") {
|
|
1755
|
+
try {
|
|
1756
|
+
return JSON.parse(value);
|
|
1757
|
+
} catch {
|
|
1758
|
+
return value;
|
|
1706
1759
|
}
|
|
1707
|
-
normalizedData[cleanAttr] = value;
|
|
1708
1760
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1761
|
+
return value;
|
|
1762
|
+
default:
|
|
1763
|
+
return value;
|
|
1711
1764
|
}
|
|
1712
|
-
return bodyData;
|
|
1713
1765
|
}
|
|
1714
1766
|
|
|
1715
1767
|
// src/restura/middleware/schemaValidation.ts
|
|
1716
1768
|
async function schemaValidation(req, res, next) {
|
|
1717
|
-
req.data = getRequestData(req);
|
|
1769
|
+
req.data = getRequestData(req, {});
|
|
1718
1770
|
try {
|
|
1719
1771
|
resturaSchema.parse(req.data);
|
|
1720
1772
|
next();
|
|
@@ -1725,22 +1777,22 @@ async function schemaValidation(req, res, next) {
|
|
|
1725
1777
|
}
|
|
1726
1778
|
|
|
1727
1779
|
// src/restura/schemas/resturaConfigSchema.ts
|
|
1728
|
-
import { z as
|
|
1780
|
+
import { z as z4 } from "zod";
|
|
1729
1781
|
var isTsx = process.argv[1]?.endsWith(".ts");
|
|
1730
1782
|
var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
|
|
1731
1783
|
var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
|
|
1732
|
-
var resturaConfigSchema =
|
|
1733
|
-
authToken:
|
|
1734
|
-
sendErrorStackTrace:
|
|
1735
|
-
schemaFilePath:
|
|
1736
|
-
customApiFolderPath:
|
|
1737
|
-
generatedTypesPath:
|
|
1738
|
-
fileTempCachePath:
|
|
1739
|
-
scratchDatabaseSuffix:
|
|
1784
|
+
var resturaConfigSchema = z4.object({
|
|
1785
|
+
authToken: z4.string().min(1, "Missing Restura Auth Token"),
|
|
1786
|
+
sendErrorStackTrace: z4.boolean().default(false),
|
|
1787
|
+
schemaFilePath: z4.string().default(process.cwd() + "/restura.schema.json"),
|
|
1788
|
+
customApiFolderPath: z4.string().default(process.cwd() + customApiFolderPath),
|
|
1789
|
+
generatedTypesPath: z4.string().default(process.cwd() + "/src/@types"),
|
|
1790
|
+
fileTempCachePath: z4.string().optional(),
|
|
1791
|
+
scratchDatabaseSuffix: z4.string().optional()
|
|
1740
1792
|
});
|
|
1741
1793
|
|
|
1742
1794
|
// src/restura/sql/PsqlEngine.ts
|
|
1743
|
-
import { ObjectUtils as
|
|
1795
|
+
import { ObjectUtils as ObjectUtils3 } from "@redskytech/core-utils";
|
|
1744
1796
|
import getDiff from "@wmfs/pg-diff-sync";
|
|
1745
1797
|
import pgInfo from "@wmfs/pg-info";
|
|
1746
1798
|
import pg2 from "pg";
|
|
@@ -1752,7 +1804,7 @@ import pg from "pg";
|
|
|
1752
1804
|
import crypto from "crypto";
|
|
1753
1805
|
import format2 from "pg-format";
|
|
1754
1806
|
import { format as sqlFormat } from "sql-formatter";
|
|
1755
|
-
import { z as
|
|
1807
|
+
import { z as z5 } from "zod";
|
|
1756
1808
|
|
|
1757
1809
|
// src/restura/sql/PsqlUtils.ts
|
|
1758
1810
|
import format from "pg-format";
|
|
@@ -1788,13 +1840,15 @@ function insertObjectQuery(table, obj) {
|
|
|
1788
1840
|
INSERT INTO "${table}" (${columns})
|
|
1789
1841
|
VALUES (${values})
|
|
1790
1842
|
RETURNING *`;
|
|
1791
|
-
query = query.replace(/'(\?)'
|
|
1843
|
+
query = query.replace(/'(\?)'/g, "?");
|
|
1792
1844
|
return query;
|
|
1793
1845
|
}
|
|
1794
1846
|
function updateObjectQuery(table, obj, whereStatement, incrementSyncVersion = false) {
|
|
1795
1847
|
const setArray = [];
|
|
1796
1848
|
for (const i in obj) {
|
|
1797
|
-
|
|
1849
|
+
let value = SQL`${obj[i]}`;
|
|
1850
|
+
value = value.replace(/'(\?)'/g, "?");
|
|
1851
|
+
setArray.push(`${escapeColumnName(i)} = ` + value);
|
|
1798
1852
|
}
|
|
1799
1853
|
if (incrementSyncVersion) {
|
|
1800
1854
|
setArray.push(`"syncVersion" = "syncVersion" + 1`);
|
|
@@ -1804,7 +1858,7 @@ function updateObjectQuery(table, obj, whereStatement, incrementSyncVersion = fa
|
|
|
1804
1858
|
SET ${setArray.join(", ")} ${whereStatement}
|
|
1805
1859
|
RETURNING *`;
|
|
1806
1860
|
}
|
|
1807
|
-
function
|
|
1861
|
+
function isValueNumber(value) {
|
|
1808
1862
|
return !isNaN(Number(value));
|
|
1809
1863
|
}
|
|
1810
1864
|
function SQL(strings, ...values) {
|
|
@@ -1856,10 +1910,10 @@ var PsqlConnection = class {
|
|
|
1856
1910
|
try {
|
|
1857
1911
|
return zodSchema.parse(result);
|
|
1858
1912
|
} catch (error) {
|
|
1859
|
-
if (error instanceof
|
|
1913
|
+
if (error instanceof z5.ZodError) {
|
|
1860
1914
|
logger.error("Invalid data returned from database:");
|
|
1861
1915
|
logger.silly("\n" + JSON.stringify(result, null, 2));
|
|
1862
|
-
logger.error("\n" +
|
|
1916
|
+
logger.error("\n" + z5.prettifyError(error));
|
|
1863
1917
|
} else {
|
|
1864
1918
|
logger.error(error);
|
|
1865
1919
|
}
|
|
@@ -1887,12 +1941,12 @@ var PsqlConnection = class {
|
|
|
1887
1941
|
async runQuerySchema(query, params, requesterDetails, zodSchema) {
|
|
1888
1942
|
const result = await this.runQuery(query, params, requesterDetails);
|
|
1889
1943
|
try {
|
|
1890
|
-
return
|
|
1944
|
+
return z5.array(zodSchema).parse(result);
|
|
1891
1945
|
} catch (error) {
|
|
1892
|
-
if (error instanceof
|
|
1946
|
+
if (error instanceof z5.ZodError) {
|
|
1893
1947
|
logger.error("Invalid data returned from database:");
|
|
1894
1948
|
logger.silly("\n" + JSON.stringify(result, null, 2));
|
|
1895
|
-
logger.error("\n" +
|
|
1949
|
+
logger.error("\n" + z5.prettifyError(error));
|
|
1896
1950
|
} else {
|
|
1897
1951
|
logger.error(error);
|
|
1898
1952
|
}
|
|
@@ -1932,8 +1986,7 @@ var PsqlConnection = class {
|
|
|
1932
1986
|
if ("isSystemUser" in queryMetadata && queryMetadata.isSystemUser) initiator = "SYSTEM";
|
|
1933
1987
|
logger.silly(`${prefix}query by ${initiator}, Query ->
|
|
1934
1988
|
${formattedSql}`, {
|
|
1935
|
-
|
|
1936
|
-
_meta: { durationNs: nanoseconds }
|
|
1989
|
+
durationMs
|
|
1937
1990
|
});
|
|
1938
1991
|
}
|
|
1939
1992
|
};
|
|
@@ -1965,7 +2018,7 @@ var PsqlPool = class extends PsqlConnection {
|
|
|
1965
2018
|
};
|
|
1966
2019
|
|
|
1967
2020
|
// src/restura/sql/SqlEngine.ts
|
|
1968
|
-
import { ObjectUtils as
|
|
2021
|
+
import { ObjectUtils as ObjectUtils2 } from "@redskytech/core-utils";
|
|
1969
2022
|
var SqlEngine = class {
|
|
1970
2023
|
async runQueryForRoute(req, routeData, schema) {
|
|
1971
2024
|
if (!this.canRequesterAccessTable(
|
|
@@ -2008,18 +2061,18 @@ var SqlEngine = class {
|
|
|
2008
2061
|
const columnSchema = tableSchema.columns.find((item2) => item2.name === columnName);
|
|
2009
2062
|
if (!columnSchema)
|
|
2010
2063
|
throw new RsError("SCHEMA_ERROR", `Column ${columnName} not found in table ${tableName}`);
|
|
2011
|
-
if (
|
|
2064
|
+
if (ObjectUtils2.isArrayWithData(columnSchema.roles)) {
|
|
2012
2065
|
if (!requesterRole) return false;
|
|
2013
2066
|
return columnSchema.roles.includes(requesterRole);
|
|
2014
2067
|
}
|
|
2015
|
-
if (
|
|
2068
|
+
if (ObjectUtils2.isArrayWithData(columnSchema.scopes)) {
|
|
2016
2069
|
if (!requesterScopes) return false;
|
|
2017
2070
|
return columnSchema.scopes.every((scope) => requesterScopes.includes(scope));
|
|
2018
2071
|
}
|
|
2019
2072
|
return true;
|
|
2020
2073
|
}
|
|
2021
2074
|
if (item.subquery) {
|
|
2022
|
-
return
|
|
2075
|
+
return ObjectUtils2.isArrayWithData(
|
|
2023
2076
|
item.subquery.properties.filter((nestedItem) => {
|
|
2024
2077
|
return this.canRequesterAccessColumn(requesterRole, requesterScopes, schema, nestedItem, joins);
|
|
2025
2078
|
})
|
|
@@ -2029,11 +2082,11 @@ var SqlEngine = class {
|
|
|
2029
2082
|
}
|
|
2030
2083
|
canRequesterAccessTable(requesterRole, requesterScopes, schema, tableName) {
|
|
2031
2084
|
const tableSchema = this.getTableSchema(schema, tableName);
|
|
2032
|
-
if (
|
|
2085
|
+
if (ObjectUtils2.isArrayWithData(tableSchema.roles)) {
|
|
2033
2086
|
if (!requesterRole) return false;
|
|
2034
2087
|
return tableSchema.roles.includes(requesterRole);
|
|
2035
2088
|
}
|
|
2036
|
-
if (
|
|
2089
|
+
if (ObjectUtils2.isArrayWithData(tableSchema.scopes)) {
|
|
2037
2090
|
if (!requesterScopes) return false;
|
|
2038
2091
|
return tableSchema.scopes.some((scope) => requesterScopes.includes(scope));
|
|
2039
2092
|
}
|
|
@@ -2309,7 +2362,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2309
2362
|
});
|
|
2310
2363
|
this.triggerClient.on("notification", async (msg) => {
|
|
2311
2364
|
if (msg.channel === "insert" || msg.channel === "update" || msg.channel === "delete") {
|
|
2312
|
-
const payload =
|
|
2365
|
+
const payload = ObjectUtils3.safeParse(msg.payload);
|
|
2313
2366
|
await this.handleTrigger(payload, msg.channel.toUpperCase());
|
|
2314
2367
|
}
|
|
2315
2368
|
});
|
|
@@ -2373,11 +2426,11 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2373
2426
|
if (!index.isPrimaryKey) {
|
|
2374
2427
|
let unique = " ";
|
|
2375
2428
|
if (index.isUnique) unique = "UNIQUE ";
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
);
|
|
2429
|
+
let indexSQL = ` CREATE ${unique}INDEX "${index.name}" ON "${table.name}"`;
|
|
2430
|
+
indexSQL += ` (${index.columns.map((item) => `"${item}" ${index.order}`).join(", ")})`;
|
|
2431
|
+
indexSQL += index.where ? ` WHERE ${index.where}` : "";
|
|
2432
|
+
indexSQL += ";";
|
|
2433
|
+
indexes.push(indexSQL);
|
|
2381
2434
|
}
|
|
2382
2435
|
}
|
|
2383
2436
|
sql += "\n);";
|
|
@@ -2483,7 +2536,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2483
2536
|
}
|
|
2484
2537
|
createNestedSelect(req, schema, item, routeData, sqlParams) {
|
|
2485
2538
|
if (!item.subquery) return "";
|
|
2486
|
-
if (!
|
|
2539
|
+
if (!ObjectUtils3.isArrayWithData(
|
|
2487
2540
|
item.subquery.properties.filter((nestedItem) => {
|
|
2488
2541
|
return this.canRequesterAccessColumn(
|
|
2489
2542
|
req.requesterDetails.role,
|
|
@@ -2519,7 +2572,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2519
2572
|
}
|
|
2520
2573
|
return `'${nestedItem.name}', ${escapeColumnName(nestedItem.selector)}`;
|
|
2521
2574
|
}).filter(Boolean).join(", ")}
|
|
2522
|
-
))
|
|
2575
|
+
))
|
|
2523
2576
|
FROM
|
|
2524
2577
|
"${item.subquery.table}"
|
|
2525
2578
|
${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, sqlParams)}
|
|
@@ -2627,7 +2680,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2627
2680
|
);
|
|
2628
2681
|
const [pageResults, totalResponse] = await Promise.all([pagePromise, totalPromise]);
|
|
2629
2682
|
let total = 0;
|
|
2630
|
-
if (
|
|
2683
|
+
if (ObjectUtils3.isArrayWithData(totalResponse)) {
|
|
2631
2684
|
total = totalResponse[0].total;
|
|
2632
2685
|
}
|
|
2633
2686
|
return { data: pageResults, total };
|
|
@@ -2657,14 +2710,9 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2657
2710
|
}
|
|
2658
2711
|
let incrementSyncVersion = false;
|
|
2659
2712
|
if (table.columns.find((column) => column.name === "syncVersion")) incrementSyncVersion = true;
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
const assignmentEscaped = escapeColumnName(assignment.name);
|
|
2664
|
-
if (SqlUtils.convertDatabaseTypeToTypescript(column.type) === "number")
|
|
2665
|
-
bodyNoId[assignmentEscaped] = Number(assignment.value);
|
|
2666
|
-
else bodyNoId[assignmentEscaped] = assignment.value;
|
|
2667
|
-
}
|
|
2713
|
+
(routeData.assignments || []).forEach((assignment) => {
|
|
2714
|
+
bodyNoId[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
|
|
2715
|
+
});
|
|
2668
2716
|
let whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
2669
2717
|
const originalWhereClause = whereClause;
|
|
2670
2718
|
const originalSqlParams = [...sqlParams];
|
|
@@ -3088,6 +3136,19 @@ var TempCache = class {
|
|
|
3088
3136
|
}
|
|
3089
3137
|
};
|
|
3090
3138
|
|
|
3139
|
+
// src/restura/utils/utils.ts
|
|
3140
|
+
function sortObjectKeysAlphabetically(obj) {
|
|
3141
|
+
if (Array.isArray(obj)) {
|
|
3142
|
+
return obj.map(sortObjectKeysAlphabetically);
|
|
3143
|
+
} else if (obj !== null && typeof obj === "object") {
|
|
3144
|
+
return Object.keys(obj).sort().reduce((sorted, key) => {
|
|
3145
|
+
sorted[key] = sortObjectKeysAlphabetically(obj[key]);
|
|
3146
|
+
return sorted;
|
|
3147
|
+
}, {});
|
|
3148
|
+
}
|
|
3149
|
+
return obj;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3091
3152
|
// src/restura/restura.ts
|
|
3092
3153
|
var ResturaEngine = class {
|
|
3093
3154
|
// Make public so other modules can access without re-parsing the config
|
|
@@ -3106,6 +3167,7 @@ var ResturaEngine = class {
|
|
|
3106
3167
|
responseValidator;
|
|
3107
3168
|
authenticationHandler;
|
|
3108
3169
|
customTypeValidation;
|
|
3170
|
+
standardTypeValidation;
|
|
3109
3171
|
psqlConnectionPool;
|
|
3110
3172
|
psqlEngine;
|
|
3111
3173
|
/**
|
|
@@ -3215,7 +3277,7 @@ var ResturaEngine = class {
|
|
|
3215
3277
|
throw new Error("Missing restura schema file");
|
|
3216
3278
|
}
|
|
3217
3279
|
const schemaFileData = fs4.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
|
|
3218
|
-
const schema =
|
|
3280
|
+
const schema = ObjectUtils4.safeParse(schemaFileData);
|
|
3219
3281
|
const isValid = await isSchemaValid(schema);
|
|
3220
3282
|
if (!isValid) {
|
|
3221
3283
|
logger.error("Schema is not valid");
|
|
@@ -3226,6 +3288,7 @@ var ResturaEngine = class {
|
|
|
3226
3288
|
async reloadEndpoints() {
|
|
3227
3289
|
this.schema = await this.getLatestFileSystemSchema();
|
|
3228
3290
|
this.customTypeValidation = customTypeValidationGenerator(this.schema);
|
|
3291
|
+
this.standardTypeValidation = standardTypeValidationGenerator(this.schema);
|
|
3229
3292
|
this.resturaRouter = express.Router();
|
|
3230
3293
|
this.resetPublicEndpoints();
|
|
3231
3294
|
let routeCount = 0;
|
|
@@ -3243,6 +3306,8 @@ var ResturaEngine = class {
|
|
|
3243
3306
|
this.resturaRouter[route.method.toLowerCase()](
|
|
3244
3307
|
route.path,
|
|
3245
3308
|
// <-- Notice we only use path here since the baseUrl is already added to the router.
|
|
3309
|
+
this.attachRouteData,
|
|
3310
|
+
addDeprecationResponse,
|
|
3246
3311
|
this.executeRouteLogic
|
|
3247
3312
|
);
|
|
3248
3313
|
routeCount++;
|
|
@@ -3320,12 +3385,25 @@ var ResturaEngine = class {
|
|
|
3320
3385
|
});
|
|
3321
3386
|
});
|
|
3322
3387
|
}
|
|
3388
|
+
attachRouteData(req, _res, next) {
|
|
3389
|
+
try {
|
|
3390
|
+
req.routeData = this.getRouteData(req.method, req.baseUrl, req.path);
|
|
3391
|
+
next();
|
|
3392
|
+
} catch (e) {
|
|
3393
|
+
next(e);
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3323
3396
|
async executeRouteLogic(req, res, next) {
|
|
3324
3397
|
try {
|
|
3325
|
-
const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
|
|
3398
|
+
const routeData = req.routeData ?? this.getRouteData(req.method, req.baseUrl, req.path);
|
|
3326
3399
|
this.validateAuthorization(req, routeData);
|
|
3327
3400
|
await this.getMulterFilesIfAny(req, res, routeData);
|
|
3328
|
-
requestValidator(
|
|
3401
|
+
requestValidator(
|
|
3402
|
+
req,
|
|
3403
|
+
routeData,
|
|
3404
|
+
this.customTypeValidation,
|
|
3405
|
+
this.standardTypeValidation
|
|
3406
|
+
);
|
|
3329
3407
|
if (this.isCustomRoute(routeData)) {
|
|
3330
3408
|
await this.runCustomRouteLogic(req, res, routeData);
|
|
3331
3409
|
return;
|
|
@@ -3439,6 +3517,9 @@ __decorateClass([
|
|
|
3439
3517
|
__decorateClass([
|
|
3440
3518
|
boundMethod
|
|
3441
3519
|
], ResturaEngine.prototype, "getMulterFilesIfAny", 1);
|
|
3520
|
+
__decorateClass([
|
|
3521
|
+
boundMethod
|
|
3522
|
+
], ResturaEngine.prototype, "attachRouteData", 1);
|
|
3442
3523
|
__decorateClass([
|
|
3443
3524
|
boundMethod
|
|
3444
3525
|
], ResturaEngine.prototype, "executeRouteLogic", 1);
|
|
@@ -3502,7 +3583,7 @@ export {
|
|
|
3502
3583
|
filterPsqlParser_default as filterPsqlParser,
|
|
3503
3584
|
insertObjectQuery,
|
|
3504
3585
|
isSchemaValid,
|
|
3505
|
-
|
|
3586
|
+
isValueNumber,
|
|
3506
3587
|
logger,
|
|
3507
3588
|
modelGenerator,
|
|
3508
3589
|
questionMarksToOrderedParams,
|