@restura/core 0.1.0-alpha.15 → 0.1.0-alpha.16
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 +46 -17
- package/dist/index.d.ts +46 -17
- package/dist/index.js +241 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +241 -52
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import winston from 'winston';
|
|
2
2
|
import * as express from 'express';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { QueryResultRow, QueryConfigValues, QueryResult, PoolConfig, Pool
|
|
4
|
+
import { QueryResultRow, QueryConfigValues, QueryResult, PoolConfig, Pool } from 'pg';
|
|
5
5
|
import { IncomingHttpHeaders } from 'http2';
|
|
6
6
|
|
|
7
7
|
declare const logger: winston.Logger;
|
|
@@ -1392,10 +1392,10 @@ interface AsyncExpressApplication {
|
|
|
1392
1392
|
}
|
|
1393
1393
|
|
|
1394
1394
|
declare abstract class PsqlConnection {
|
|
1395
|
-
constructor();
|
|
1395
|
+
protected constructor();
|
|
1396
1396
|
protected abstract query<R extends QueryResultRow = QueryResultRow, T extends Array<unknown> = unknown[]>(query: string, values?: QueryConfigValues<T>): Promise<QueryResult<R>>;
|
|
1397
|
-
queryOne(query: string, options: any[], requesterDetails: RequesterDetails): Promise<
|
|
1398
|
-
runQuery(query: string, options: any[], requesterDetails: RequesterDetails): Promise<
|
|
1397
|
+
queryOne<T>(query: string, options: any[], requesterDetails: RequesterDetails): Promise<T>;
|
|
1398
|
+
runQuery<T>(query: string, options: any[], requesterDetails: RequesterDetails): Promise<T[]>;
|
|
1399
1399
|
private logSqlStatement;
|
|
1400
1400
|
}
|
|
1401
1401
|
|
|
@@ -1406,18 +1406,6 @@ declare class PsqlPool extends PsqlConnection {
|
|
|
1406
1406
|
protected query<R extends QueryResultRow = QueryResultRow, T extends Array<unknown> = unknown[]>(query: string, values?: QueryConfigValues<T>): Promise<QueryResult<R>>;
|
|
1407
1407
|
}
|
|
1408
1408
|
|
|
1409
|
-
declare class PsqlTransaction extends PsqlConnection {
|
|
1410
|
-
clientConfig: ClientConfig;
|
|
1411
|
-
client: Client;
|
|
1412
|
-
private beginTransactionPromise;
|
|
1413
|
-
constructor(clientConfig: ClientConfig);
|
|
1414
|
-
private beginTransaction;
|
|
1415
|
-
rollback(): Promise<QueryResult<QueryResultRow>>;
|
|
1416
|
-
commit(): Promise<QueryResult<QueryResultRow>>;
|
|
1417
|
-
release(): Promise<void>;
|
|
1418
|
-
protected query<R extends QueryResultRow = QueryResultRow, T extends Array<unknown> = unknown[]>(query: string, values?: QueryConfigValues<T>): Promise<QueryResult<R>>;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
1409
|
declare class ResturaEngine {
|
|
1422
1410
|
resturaConfig: ResturaConfigSchema;
|
|
1423
1411
|
private resturaRouter;
|
|
@@ -1516,4 +1504,45 @@ declare function updateObjectQuery(table: string, obj: DynamicObject, whereState
|
|
|
1516
1504
|
declare function isValueNumber(value: unknown): value is number;
|
|
1517
1505
|
declare function SQL(strings: any, ...values: any): any;
|
|
1518
1506
|
|
|
1519
|
-
|
|
1507
|
+
type EventType = 'DATABASE_ROW_DELETE' | 'DATABASE_ROW_INSERT' | 'DATABASE_COLUMN_UPDATE' | 'WEBHOOK';
|
|
1508
|
+
type MutationType = 'INSERT' | 'UPDATE' | 'DELETE';
|
|
1509
|
+
interface SqlMutationData {
|
|
1510
|
+
mutationType: MutationType;
|
|
1511
|
+
requesterDetails: RequesterDetails;
|
|
1512
|
+
}
|
|
1513
|
+
interface DatabaseActionData {
|
|
1514
|
+
tableName: string;
|
|
1515
|
+
requesterDetails: RequesterDetails;
|
|
1516
|
+
}
|
|
1517
|
+
interface ActionRowInsertData<T = DynamicObject> extends DatabaseActionData {
|
|
1518
|
+
insertId: number;
|
|
1519
|
+
insertObject: T;
|
|
1520
|
+
}
|
|
1521
|
+
interface ActionRowDeleteData extends DatabaseActionData {
|
|
1522
|
+
deletedRow: DynamicObject;
|
|
1523
|
+
}
|
|
1524
|
+
interface ActionColumnChangeData extends DatabaseActionData {
|
|
1525
|
+
tableName: string;
|
|
1526
|
+
rowId: number;
|
|
1527
|
+
newData: DynamicObject;
|
|
1528
|
+
oldData: DynamicObject;
|
|
1529
|
+
}
|
|
1530
|
+
interface ActionRowInsertFilter {
|
|
1531
|
+
tableName: string;
|
|
1532
|
+
}
|
|
1533
|
+
interface ActionRowDeleteFilter {
|
|
1534
|
+
tableName: string;
|
|
1535
|
+
}
|
|
1536
|
+
interface ActionColumnChangeFilter {
|
|
1537
|
+
tableName: string;
|
|
1538
|
+
}
|
|
1539
|
+
type TriggerResult = {
|
|
1540
|
+
table: string;
|
|
1541
|
+
insertId?: number;
|
|
1542
|
+
query: string;
|
|
1543
|
+
record: DynamicObject;
|
|
1544
|
+
previousRecord: DynamicObject;
|
|
1545
|
+
requesterId: number;
|
|
1546
|
+
};
|
|
1547
|
+
|
|
1548
|
+
export { type ActionColumnChangeData, type ActionColumnChangeFilter, type ActionRowDeleteData, type ActionRowDeleteFilter, type ActionRowInsertData, type ActionRowInsertFilter, type ApiMethod, type AsyncExpressApplication, type AuthenticateHandler, type AuthenticationUserDetails, type ConjunctionTypes, type DatabaseActionData, type DynamicObject, type ErrorCode, type EventType, HtmlStatusCodes, type LoginDetails, type MatchTypes, type MutationType, type PageQuery, PsqlPool, type RequesterDetails, type ResponseType, type ResponseTypeMap, RsError, type RsErrorData, type RsErrorInternalData, type RsHeaders, type RsPagedResponseData, type RsRequest, type RsResponse, type RsResponseData, type RsRouteHandler, SQL, type SchemaChangeValue, type SchemaPreview, type SqlMutationData, type StandardOrderTypes, type TriggerResult, type ValidAuthenticationCallback, type ValidatorString, escapeColumnName, insertObjectQuery, isValueNumber, logger, questionMarksToOrderedParams, restura, updateObjectQuery };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import winston from 'winston';
|
|
2
2
|
import * as express from 'express';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { QueryResultRow, QueryConfigValues, QueryResult, PoolConfig, Pool
|
|
4
|
+
import { QueryResultRow, QueryConfigValues, QueryResult, PoolConfig, Pool } from 'pg';
|
|
5
5
|
import { IncomingHttpHeaders } from 'http2';
|
|
6
6
|
|
|
7
7
|
declare const logger: winston.Logger;
|
|
@@ -1392,10 +1392,10 @@ interface AsyncExpressApplication {
|
|
|
1392
1392
|
}
|
|
1393
1393
|
|
|
1394
1394
|
declare abstract class PsqlConnection {
|
|
1395
|
-
constructor();
|
|
1395
|
+
protected constructor();
|
|
1396
1396
|
protected abstract query<R extends QueryResultRow = QueryResultRow, T extends Array<unknown> = unknown[]>(query: string, values?: QueryConfigValues<T>): Promise<QueryResult<R>>;
|
|
1397
|
-
queryOne(query: string, options: any[], requesterDetails: RequesterDetails): Promise<
|
|
1398
|
-
runQuery(query: string, options: any[], requesterDetails: RequesterDetails): Promise<
|
|
1397
|
+
queryOne<T>(query: string, options: any[], requesterDetails: RequesterDetails): Promise<T>;
|
|
1398
|
+
runQuery<T>(query: string, options: any[], requesterDetails: RequesterDetails): Promise<T[]>;
|
|
1399
1399
|
private logSqlStatement;
|
|
1400
1400
|
}
|
|
1401
1401
|
|
|
@@ -1406,18 +1406,6 @@ declare class PsqlPool extends PsqlConnection {
|
|
|
1406
1406
|
protected query<R extends QueryResultRow = QueryResultRow, T extends Array<unknown> = unknown[]>(query: string, values?: QueryConfigValues<T>): Promise<QueryResult<R>>;
|
|
1407
1407
|
}
|
|
1408
1408
|
|
|
1409
|
-
declare class PsqlTransaction extends PsqlConnection {
|
|
1410
|
-
clientConfig: ClientConfig;
|
|
1411
|
-
client: Client;
|
|
1412
|
-
private beginTransactionPromise;
|
|
1413
|
-
constructor(clientConfig: ClientConfig);
|
|
1414
|
-
private beginTransaction;
|
|
1415
|
-
rollback(): Promise<QueryResult<QueryResultRow>>;
|
|
1416
|
-
commit(): Promise<QueryResult<QueryResultRow>>;
|
|
1417
|
-
release(): Promise<void>;
|
|
1418
|
-
protected query<R extends QueryResultRow = QueryResultRow, T extends Array<unknown> = unknown[]>(query: string, values?: QueryConfigValues<T>): Promise<QueryResult<R>>;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
1409
|
declare class ResturaEngine {
|
|
1422
1410
|
resturaConfig: ResturaConfigSchema;
|
|
1423
1411
|
private resturaRouter;
|
|
@@ -1516,4 +1504,45 @@ declare function updateObjectQuery(table: string, obj: DynamicObject, whereState
|
|
|
1516
1504
|
declare function isValueNumber(value: unknown): value is number;
|
|
1517
1505
|
declare function SQL(strings: any, ...values: any): any;
|
|
1518
1506
|
|
|
1519
|
-
|
|
1507
|
+
type EventType = 'DATABASE_ROW_DELETE' | 'DATABASE_ROW_INSERT' | 'DATABASE_COLUMN_UPDATE' | 'WEBHOOK';
|
|
1508
|
+
type MutationType = 'INSERT' | 'UPDATE' | 'DELETE';
|
|
1509
|
+
interface SqlMutationData {
|
|
1510
|
+
mutationType: MutationType;
|
|
1511
|
+
requesterDetails: RequesterDetails;
|
|
1512
|
+
}
|
|
1513
|
+
interface DatabaseActionData {
|
|
1514
|
+
tableName: string;
|
|
1515
|
+
requesterDetails: RequesterDetails;
|
|
1516
|
+
}
|
|
1517
|
+
interface ActionRowInsertData<T = DynamicObject> extends DatabaseActionData {
|
|
1518
|
+
insertId: number;
|
|
1519
|
+
insertObject: T;
|
|
1520
|
+
}
|
|
1521
|
+
interface ActionRowDeleteData extends DatabaseActionData {
|
|
1522
|
+
deletedRow: DynamicObject;
|
|
1523
|
+
}
|
|
1524
|
+
interface ActionColumnChangeData extends DatabaseActionData {
|
|
1525
|
+
tableName: string;
|
|
1526
|
+
rowId: number;
|
|
1527
|
+
newData: DynamicObject;
|
|
1528
|
+
oldData: DynamicObject;
|
|
1529
|
+
}
|
|
1530
|
+
interface ActionRowInsertFilter {
|
|
1531
|
+
tableName: string;
|
|
1532
|
+
}
|
|
1533
|
+
interface ActionRowDeleteFilter {
|
|
1534
|
+
tableName: string;
|
|
1535
|
+
}
|
|
1536
|
+
interface ActionColumnChangeFilter {
|
|
1537
|
+
tableName: string;
|
|
1538
|
+
}
|
|
1539
|
+
type TriggerResult = {
|
|
1540
|
+
table: string;
|
|
1541
|
+
insertId?: number;
|
|
1542
|
+
query: string;
|
|
1543
|
+
record: DynamicObject;
|
|
1544
|
+
previousRecord: DynamicObject;
|
|
1545
|
+
requesterId: number;
|
|
1546
|
+
};
|
|
1547
|
+
|
|
1548
|
+
export { type ActionColumnChangeData, type ActionColumnChangeFilter, type ActionRowDeleteData, type ActionRowDeleteFilter, type ActionRowInsertData, type ActionRowInsertFilter, type ApiMethod, type AsyncExpressApplication, type AuthenticateHandler, type AuthenticationUserDetails, type ConjunctionTypes, type DatabaseActionData, type DynamicObject, type ErrorCode, type EventType, HtmlStatusCodes, type LoginDetails, type MatchTypes, type MutationType, type PageQuery, PsqlPool, type RequesterDetails, type ResponseType, type ResponseTypeMap, RsError, type RsErrorData, type RsErrorInternalData, type RsHeaders, type RsPagedResponseData, type RsRequest, type RsResponse, type RsResponseData, type RsRouteHandler, SQL, type SchemaChangeValue, type SchemaPreview, type SqlMutationData, type StandardOrderTypes, type TriggerResult, type ValidAuthenticationCallback, type ValidatorString, escapeColumnName, insertObjectQuery, isValueNumber, logger, questionMarksToOrderedParams, restura, updateObjectQuery };
|
package/dist/index.js
CHANGED
|
@@ -68,9 +68,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
68
68
|
var src_exports = {};
|
|
69
69
|
__export(src_exports, {
|
|
70
70
|
HtmlStatusCodes: () => HtmlStatusCodes,
|
|
71
|
-
PsqlConnection: () => PsqlConnection,
|
|
72
71
|
PsqlPool: () => PsqlPool,
|
|
73
|
-
PsqlTransaction: () => PsqlTransaction,
|
|
74
72
|
RsError: () => RsError,
|
|
75
73
|
SQL: () => SQL,
|
|
76
74
|
escapeColumnName: () => escapeColumnName,
|
|
@@ -260,7 +258,7 @@ var import_crypto = require("crypto");
|
|
|
260
258
|
var express = __toESM(require("express"));
|
|
261
259
|
var import_fs3 = __toESM(require("fs"));
|
|
262
260
|
var import_path3 = __toESM(require("path"));
|
|
263
|
-
var
|
|
261
|
+
var import_pg3 = __toESM(require("pg"));
|
|
264
262
|
var prettier3 = __toESM(require("prettier"));
|
|
265
263
|
|
|
266
264
|
// src/restura/sql/SqlUtils.ts
|
|
@@ -1279,7 +1277,8 @@ function insertObjectQuery(table, obj) {
|
|
|
1279
1277
|
const params = Object.values(obj);
|
|
1280
1278
|
const columns = keys.map((column) => escapeColumnName(column)).join(", ");
|
|
1281
1279
|
const values = params.map((value) => SQL`${value}`).join(", ");
|
|
1282
|
-
const query = `
|
|
1280
|
+
const query = `
|
|
1281
|
+
INSERT INTO "${table}" (${columns})
|
|
1283
1282
|
VALUES (${values})
|
|
1284
1283
|
RETURNING *`;
|
|
1285
1284
|
return query;
|
|
@@ -1289,7 +1288,8 @@ function updateObjectQuery(table, obj, whereStatement) {
|
|
|
1289
1288
|
for (const i in obj) {
|
|
1290
1289
|
setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
|
|
1291
1290
|
}
|
|
1292
|
-
return `
|
|
1291
|
+
return `
|
|
1292
|
+
UPDATE ${escapeColumnName(table)}
|
|
1293
1293
|
SET ${setArray.join(", ")} ${whereStatement}
|
|
1294
1294
|
RETURNING *`;
|
|
1295
1295
|
}
|
|
@@ -1319,8 +1319,10 @@ var PsqlConnection = class {
|
|
|
1319
1319
|
async queryOne(query, options, requesterDetails) {
|
|
1320
1320
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1321
1321
|
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1322
|
+
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(requesterDetails)})
|
|
1323
|
+
`;
|
|
1322
1324
|
try {
|
|
1323
|
-
const response = await this.query(formattedQuery, options);
|
|
1325
|
+
const response = await this.query(queryMetadata + formattedQuery, options);
|
|
1324
1326
|
if (response.rows.length === 0) throw new RsError("NOT_FOUND", "No results found");
|
|
1325
1327
|
else if (response.rows.length > 1) throw new RsError("DUPLICATE", "More than one result found");
|
|
1326
1328
|
return response.rows[0];
|
|
@@ -1337,8 +1339,10 @@ var PsqlConnection = class {
|
|
|
1337
1339
|
async runQuery(query, options, requesterDetails) {
|
|
1338
1340
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1339
1341
|
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1342
|
+
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(requesterDetails)})
|
|
1343
|
+
`;
|
|
1340
1344
|
try {
|
|
1341
|
-
const response = await this.query(formattedQuery, options);
|
|
1345
|
+
const response = await this.query(queryMetadata + formattedQuery, options);
|
|
1342
1346
|
return response.rows;
|
|
1343
1347
|
} catch (error) {
|
|
1344
1348
|
console.error(error, query, options);
|
|
@@ -1589,6 +1593,110 @@ var filterPsqlParser = import_pegjs.default.generate(filterSqlGrammar, {
|
|
|
1589
1593
|
});
|
|
1590
1594
|
var filterPsqlParser_default = filterPsqlParser;
|
|
1591
1595
|
|
|
1596
|
+
// src/restura/eventManager.ts
|
|
1597
|
+
var import_bluebird = __toESM(require("bluebird"));
|
|
1598
|
+
var EventManager = class {
|
|
1599
|
+
constructor() {
|
|
1600
|
+
this.actionHandlers = {
|
|
1601
|
+
DATABASE_ROW_DELETE: [],
|
|
1602
|
+
DATABASE_ROW_INSERT: [],
|
|
1603
|
+
DATABASE_COLUMN_UPDATE: []
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
addRowInsertHandler(onInsert, filter) {
|
|
1607
|
+
this.actionHandlers.DATABASE_ROW_INSERT.push({
|
|
1608
|
+
callback: onInsert,
|
|
1609
|
+
filter
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
addColumnChangeHandler(onUpdate, filter) {
|
|
1613
|
+
this.actionHandlers.DATABASE_COLUMN_UPDATE.push({
|
|
1614
|
+
callback: onUpdate,
|
|
1615
|
+
filter
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
addRowDeleteHandler(onDelete, filter) {
|
|
1619
|
+
this.actionHandlers.DATABASE_ROW_DELETE.push({
|
|
1620
|
+
callback: onDelete,
|
|
1621
|
+
filter
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
async fireActionFromDbTrigger(sqlMutationData, result) {
|
|
1625
|
+
if (sqlMutationData.mutationType === "INSERT") {
|
|
1626
|
+
await this.fireInsertActions(sqlMutationData, result);
|
|
1627
|
+
} else if (sqlMutationData.mutationType === "UPDATE") {
|
|
1628
|
+
await this.fireUpdateActions(sqlMutationData, result);
|
|
1629
|
+
} else if (sqlMutationData.mutationType === "DELETE") {
|
|
1630
|
+
await this.fireDeleteActions(sqlMutationData, result);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
async fireInsertActions(data, triggerResult) {
|
|
1634
|
+
await import_bluebird.default.map(
|
|
1635
|
+
this.actionHandlers.DATABASE_ROW_INSERT,
|
|
1636
|
+
({ callback, filter }) => {
|
|
1637
|
+
if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
|
|
1638
|
+
const insertData = {
|
|
1639
|
+
tableName: triggerResult.table,
|
|
1640
|
+
insertId: triggerResult.record.id,
|
|
1641
|
+
insertObject: triggerResult.record,
|
|
1642
|
+
requesterDetails: data.requesterDetails
|
|
1643
|
+
};
|
|
1644
|
+
callback(insertData, data.requesterDetails);
|
|
1645
|
+
},
|
|
1646
|
+
{ concurrency: 10 }
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
async fireDeleteActions(data, triggerResult) {
|
|
1650
|
+
await import_bluebird.default.map(
|
|
1651
|
+
this.actionHandlers.DATABASE_ROW_DELETE,
|
|
1652
|
+
({ callback, filter }) => {
|
|
1653
|
+
if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
|
|
1654
|
+
const deleteData = {
|
|
1655
|
+
tableName: triggerResult.table,
|
|
1656
|
+
deletedRow: triggerResult.previousRecord,
|
|
1657
|
+
requesterDetails: data.requesterDetails
|
|
1658
|
+
};
|
|
1659
|
+
callback(deleteData, data.requesterDetails);
|
|
1660
|
+
},
|
|
1661
|
+
{ concurrency: 10 }
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
async fireUpdateActions(data, triggerResult) {
|
|
1665
|
+
await import_bluebird.default.map(
|
|
1666
|
+
this.actionHandlers.DATABASE_COLUMN_UPDATE,
|
|
1667
|
+
({ callback, filter }) => {
|
|
1668
|
+
if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
|
|
1669
|
+
const columnChangeData = {
|
|
1670
|
+
tableName: triggerResult.table,
|
|
1671
|
+
rowId: triggerResult.record.id,
|
|
1672
|
+
newData: triggerResult.record,
|
|
1673
|
+
oldData: triggerResult.previousRecord,
|
|
1674
|
+
requesterDetails: data.requesterDetails
|
|
1675
|
+
};
|
|
1676
|
+
callback(columnChangeData, data.requesterDetails);
|
|
1677
|
+
},
|
|
1678
|
+
{ concurrency: 10 }
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
hasHandlersForEventType(eventType, filter, triggerResult) {
|
|
1682
|
+
if (filter) {
|
|
1683
|
+
switch (eventType) {
|
|
1684
|
+
case "DATABASE_ROW_INSERT":
|
|
1685
|
+
case "DATABASE_ROW_DELETE":
|
|
1686
|
+
if (filter.tableName && filter.tableName !== triggerResult.table) return false;
|
|
1687
|
+
break;
|
|
1688
|
+
case "DATABASE_COLUMN_UPDATE":
|
|
1689
|
+
const filterColumnChange = filter;
|
|
1690
|
+
if (filterColumnChange.tableName !== filter.tableName) return false;
|
|
1691
|
+
break;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
return true;
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
var eventManager = new EventManager();
|
|
1698
|
+
var eventManager_default = eventManager;
|
|
1699
|
+
|
|
1592
1700
|
// src/restura/sql/PsqlEngine.ts
|
|
1593
1701
|
var { Client } = import_pg2.default;
|
|
1594
1702
|
var systemUser = {
|
|
@@ -1598,9 +1706,49 @@ var systemUser = {
|
|
|
1598
1706
|
isSystemUser: true
|
|
1599
1707
|
};
|
|
1600
1708
|
var PsqlEngine = class extends SqlEngine {
|
|
1601
|
-
constructor(psqlConnectionPool) {
|
|
1709
|
+
constructor(psqlConnectionPool, shouldListenForDbTriggers = false) {
|
|
1602
1710
|
super();
|
|
1603
1711
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
1712
|
+
if (shouldListenForDbTriggers) {
|
|
1713
|
+
this.setupTriggerListeners = this.listenForDbTriggers();
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
async close() {
|
|
1717
|
+
if (this.triggerClient) {
|
|
1718
|
+
await this.triggerClient.end();
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
async listenForDbTriggers() {
|
|
1722
|
+
this.triggerClient = new Client({
|
|
1723
|
+
user: this.psqlConnectionPool.poolConfig.user,
|
|
1724
|
+
host: this.psqlConnectionPool.poolConfig.host,
|
|
1725
|
+
database: this.psqlConnectionPool.poolConfig.database,
|
|
1726
|
+
password: this.psqlConnectionPool.poolConfig.password,
|
|
1727
|
+
port: this.psqlConnectionPool.poolConfig.port,
|
|
1728
|
+
connectionTimeoutMillis: 2e3
|
|
1729
|
+
});
|
|
1730
|
+
await this.triggerClient.connect();
|
|
1731
|
+
const promises = [];
|
|
1732
|
+
promises.push(this.triggerClient.query("LISTEN insert"));
|
|
1733
|
+
promises.push(this.triggerClient.query("LISTEN update"));
|
|
1734
|
+
promises.push(this.triggerClient.query("LISTEN delete"));
|
|
1735
|
+
await Promise.all(promises);
|
|
1736
|
+
this.triggerClient.on("notification", async (msg) => {
|
|
1737
|
+
if (msg.channel === "insert" || msg.channel === "update" || msg.channel === "delete") {
|
|
1738
|
+
const payload = JSON.parse(msg.payload);
|
|
1739
|
+
await this.handleTrigger(payload, msg.channel.toUpperCase());
|
|
1740
|
+
}
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
async handleTrigger(payload, mutationType) {
|
|
1744
|
+
const findRequesterDetailsRegex = /^--QUERY_METADATA\(\{.*\}\)/;
|
|
1745
|
+
let requesterDetails = {};
|
|
1746
|
+
const match = payload.query.match(findRequesterDetailsRegex);
|
|
1747
|
+
if (match) {
|
|
1748
|
+
const jsonString = match[0].slice(match[0].indexOf("{"), match[0].lastIndexOf("}") + 1);
|
|
1749
|
+
requesterDetails = import_core_utils5.ObjectUtils.safeParse(jsonString);
|
|
1750
|
+
await eventManager_default.fireActionFromDbTrigger({ requesterDetails, mutationType }, payload);
|
|
1751
|
+
}
|
|
1604
1752
|
}
|
|
1605
1753
|
async createDatabaseFromSchema(schema, connection) {
|
|
1606
1754
|
const sqlFullStatement = this.generateDatabaseSchemaFromSchema(schema);
|
|
@@ -1611,7 +1759,11 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1611
1759
|
const sqlStatements = [];
|
|
1612
1760
|
const enums = [];
|
|
1613
1761
|
const indexes = [];
|
|
1762
|
+
const triggers = [];
|
|
1614
1763
|
for (const table of schema.database) {
|
|
1764
|
+
triggers.push(this.createInsertTriggers(table.name));
|
|
1765
|
+
triggers.push(this.createUpdateTrigger(table.name));
|
|
1766
|
+
triggers.push(this.createDeleteTrigger(table.name));
|
|
1615
1767
|
let sql = `CREATE TABLE "${table.name}"
|
|
1616
1768
|
( `;
|
|
1617
1769
|
const tableColumns = [];
|
|
@@ -1649,7 +1801,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1649
1801
|
indexes.push(
|
|
1650
1802
|
` CREATE ${unique}INDEX "${index.name}" ON "${table.name}" (${index.columns.map((item) => {
|
|
1651
1803
|
return `"${item}" ${index.order}`;
|
|
1652
|
-
}).join(", ")})
|
|
1804
|
+
}).join(", ")});`
|
|
1653
1805
|
);
|
|
1654
1806
|
}
|
|
1655
1807
|
}
|
|
@@ -1679,7 +1831,8 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1679
1831
|
}
|
|
1680
1832
|
sqlStatements.push(sql + constraints.join(",\n") + ";");
|
|
1681
1833
|
}
|
|
1682
|
-
sqlStatements.push(indexes.join("
|
|
1834
|
+
sqlStatements.push(indexes.join("\n"));
|
|
1835
|
+
sqlStatements.push(triggers.join("\n"));
|
|
1683
1836
|
return enums.join("\n") + "\n" + sqlStatements.join("\n\n");
|
|
1684
1837
|
}
|
|
1685
1838
|
async getScratchPool() {
|
|
@@ -1747,8 +1900,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1747
1900
|
)) {
|
|
1748
1901
|
return "'[]'";
|
|
1749
1902
|
}
|
|
1750
|
-
return `COALESCE((
|
|
1751
|
-
SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
1903
|
+
return `COALESCE((SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
1752
1904
|
${item.subquery.properties.map((nestedItem) => {
|
|
1753
1905
|
if (!this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
|
|
1754
1906
|
...routeData.joins,
|
|
@@ -1757,7 +1909,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1757
1909
|
return;
|
|
1758
1910
|
}
|
|
1759
1911
|
if (nestedItem.subquery) {
|
|
1760
|
-
return `
|
|
1912
|
+
return `'${nestedItem.name}', ${this.createNestedSelect(
|
|
1761
1913
|
// recursion
|
|
1762
1914
|
req,
|
|
1763
1915
|
schema,
|
|
@@ -1768,7 +1920,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1768
1920
|
)}`;
|
|
1769
1921
|
}
|
|
1770
1922
|
return `'${nestedItem.name}', ${escapeColumnName(nestedItem.selector)}`;
|
|
1771
|
-
}).filter(Boolean).join(",")}
|
|
1923
|
+
}).filter(Boolean).join(", ")}
|
|
1772
1924
|
))
|
|
1773
1925
|
FROM
|
|
1774
1926
|
"${item.subquery.table}"
|
|
@@ -1896,10 +2048,12 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1896
2048
|
req.requesterDetails.role,
|
|
1897
2049
|
sqlParams
|
|
1898
2050
|
);
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2051
|
+
const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
2052
|
+
if (whereClause.replace(/\s/g, "") === "") {
|
|
2053
|
+
throw new RsError("DELETE_FORBIDDEN", "Deletes need a where clause");
|
|
2054
|
+
}
|
|
2055
|
+
const deleteStatement = `
|
|
2056
|
+
DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
|
|
1903
2057
|
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
|
|
1904
2058
|
return true;
|
|
1905
2059
|
}
|
|
@@ -1962,17 +2116,17 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1962
2116
|
);
|
|
1963
2117
|
let operator = item.operator;
|
|
1964
2118
|
if (operator === "LIKE") {
|
|
1965
|
-
|
|
2119
|
+
item.value = `%${item.value}%`;
|
|
1966
2120
|
} else if (operator === "STARTS WITH") {
|
|
1967
2121
|
operator = "LIKE";
|
|
1968
|
-
|
|
2122
|
+
item.value = `${item.value}%`;
|
|
1969
2123
|
} else if (operator === "ENDS WITH") {
|
|
1970
2124
|
operator = "LIKE";
|
|
1971
|
-
|
|
2125
|
+
item.value = `%${item.value}`;
|
|
1972
2126
|
}
|
|
1973
2127
|
const replacedValue = this.replaceParamKeywords(item.value, routeData, req, sqlParams);
|
|
1974
2128
|
const escapedValue = SQL`${replacedValue}`;
|
|
1975
|
-
whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator} ${["IN", "NOT IN"].includes(operator) ? `(${escapedValue})` : escapedValue}
|
|
2129
|
+
whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator.replace("LIKE", "ILIKE")} ${["IN", "NOT IN"].includes(operator) ? `(${escapedValue})` : escapedValue}
|
|
1976
2130
|
`;
|
|
1977
2131
|
});
|
|
1978
2132
|
const data = req.data;
|
|
@@ -2006,7 +2160,67 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2006
2160
|
}
|
|
2007
2161
|
return whereClause;
|
|
2008
2162
|
}
|
|
2163
|
+
createUpdateTrigger(tableName) {
|
|
2164
|
+
return `
|
|
2165
|
+
CREATE OR REPLACE FUNCTION notify_${tableName}_update()
|
|
2166
|
+
RETURNS TRIGGER AS $$
|
|
2167
|
+
BEGIN
|
|
2168
|
+
PERFORM pg_notify('update', JSON_BUILD_OBJECT('table', '${tableName}', 'query', current_query(), 'record', NEW, 'previousRecord', OLD)::text);
|
|
2169
|
+
RETURN NEW;
|
|
2170
|
+
END;
|
|
2171
|
+
$$ LANGUAGE plpgsql;
|
|
2172
|
+
|
|
2173
|
+
CREATE OR REPLACE TRIGGER ${tableName}_update
|
|
2174
|
+
AFTER UPDATE ON "${tableName}"
|
|
2175
|
+
FOR EACH ROW
|
|
2176
|
+
EXECUTE FUNCTION notify_${tableName}_update();
|
|
2177
|
+
`;
|
|
2178
|
+
}
|
|
2179
|
+
createDeleteTrigger(tableName) {
|
|
2180
|
+
return `
|
|
2181
|
+
CREATE OR REPLACE FUNCTION notify_${tableName}_delete()
|
|
2182
|
+
RETURNS TRIGGER AS $$
|
|
2183
|
+
BEGIN
|
|
2184
|
+
PERFORM pg_notify('delete', JSON_BUILD_OBJECT('table', '${tableName}', 'query', current_query(), 'record', NEW, 'previousRecord', OLD)::text);
|
|
2185
|
+
RETURN NEW;
|
|
2186
|
+
END;
|
|
2187
|
+
$$ LANGUAGE plpgsql;
|
|
2188
|
+
|
|
2189
|
+
CREATE OR REPLACE TRIGGER "${tableName}_delete"
|
|
2190
|
+
AFTER DELETE ON "${tableName}"
|
|
2191
|
+
FOR EACH ROW
|
|
2192
|
+
EXECUTE FUNCTION notify_${tableName}_delete();
|
|
2193
|
+
`;
|
|
2194
|
+
}
|
|
2195
|
+
createInsertTriggers(tableName) {
|
|
2196
|
+
return `
|
|
2197
|
+
CREATE OR REPLACE FUNCTION notify_${tableName}_insert()
|
|
2198
|
+
RETURNS TRIGGER AS $$
|
|
2199
|
+
BEGIN
|
|
2200
|
+
PERFORM pg_notify('insert', JSON_BUILD_OBJECT('table', '${tableName}', 'query', current_query(), 'record', NEW, 'previousRecord', OLD)::text);
|
|
2201
|
+
RETURN NEW;
|
|
2202
|
+
END;
|
|
2203
|
+
$$ LANGUAGE plpgsql;
|
|
2204
|
+
|
|
2205
|
+
CREATE TRIGGER "${tableName}_insert"
|
|
2206
|
+
AFTER INSERT ON "${tableName}"
|
|
2207
|
+
FOR EACH ROW
|
|
2208
|
+
EXECUTE FUNCTION notify_${tableName}_insert();
|
|
2209
|
+
`;
|
|
2210
|
+
}
|
|
2009
2211
|
};
|
|
2212
|
+
__decorateClass([
|
|
2213
|
+
boundMethod
|
|
2214
|
+
], PsqlEngine.prototype, "handleTrigger", 1);
|
|
2215
|
+
__decorateClass([
|
|
2216
|
+
boundMethod
|
|
2217
|
+
], PsqlEngine.prototype, "createUpdateTrigger", 1);
|
|
2218
|
+
__decorateClass([
|
|
2219
|
+
boundMethod
|
|
2220
|
+
], PsqlEngine.prototype, "createDeleteTrigger", 1);
|
|
2221
|
+
__decorateClass([
|
|
2222
|
+
boundMethod
|
|
2223
|
+
], PsqlEngine.prototype, "createInsertTriggers", 1);
|
|
2010
2224
|
function schemaToPsqlType(column, tableName) {
|
|
2011
2225
|
if (column.hasAutoIncrement) return "BIGSERIAL";
|
|
2012
2226
|
if (column.type === "ENUM") return `"${tableName}_${column.name}_enum"`;
|
|
@@ -2015,35 +2229,6 @@ function schemaToPsqlType(column, tableName) {
|
|
|
2015
2229
|
return column.type;
|
|
2016
2230
|
}
|
|
2017
2231
|
|
|
2018
|
-
// src/restura/sql/PsqlTransaction.ts
|
|
2019
|
-
var import_pg3 = __toESM(require("pg"));
|
|
2020
|
-
var { Client: Client2 } = import_pg3.default;
|
|
2021
|
-
var PsqlTransaction = class extends PsqlConnection {
|
|
2022
|
-
constructor(clientConfig) {
|
|
2023
|
-
super();
|
|
2024
|
-
this.clientConfig = clientConfig;
|
|
2025
|
-
this.client = new Client2(clientConfig);
|
|
2026
|
-
this.beginTransactionPromise = this.beginTransaction();
|
|
2027
|
-
}
|
|
2028
|
-
async beginTransaction() {
|
|
2029
|
-
return this.query("BEGIN");
|
|
2030
|
-
}
|
|
2031
|
-
async rollback() {
|
|
2032
|
-
return this.query("ROLLBACK");
|
|
2033
|
-
}
|
|
2034
|
-
async commit() {
|
|
2035
|
-
return this.query("COMMIT");
|
|
2036
|
-
}
|
|
2037
|
-
async release() {
|
|
2038
|
-
return this.client.end();
|
|
2039
|
-
}
|
|
2040
|
-
async query(query, values) {
|
|
2041
|
-
await this.client.connect();
|
|
2042
|
-
await this.beginTransactionPromise;
|
|
2043
|
-
return this.client.query(query, values);
|
|
2044
|
-
}
|
|
2045
|
-
};
|
|
2046
|
-
|
|
2047
2232
|
// src/restura/compareSchema.ts
|
|
2048
2233
|
var import_lodash = __toESM(require("lodash.clonedeep"));
|
|
2049
2234
|
var CompareSchema = class {
|
|
@@ -2135,7 +2320,7 @@ var compareSchema = new CompareSchema();
|
|
|
2135
2320
|
var compareSchema_default = compareSchema;
|
|
2136
2321
|
|
|
2137
2322
|
// src/restura/restura.ts
|
|
2138
|
-
var { types } =
|
|
2323
|
+
var { types } = import_pg3.default;
|
|
2139
2324
|
var ResturaEngine = class {
|
|
2140
2325
|
constructor() {
|
|
2141
2326
|
this.publicEndpoints = {
|
|
@@ -2510,12 +2695,14 @@ var setupPgReturnTypes = () => {
|
|
|
2510
2695
|
};
|
|
2511
2696
|
setupPgReturnTypes();
|
|
2512
2697
|
var restura = new ResturaEngine();
|
|
2698
|
+
|
|
2699
|
+
// src/restura/sql/PsqlTransaction.ts
|
|
2700
|
+
var import_pg4 = __toESM(require("pg"));
|
|
2701
|
+
var { Client: Client2 } = import_pg4.default;
|
|
2513
2702
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2514
2703
|
0 && (module.exports = {
|
|
2515
2704
|
HtmlStatusCodes,
|
|
2516
|
-
PsqlConnection,
|
|
2517
2705
|
PsqlPool,
|
|
2518
|
-
PsqlTransaction,
|
|
2519
2706
|
RsError,
|
|
2520
2707
|
SQL,
|
|
2521
2708
|
escapeColumnName,
|