@restura/core 1.2.0 → 1.4.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 +26 -1
- package/dist/index.js +65 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1070,6 +1070,20 @@ declare abstract class SqlEngine {
|
|
|
1070
1070
|
protected abstract generateOrderBy(req: RsRequest<unknown>, routeData: StandardRouteData): string;
|
|
1071
1071
|
protected abstract generateWhereClause(req: RsRequest<unknown>, where: WhereData[], routeData: StandardRouteData, sqlParams: string[]): string;
|
|
1072
1072
|
protected replaceParamKeywords(value: string | number, routeData: RouteData, req: RsRequest<unknown>, sqlParams: string[]): string | number;
|
|
1073
|
+
/**
|
|
1074
|
+
* Replaces local parameter keywords (e.g., $paramName) in SQL query strings with '?' placeholders
|
|
1075
|
+
* and adds the corresponding parameter values to the sqlParams array for parameterized queries.
|
|
1076
|
+
*
|
|
1077
|
+
* Validates that each parameter keyword exists in the route's request schema before processing.
|
|
1078
|
+
* If the value is not a string or routeData has no request schema, returns the value unchanged.
|
|
1079
|
+
*
|
|
1080
|
+
* @param value - The string or number value that may contain parameter keywords (e.g., "$userId")
|
|
1081
|
+
* @param routeData - The route data containing the request schema for parameter validation
|
|
1082
|
+
* @param req - The request object containing the actual parameter values in req.data
|
|
1083
|
+
* @param sqlParams - Array to which parameter values are appended (modified by reference)
|
|
1084
|
+
* @returns The value with parameter keywords replaced by '?' placeholders, or the original value if unchanged
|
|
1085
|
+
* @throws {RsError} If a parameter keyword is found that doesn't exist in the route's request schema
|
|
1086
|
+
*/
|
|
1073
1087
|
protected replaceLocalParamKeywords(value: string | number, routeData: RouteData, req: RsRequest<unknown>, sqlParams: string[]): string | number;
|
|
1074
1088
|
protected replaceGlobalParamKeywords(value: string | number, routeData: RouteData, req: RsRequest<unknown>, sqlParams: string[]): string | number;
|
|
1075
1089
|
abstract generateDatabaseSchemaFromSchema(schema: ResturaSchema): string;
|
|
@@ -1102,6 +1116,16 @@ declare class PsqlEngine extends SqlEngine {
|
|
|
1102
1116
|
protected createNestedSelect(req: RsRequest<unknown>, schema: ResturaSchema, item: ResponseData, routeData: StandardRouteData, sqlParams: string[]): string;
|
|
1103
1117
|
protected executeCreateRequest(req: RsRequest<unknown>, routeData: StandardRouteData, schema: ResturaSchema): Promise<DynamicObject>;
|
|
1104
1118
|
protected executeGetRequest(req: RsRequest<unknown>, routeData: StandardRouteData, schema: ResturaSchema): Promise<DynamicObject | any[]>;
|
|
1119
|
+
/**
|
|
1120
|
+
* Executes an update request. The request will pull out the id and baseSyncVersion from the request body.
|
|
1121
|
+
* (If Present) The baseSyncVersion is used to check if the record has been modified since the last sync.
|
|
1122
|
+
* If the update fails because the baseSyncVersion has changed, a conflict error will be thrown.
|
|
1123
|
+
* IDs can not be updated using this method.
|
|
1124
|
+
* @param req - The request object.
|
|
1125
|
+
* @param routeData - The route data object.
|
|
1126
|
+
* @param schema - The schema object.
|
|
1127
|
+
* @returns The response object.
|
|
1128
|
+
*/
|
|
1105
1129
|
protected executeUpdateRequest(req: RsRequest<unknown>, routeData: StandardRouteData, schema: ResturaSchema): Promise<DynamicObject>;
|
|
1106
1130
|
protected executeDeleteRequest(req: RsRequest<unknown>, routeData: StandardRouteData, schema: ResturaSchema): Promise<boolean>;
|
|
1107
1131
|
protected generateJoinStatements(req: RsRequest<unknown>, joins: JoinData[], baseTable: string, routeData: StandardRouteData | CustomRouteData, schema: ResturaSchema, sqlParams: string[]): string;
|
|
@@ -1157,9 +1181,10 @@ declare function insertObjectQuery(table: string, obj: DynamicObject): string;
|
|
|
1157
1181
|
* @param table Table name to update the object in
|
|
1158
1182
|
* @param obj Data to update in the table
|
|
1159
1183
|
* @param whereStatement Where clause to determine which rows to update
|
|
1184
|
+
* @param incrementSyncVersion Whether to increment the syncVersion column
|
|
1160
1185
|
* @returns the query to update the object in the table
|
|
1161
1186
|
*/
|
|
1162
|
-
declare function updateObjectQuery(table: string, obj: DynamicObject, whereStatement: string): string;
|
|
1187
|
+
declare function updateObjectQuery(table: string, obj: DynamicObject, whereStatement: string, incrementSyncVersion?: boolean): string;
|
|
1163
1188
|
declare function isValueNumber(value: unknown): value is number;
|
|
1164
1189
|
/**
|
|
1165
1190
|
* This method is used to format a query and escape user input.
|
package/dist/index.js
CHANGED
|
@@ -1791,15 +1791,18 @@ INSERT INTO "${table}" (${columns})
|
|
|
1791
1791
|
query = query.replace(/'(\?)'/, "?");
|
|
1792
1792
|
return query;
|
|
1793
1793
|
}
|
|
1794
|
-
function updateObjectQuery(table, obj, whereStatement) {
|
|
1794
|
+
function updateObjectQuery(table, obj, whereStatement, incrementSyncVersion = false) {
|
|
1795
1795
|
const setArray = [];
|
|
1796
1796
|
for (const i in obj) {
|
|
1797
1797
|
setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
|
|
1798
1798
|
}
|
|
1799
|
+
if (incrementSyncVersion) {
|
|
1800
|
+
setArray.push(`"syncVersion" = "syncVersion" + 1`);
|
|
1801
|
+
}
|
|
1799
1802
|
return `
|
|
1800
|
-
UPDATE ${escapeColumnName(table)}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
+
UPDATE ${escapeColumnName(table)}
|
|
1804
|
+
SET ${setArray.join(", ")} ${whereStatement}
|
|
1805
|
+
RETURNING *`;
|
|
1803
1806
|
}
|
|
1804
1807
|
function isValueNumber2(value) {
|
|
1805
1808
|
return !isNaN(Number(value));
|
|
@@ -1835,9 +1838,9 @@ var PsqlConnection = class {
|
|
|
1835
1838
|
const startTime = process.hrtime();
|
|
1836
1839
|
try {
|
|
1837
1840
|
const response = await this.query(queryMetadata + formattedQuery, options);
|
|
1838
|
-
this.logSqlStatement(formattedQuery, options, meta, startTime);
|
|
1839
1841
|
if (response.rows.length === 0) throw new RsError("NOT_FOUND", "No results found");
|
|
1840
1842
|
else if (response.rows.length > 1) throw new RsError("DUPLICATE", "More than one result found");
|
|
1843
|
+
this.logSqlStatement(formattedQuery, options, meta, startTime);
|
|
1841
1844
|
return response.rows[0];
|
|
1842
1845
|
} catch (error) {
|
|
1843
1846
|
this.logSqlStatement(formattedQuery, options, meta, startTime);
|
|
@@ -2042,6 +2045,20 @@ var SqlEngine = class {
|
|
|
2042
2045
|
returnValue = this.replaceGlobalParamKeywords(returnValue, routeData, req, sqlParams);
|
|
2043
2046
|
return returnValue;
|
|
2044
2047
|
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Replaces local parameter keywords (e.g., $paramName) in SQL query strings with '?' placeholders
|
|
2050
|
+
* and adds the corresponding parameter values to the sqlParams array for parameterized queries.
|
|
2051
|
+
*
|
|
2052
|
+
* Validates that each parameter keyword exists in the route's request schema before processing.
|
|
2053
|
+
* If the value is not a string or routeData has no request schema, returns the value unchanged.
|
|
2054
|
+
*
|
|
2055
|
+
* @param value - The string or number value that may contain parameter keywords (e.g., "$userId")
|
|
2056
|
+
* @param routeData - The route data containing the request schema for parameter validation
|
|
2057
|
+
* @param req - The request object containing the actual parameter values in req.data
|
|
2058
|
+
* @param sqlParams - Array to which parameter values are appended (modified by reference)
|
|
2059
|
+
* @returns The value with parameter keywords replaced by '?' placeholders, or the original value if unchanged
|
|
2060
|
+
* @throws {RsError} If a parameter keyword is found that doesn't exist in the route's request schema
|
|
2061
|
+
*/
|
|
2045
2062
|
replaceLocalParamKeywords(value, routeData, req, sqlParams) {
|
|
2046
2063
|
if (!routeData.request) return value;
|
|
2047
2064
|
const data = req.data;
|
|
@@ -2618,9 +2635,19 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2618
2635
|
throw new RsError("UNKNOWN_ERROR", "Unknown route type.");
|
|
2619
2636
|
}
|
|
2620
2637
|
}
|
|
2638
|
+
/**
|
|
2639
|
+
* Executes an update request. The request will pull out the id and baseSyncVersion from the request body.
|
|
2640
|
+
* (If Present) The baseSyncVersion is used to check if the record has been modified since the last sync.
|
|
2641
|
+
* If the update fails because the baseSyncVersion has changed, a conflict error will be thrown.
|
|
2642
|
+
* IDs can not be updated using this method.
|
|
2643
|
+
* @param req - The request object.
|
|
2644
|
+
* @param routeData - The route data object.
|
|
2645
|
+
* @param schema - The schema object.
|
|
2646
|
+
* @returns The response object.
|
|
2647
|
+
*/
|
|
2621
2648
|
async executeUpdateRequest(req, routeData, schema) {
|
|
2622
2649
|
const sqlParams = [];
|
|
2623
|
-
const { id, ...bodyNoId } = req.body;
|
|
2650
|
+
const { id, baseSyncVersion, ...bodyNoId } = req.body;
|
|
2624
2651
|
const table = schema.database.find((item) => {
|
|
2625
2652
|
return item.name === routeData.table;
|
|
2626
2653
|
});
|
|
@@ -2628,6 +2655,8 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2628
2655
|
if (table.columns.find((column) => column.name === "modifiedOn")) {
|
|
2629
2656
|
bodyNoId.modifiedOn = (/* @__PURE__ */ new Date()).toISOString();
|
|
2630
2657
|
}
|
|
2658
|
+
let incrementSyncVersion = false;
|
|
2659
|
+
if (table.columns.find((column) => column.name === "syncVersion")) incrementSyncVersion = true;
|
|
2631
2660
|
for (const assignment of routeData.assignments) {
|
|
2632
2661
|
const column = table.columns.find((column2) => column2.name === assignment.name);
|
|
2633
2662
|
if (!column) continue;
|
|
@@ -2636,9 +2665,36 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2636
2665
|
bodyNoId[assignmentEscaped] = Number(assignment.value);
|
|
2637
2666
|
else bodyNoId[assignmentEscaped] = assignment.value;
|
|
2638
2667
|
}
|
|
2639
|
-
|
|
2640
|
-
const
|
|
2641
|
-
|
|
2668
|
+
let whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
2669
|
+
const originalWhereClause = whereClause;
|
|
2670
|
+
const originalSqlParams = [...sqlParams];
|
|
2671
|
+
if (baseSyncVersion) {
|
|
2672
|
+
const syncVersionCheck = whereClause ? `${whereClause} AND "syncVersion" = ?` : `"syncVersion" = ?`;
|
|
2673
|
+
sqlParams.push(baseSyncVersion.toString());
|
|
2674
|
+
whereClause = syncVersionCheck;
|
|
2675
|
+
}
|
|
2676
|
+
const query = updateObjectQuery(routeData.table, bodyNoId, whereClause, incrementSyncVersion);
|
|
2677
|
+
try {
|
|
2678
|
+
await this.psqlConnectionPool.queryOne(query, [...sqlParams], req.requesterDetails);
|
|
2679
|
+
} catch (error) {
|
|
2680
|
+
if (!baseSyncVersion || !(error instanceof RsError) || error.err !== "NOT_FOUND") throw error;
|
|
2681
|
+
let isConflict = false;
|
|
2682
|
+
try {
|
|
2683
|
+
await this.psqlConnectionPool.queryOne(
|
|
2684
|
+
`SELECT 1 FROM "${routeData.table}" ${originalWhereClause};`,
|
|
2685
|
+
originalSqlParams,
|
|
2686
|
+
req.requesterDetails
|
|
2687
|
+
);
|
|
2688
|
+
isConflict = true;
|
|
2689
|
+
} catch {
|
|
2690
|
+
}
|
|
2691
|
+
if (isConflict)
|
|
2692
|
+
throw new RsError(
|
|
2693
|
+
"CONFLICT",
|
|
2694
|
+
"The record has been modified since the baseSyncVersion value was provided."
|
|
2695
|
+
);
|
|
2696
|
+
throw error;
|
|
2697
|
+
}
|
|
2642
2698
|
return this.executeGetRequest(req, routeData, schema);
|
|
2643
2699
|
}
|
|
2644
2700
|
async executeDeleteRequest(req, routeData, schema) {
|