@restura/core 0.1.0-alpha.14 → 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 +65 -36
- package/dist/index.d.ts +65 -36
- package/dist/index.js +272 -77
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +272 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -748,7 +748,7 @@ var joinDataSchema = z3.object({
|
|
|
748
748
|
var requestDataSchema = z3.object({
|
|
749
749
|
name: z3.string(),
|
|
750
750
|
required: z3.boolean(),
|
|
751
|
-
isNullable: z3.boolean().optional()
|
|
751
|
+
isNullable: z3.boolean().optional(),
|
|
752
752
|
validator: z3.array(validatorDataSchema)
|
|
753
753
|
}).strict();
|
|
754
754
|
var responseDataSchema = z3.object({
|
|
@@ -834,6 +834,12 @@ var postgresColumnDateTypesSchema = z3.enum([
|
|
|
834
834
|
"INTERVAL"
|
|
835
835
|
// time span
|
|
836
836
|
]);
|
|
837
|
+
var postgresColumnJsonTypesSchema = z3.enum([
|
|
838
|
+
"JSON",
|
|
839
|
+
// stores JSON data as raw text
|
|
840
|
+
"JSONB"
|
|
841
|
+
// stores JSON data in a binary format, optimized for query performance
|
|
842
|
+
]);
|
|
837
843
|
var mariaDbColumnNumericTypesSchema = z3.enum([
|
|
838
844
|
"BOOLEAN",
|
|
839
845
|
// 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
|
|
@@ -896,6 +902,7 @@ var columnDataSchema = z3.object({
|
|
|
896
902
|
postgresColumnNumericTypesSchema,
|
|
897
903
|
postgresColumnStringTypesSchema,
|
|
898
904
|
postgresColumnDateTypesSchema,
|
|
905
|
+
postgresColumnJsonTypesSchema,
|
|
899
906
|
mariaDbColumnNumericTypesSchema,
|
|
900
907
|
mariaDbColumnStringTypesSchema,
|
|
901
908
|
mariaDbColumnDateTypesSchema
|
|
@@ -1203,10 +1210,16 @@ function convertTable(table) {
|
|
|
1203
1210
|
|
|
1204
1211
|
// src/restura/sql/PsqlEngine.ts
|
|
1205
1212
|
import { ObjectUtils as ObjectUtils4 } from "@redskytech/core-utils";
|
|
1213
|
+
import getDiff from "@wmfs/pg-diff-sync";
|
|
1214
|
+
import pgInfo from "@wmfs/pg-info";
|
|
1215
|
+
import pg2 from "pg";
|
|
1206
1216
|
|
|
1207
1217
|
// src/restura/sql/PsqlPool.ts
|
|
1208
1218
|
import pg from "pg";
|
|
1209
1219
|
|
|
1220
|
+
// src/restura/sql/PsqlConnection.ts
|
|
1221
|
+
import format3 from "pg-format";
|
|
1222
|
+
|
|
1210
1223
|
// src/restura/sql/PsqlUtils.ts
|
|
1211
1224
|
import format2 from "pg-format";
|
|
1212
1225
|
function escapeColumnName(columnName) {
|
|
@@ -1222,7 +1235,8 @@ function insertObjectQuery(table, obj) {
|
|
|
1222
1235
|
const params = Object.values(obj);
|
|
1223
1236
|
const columns = keys.map((column) => escapeColumnName(column)).join(", ");
|
|
1224
1237
|
const values = params.map((value) => SQL`${value}`).join(", ");
|
|
1225
|
-
const query = `
|
|
1238
|
+
const query = `
|
|
1239
|
+
INSERT INTO "${table}" (${columns})
|
|
1226
1240
|
VALUES (${values})
|
|
1227
1241
|
RETURNING *`;
|
|
1228
1242
|
return query;
|
|
@@ -1232,7 +1246,8 @@ function updateObjectQuery(table, obj, whereStatement) {
|
|
|
1232
1246
|
for (const i in obj) {
|
|
1233
1247
|
setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
|
|
1234
1248
|
}
|
|
1235
|
-
return `
|
|
1249
|
+
return `
|
|
1250
|
+
UPDATE ${escapeColumnName(table)}
|
|
1236
1251
|
SET ${setArray.join(", ")} ${whereStatement}
|
|
1237
1252
|
RETURNING *`;
|
|
1238
1253
|
}
|
|
@@ -1255,7 +1270,6 @@ function SQL(strings, ...values) {
|
|
|
1255
1270
|
}
|
|
1256
1271
|
|
|
1257
1272
|
// src/restura/sql/PsqlConnection.ts
|
|
1258
|
-
import format3 from "pg-format";
|
|
1259
1273
|
var PsqlConnection = class {
|
|
1260
1274
|
constructor() {
|
|
1261
1275
|
}
|
|
@@ -1263,8 +1277,10 @@ var PsqlConnection = class {
|
|
|
1263
1277
|
async queryOne(query, options, requesterDetails) {
|
|
1264
1278
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1265
1279
|
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1280
|
+
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(requesterDetails)})
|
|
1281
|
+
`;
|
|
1266
1282
|
try {
|
|
1267
|
-
const response = await this.query(formattedQuery, options);
|
|
1283
|
+
const response = await this.query(queryMetadata + formattedQuery, options);
|
|
1268
1284
|
if (response.rows.length === 0) throw new RsError("NOT_FOUND", "No results found");
|
|
1269
1285
|
else if (response.rows.length > 1) throw new RsError("DUPLICATE", "More than one result found");
|
|
1270
1286
|
return response.rows[0];
|
|
@@ -1281,8 +1297,10 @@ var PsqlConnection = class {
|
|
|
1281
1297
|
async runQuery(query, options, requesterDetails) {
|
|
1282
1298
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1283
1299
|
this.logSqlStatement(formattedQuery, options, requesterDetails);
|
|
1300
|
+
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(requesterDetails)})
|
|
1301
|
+
`;
|
|
1284
1302
|
try {
|
|
1285
|
-
const response = await this.query(formattedQuery, options);
|
|
1303
|
+
const response = await this.query(queryMetadata + formattedQuery, options);
|
|
1286
1304
|
return response.rows;
|
|
1287
1305
|
} catch (error) {
|
|
1288
1306
|
console.error(error, query, options);
|
|
@@ -1533,10 +1551,112 @@ var filterPsqlParser = peg.generate(filterSqlGrammar, {
|
|
|
1533
1551
|
});
|
|
1534
1552
|
var filterPsqlParser_default = filterPsqlParser;
|
|
1535
1553
|
|
|
1554
|
+
// src/restura/eventManager.ts
|
|
1555
|
+
import Bluebird from "bluebird";
|
|
1556
|
+
var EventManager = class {
|
|
1557
|
+
constructor() {
|
|
1558
|
+
this.actionHandlers = {
|
|
1559
|
+
DATABASE_ROW_DELETE: [],
|
|
1560
|
+
DATABASE_ROW_INSERT: [],
|
|
1561
|
+
DATABASE_COLUMN_UPDATE: []
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
addRowInsertHandler(onInsert, filter) {
|
|
1565
|
+
this.actionHandlers.DATABASE_ROW_INSERT.push({
|
|
1566
|
+
callback: onInsert,
|
|
1567
|
+
filter
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
addColumnChangeHandler(onUpdate, filter) {
|
|
1571
|
+
this.actionHandlers.DATABASE_COLUMN_UPDATE.push({
|
|
1572
|
+
callback: onUpdate,
|
|
1573
|
+
filter
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
addRowDeleteHandler(onDelete, filter) {
|
|
1577
|
+
this.actionHandlers.DATABASE_ROW_DELETE.push({
|
|
1578
|
+
callback: onDelete,
|
|
1579
|
+
filter
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
async fireActionFromDbTrigger(sqlMutationData, result) {
|
|
1583
|
+
if (sqlMutationData.mutationType === "INSERT") {
|
|
1584
|
+
await this.fireInsertActions(sqlMutationData, result);
|
|
1585
|
+
} else if (sqlMutationData.mutationType === "UPDATE") {
|
|
1586
|
+
await this.fireUpdateActions(sqlMutationData, result);
|
|
1587
|
+
} else if (sqlMutationData.mutationType === "DELETE") {
|
|
1588
|
+
await this.fireDeleteActions(sqlMutationData, result);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
async fireInsertActions(data, triggerResult) {
|
|
1592
|
+
await Bluebird.map(
|
|
1593
|
+
this.actionHandlers.DATABASE_ROW_INSERT,
|
|
1594
|
+
({ callback, filter }) => {
|
|
1595
|
+
if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
|
|
1596
|
+
const insertData = {
|
|
1597
|
+
tableName: triggerResult.table,
|
|
1598
|
+
insertId: triggerResult.record.id,
|
|
1599
|
+
insertObject: triggerResult.record,
|
|
1600
|
+
requesterDetails: data.requesterDetails
|
|
1601
|
+
};
|
|
1602
|
+
callback(insertData, data.requesterDetails);
|
|
1603
|
+
},
|
|
1604
|
+
{ concurrency: 10 }
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
async fireDeleteActions(data, triggerResult) {
|
|
1608
|
+
await Bluebird.map(
|
|
1609
|
+
this.actionHandlers.DATABASE_ROW_DELETE,
|
|
1610
|
+
({ callback, filter }) => {
|
|
1611
|
+
if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
|
|
1612
|
+
const deleteData = {
|
|
1613
|
+
tableName: triggerResult.table,
|
|
1614
|
+
deletedRow: triggerResult.previousRecord,
|
|
1615
|
+
requesterDetails: data.requesterDetails
|
|
1616
|
+
};
|
|
1617
|
+
callback(deleteData, data.requesterDetails);
|
|
1618
|
+
},
|
|
1619
|
+
{ concurrency: 10 }
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1622
|
+
async fireUpdateActions(data, triggerResult) {
|
|
1623
|
+
await Bluebird.map(
|
|
1624
|
+
this.actionHandlers.DATABASE_COLUMN_UPDATE,
|
|
1625
|
+
({ callback, filter }) => {
|
|
1626
|
+
if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
|
|
1627
|
+
const columnChangeData = {
|
|
1628
|
+
tableName: triggerResult.table,
|
|
1629
|
+
rowId: triggerResult.record.id,
|
|
1630
|
+
newData: triggerResult.record,
|
|
1631
|
+
oldData: triggerResult.previousRecord,
|
|
1632
|
+
requesterDetails: data.requesterDetails
|
|
1633
|
+
};
|
|
1634
|
+
callback(columnChangeData, data.requesterDetails);
|
|
1635
|
+
},
|
|
1636
|
+
{ concurrency: 10 }
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1639
|
+
hasHandlersForEventType(eventType, filter, triggerResult) {
|
|
1640
|
+
if (filter) {
|
|
1641
|
+
switch (eventType) {
|
|
1642
|
+
case "DATABASE_ROW_INSERT":
|
|
1643
|
+
case "DATABASE_ROW_DELETE":
|
|
1644
|
+
if (filter.tableName && filter.tableName !== triggerResult.table) return false;
|
|
1645
|
+
break;
|
|
1646
|
+
case "DATABASE_COLUMN_UPDATE":
|
|
1647
|
+
const filterColumnChange = filter;
|
|
1648
|
+
if (filterColumnChange.tableName !== filter.tableName) return false;
|
|
1649
|
+
break;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
return true;
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
var eventManager = new EventManager();
|
|
1656
|
+
var eventManager_default = eventManager;
|
|
1657
|
+
|
|
1536
1658
|
// src/restura/sql/PsqlEngine.ts
|
|
1537
|
-
|
|
1538
|
-
import pgInfo from "@wmfs/pg-info";
|
|
1539
|
-
var { Client } = "pg";
|
|
1659
|
+
var { Client } = pg2;
|
|
1540
1660
|
var systemUser = {
|
|
1541
1661
|
role: "",
|
|
1542
1662
|
host: "",
|
|
@@ -1544,9 +1664,49 @@ var systemUser = {
|
|
|
1544
1664
|
isSystemUser: true
|
|
1545
1665
|
};
|
|
1546
1666
|
var PsqlEngine = class extends SqlEngine {
|
|
1547
|
-
constructor(psqlConnectionPool) {
|
|
1667
|
+
constructor(psqlConnectionPool, shouldListenForDbTriggers = false) {
|
|
1548
1668
|
super();
|
|
1549
1669
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
1670
|
+
if (shouldListenForDbTriggers) {
|
|
1671
|
+
this.setupTriggerListeners = this.listenForDbTriggers();
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
async close() {
|
|
1675
|
+
if (this.triggerClient) {
|
|
1676
|
+
await this.triggerClient.end();
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
async listenForDbTriggers() {
|
|
1680
|
+
this.triggerClient = new Client({
|
|
1681
|
+
user: this.psqlConnectionPool.poolConfig.user,
|
|
1682
|
+
host: this.psqlConnectionPool.poolConfig.host,
|
|
1683
|
+
database: this.psqlConnectionPool.poolConfig.database,
|
|
1684
|
+
password: this.psqlConnectionPool.poolConfig.password,
|
|
1685
|
+
port: this.psqlConnectionPool.poolConfig.port,
|
|
1686
|
+
connectionTimeoutMillis: 2e3
|
|
1687
|
+
});
|
|
1688
|
+
await this.triggerClient.connect();
|
|
1689
|
+
const promises = [];
|
|
1690
|
+
promises.push(this.triggerClient.query("LISTEN insert"));
|
|
1691
|
+
promises.push(this.triggerClient.query("LISTEN update"));
|
|
1692
|
+
promises.push(this.triggerClient.query("LISTEN delete"));
|
|
1693
|
+
await Promise.all(promises);
|
|
1694
|
+
this.triggerClient.on("notification", async (msg) => {
|
|
1695
|
+
if (msg.channel === "insert" || msg.channel === "update" || msg.channel === "delete") {
|
|
1696
|
+
const payload = JSON.parse(msg.payload);
|
|
1697
|
+
await this.handleTrigger(payload, msg.channel.toUpperCase());
|
|
1698
|
+
}
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
async handleTrigger(payload, mutationType) {
|
|
1702
|
+
const findRequesterDetailsRegex = /^--QUERY_METADATA\(\{.*\}\)/;
|
|
1703
|
+
let requesterDetails = {};
|
|
1704
|
+
const match = payload.query.match(findRequesterDetailsRegex);
|
|
1705
|
+
if (match) {
|
|
1706
|
+
const jsonString = match[0].slice(match[0].indexOf("{"), match[0].lastIndexOf("}") + 1);
|
|
1707
|
+
requesterDetails = ObjectUtils4.safeParse(jsonString);
|
|
1708
|
+
await eventManager_default.fireActionFromDbTrigger({ requesterDetails, mutationType }, payload);
|
|
1709
|
+
}
|
|
1550
1710
|
}
|
|
1551
1711
|
async createDatabaseFromSchema(schema, connection) {
|
|
1552
1712
|
const sqlFullStatement = this.generateDatabaseSchemaFromSchema(schema);
|
|
@@ -1557,7 +1717,11 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1557
1717
|
const sqlStatements = [];
|
|
1558
1718
|
const enums = [];
|
|
1559
1719
|
const indexes = [];
|
|
1720
|
+
const triggers = [];
|
|
1560
1721
|
for (const table of schema.database) {
|
|
1722
|
+
triggers.push(this.createInsertTriggers(table.name));
|
|
1723
|
+
triggers.push(this.createUpdateTrigger(table.name));
|
|
1724
|
+
triggers.push(this.createDeleteTrigger(table.name));
|
|
1561
1725
|
let sql = `CREATE TABLE "${table.name}"
|
|
1562
1726
|
( `;
|
|
1563
1727
|
const tableColumns = [];
|
|
@@ -1584,7 +1748,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1584
1748
|
}
|
|
1585
1749
|
if (column.isNullable) columnSql += " NULL";
|
|
1586
1750
|
else columnSql += " NOT NULL";
|
|
1587
|
-
if (column.default) columnSql += ` DEFAULT
|
|
1751
|
+
if (column.default) columnSql += ` DEFAULT ${column.default}`;
|
|
1588
1752
|
tableColumns.push(columnSql);
|
|
1589
1753
|
}
|
|
1590
1754
|
sql += tableColumns.join(", \n");
|
|
@@ -1595,7 +1759,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1595
1759
|
indexes.push(
|
|
1596
1760
|
` CREATE ${unique}INDEX "${index.name}" ON "${table.name}" (${index.columns.map((item) => {
|
|
1597
1761
|
return `"${item}" ${index.order}`;
|
|
1598
|
-
}).join(", ")})
|
|
1762
|
+
}).join(", ")});`
|
|
1599
1763
|
);
|
|
1600
1764
|
}
|
|
1601
1765
|
}
|
|
@@ -1625,7 +1789,8 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1625
1789
|
}
|
|
1626
1790
|
sqlStatements.push(sql + constraints.join(",\n") + ";");
|
|
1627
1791
|
}
|
|
1628
|
-
sqlStatements.push(indexes.join("
|
|
1792
|
+
sqlStatements.push(indexes.join("\n"));
|
|
1793
|
+
sqlStatements.push(triggers.join("\n"));
|
|
1629
1794
|
return enums.join("\n") + "\n" + sqlStatements.join("\n\n");
|
|
1630
1795
|
}
|
|
1631
1796
|
async getScratchPool() {
|
|
@@ -1671,12 +1836,10 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1671
1836
|
await originalClient.connect();
|
|
1672
1837
|
await scratchClient.connect();
|
|
1673
1838
|
const info1 = await pgInfo({
|
|
1674
|
-
client: originalClient
|
|
1675
|
-
schema: "public"
|
|
1839
|
+
client: originalClient
|
|
1676
1840
|
});
|
|
1677
1841
|
const info2 = await pgInfo({
|
|
1678
|
-
client: scratchClient
|
|
1679
|
-
schema: "public"
|
|
1842
|
+
client: scratchClient
|
|
1680
1843
|
});
|
|
1681
1844
|
const diff = getDiff(info1, info2);
|
|
1682
1845
|
await originalClient.end();
|
|
@@ -1695,8 +1858,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1695
1858
|
)) {
|
|
1696
1859
|
return "'[]'";
|
|
1697
1860
|
}
|
|
1698
|
-
return `COALESCE((
|
|
1699
|
-
SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
1861
|
+
return `COALESCE((SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
1700
1862
|
${item.subquery.properties.map((nestedItem) => {
|
|
1701
1863
|
if (!this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
|
|
1702
1864
|
...routeData.joins,
|
|
@@ -1705,7 +1867,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1705
1867
|
return;
|
|
1706
1868
|
}
|
|
1707
1869
|
if (nestedItem.subquery) {
|
|
1708
|
-
return `
|
|
1870
|
+
return `'${nestedItem.name}', ${this.createNestedSelect(
|
|
1709
1871
|
// recursion
|
|
1710
1872
|
req,
|
|
1711
1873
|
schema,
|
|
@@ -1716,7 +1878,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1716
1878
|
)}`;
|
|
1717
1879
|
}
|
|
1718
1880
|
return `'${nestedItem.name}', ${escapeColumnName(nestedItem.selector)}`;
|
|
1719
|
-
}).filter(Boolean).join(",")}
|
|
1881
|
+
}).filter(Boolean).join(", ")}
|
|
1720
1882
|
))
|
|
1721
1883
|
FROM
|
|
1722
1884
|
"${item.subquery.table}"
|
|
@@ -1844,10 +2006,12 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1844
2006
|
req.requesterDetails.role,
|
|
1845
2007
|
sqlParams
|
|
1846
2008
|
);
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
2009
|
+
const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
2010
|
+
if (whereClause.replace(/\s/g, "") === "") {
|
|
2011
|
+
throw new RsError("DELETE_FORBIDDEN", "Deletes need a where clause");
|
|
2012
|
+
}
|
|
2013
|
+
const deleteStatement = `
|
|
2014
|
+
DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
|
|
1851
2015
|
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
|
|
1852
2016
|
return true;
|
|
1853
2017
|
}
|
|
@@ -1910,17 +2074,17 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1910
2074
|
);
|
|
1911
2075
|
let operator = item.operator;
|
|
1912
2076
|
if (operator === "LIKE") {
|
|
1913
|
-
|
|
2077
|
+
item.value = `%${item.value}%`;
|
|
1914
2078
|
} else if (operator === "STARTS WITH") {
|
|
1915
2079
|
operator = "LIKE";
|
|
1916
|
-
|
|
2080
|
+
item.value = `${item.value}%`;
|
|
1917
2081
|
} else if (operator === "ENDS WITH") {
|
|
1918
2082
|
operator = "LIKE";
|
|
1919
|
-
|
|
2083
|
+
item.value = `%${item.value}`;
|
|
1920
2084
|
}
|
|
1921
2085
|
const replacedValue = this.replaceParamKeywords(item.value, routeData, req, sqlParams);
|
|
1922
2086
|
const escapedValue = SQL`${replacedValue}`;
|
|
1923
|
-
whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator} ${["IN", "NOT IN"].includes(operator) ? `(${escapedValue})` : escapedValue}
|
|
2087
|
+
whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator.replace("LIKE", "ILIKE")} ${["IN", "NOT IN"].includes(operator) ? `(${escapedValue})` : escapedValue}
|
|
1924
2088
|
`;
|
|
1925
2089
|
});
|
|
1926
2090
|
const data = req.data;
|
|
@@ -1954,63 +2118,94 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1954
2118
|
}
|
|
1955
2119
|
return whereClause;
|
|
1956
2120
|
}
|
|
2121
|
+
createUpdateTrigger(tableName) {
|
|
2122
|
+
return `
|
|
2123
|
+
CREATE OR REPLACE FUNCTION notify_${tableName}_update()
|
|
2124
|
+
RETURNS TRIGGER AS $$
|
|
2125
|
+
BEGIN
|
|
2126
|
+
PERFORM pg_notify('update', JSON_BUILD_OBJECT('table', '${tableName}', 'query', current_query(), 'record', NEW, 'previousRecord', OLD)::text);
|
|
2127
|
+
RETURN NEW;
|
|
2128
|
+
END;
|
|
2129
|
+
$$ LANGUAGE plpgsql;
|
|
2130
|
+
|
|
2131
|
+
CREATE OR REPLACE TRIGGER ${tableName}_update
|
|
2132
|
+
AFTER UPDATE ON "${tableName}"
|
|
2133
|
+
FOR EACH ROW
|
|
2134
|
+
EXECUTE FUNCTION notify_${tableName}_update();
|
|
2135
|
+
`;
|
|
2136
|
+
}
|
|
2137
|
+
createDeleteTrigger(tableName) {
|
|
2138
|
+
return `
|
|
2139
|
+
CREATE OR REPLACE FUNCTION notify_${tableName}_delete()
|
|
2140
|
+
RETURNS TRIGGER AS $$
|
|
2141
|
+
BEGIN
|
|
2142
|
+
PERFORM pg_notify('delete', JSON_BUILD_OBJECT('table', '${tableName}', 'query', current_query(), 'record', NEW, 'previousRecord', OLD)::text);
|
|
2143
|
+
RETURN NEW;
|
|
2144
|
+
END;
|
|
2145
|
+
$$ LANGUAGE plpgsql;
|
|
2146
|
+
|
|
2147
|
+
CREATE OR REPLACE TRIGGER "${tableName}_delete"
|
|
2148
|
+
AFTER DELETE ON "${tableName}"
|
|
2149
|
+
FOR EACH ROW
|
|
2150
|
+
EXECUTE FUNCTION notify_${tableName}_delete();
|
|
2151
|
+
`;
|
|
2152
|
+
}
|
|
2153
|
+
createInsertTriggers(tableName) {
|
|
2154
|
+
return `
|
|
2155
|
+
CREATE OR REPLACE FUNCTION notify_${tableName}_insert()
|
|
2156
|
+
RETURNS TRIGGER AS $$
|
|
2157
|
+
BEGIN
|
|
2158
|
+
PERFORM pg_notify('insert', JSON_BUILD_OBJECT('table', '${tableName}', 'query', current_query(), 'record', NEW, 'previousRecord', OLD)::text);
|
|
2159
|
+
RETURN NEW;
|
|
2160
|
+
END;
|
|
2161
|
+
$$ LANGUAGE plpgsql;
|
|
2162
|
+
|
|
2163
|
+
CREATE TRIGGER "${tableName}_insert"
|
|
2164
|
+
AFTER INSERT ON "${tableName}"
|
|
2165
|
+
FOR EACH ROW
|
|
2166
|
+
EXECUTE FUNCTION notify_${tableName}_insert();
|
|
2167
|
+
`;
|
|
2168
|
+
}
|
|
1957
2169
|
};
|
|
1958
|
-
|
|
2170
|
+
__decorateClass([
|
|
2171
|
+
boundMethod
|
|
2172
|
+
], PsqlEngine.prototype, "handleTrigger", 1);
|
|
2173
|
+
__decorateClass([
|
|
2174
|
+
boundMethod
|
|
2175
|
+
], PsqlEngine.prototype, "createUpdateTrigger", 1);
|
|
2176
|
+
__decorateClass([
|
|
2177
|
+
boundMethod
|
|
2178
|
+
], PsqlEngine.prototype, "createDeleteTrigger", 1);
|
|
2179
|
+
__decorateClass([
|
|
2180
|
+
boundMethod
|
|
2181
|
+
], PsqlEngine.prototype, "createInsertTriggers", 1);
|
|
2182
|
+
function schemaToPsqlType(column, tableName) {
|
|
1959
2183
|
if (column.hasAutoIncrement) return "BIGSERIAL";
|
|
1960
2184
|
if (column.type === "ENUM") return `"${tableName}_${column.name}_enum"`;
|
|
1961
2185
|
if (column.type === "DATETIME") return "TIMESTAMPTZ";
|
|
1962
2186
|
if (column.type === "MEDIUMINT") return "INT";
|
|
1963
2187
|
return column.type;
|
|
1964
|
-
}
|
|
1965
|
-
|
|
1966
|
-
// src/restura/sql/PsqlTransaction.ts
|
|
1967
|
-
import pg2 from "pg";
|
|
1968
|
-
var { Client: Client2 } = pg2;
|
|
1969
|
-
var PsqlTransaction = class extends PsqlConnection {
|
|
1970
|
-
constructor(clientConfig) {
|
|
1971
|
-
super();
|
|
1972
|
-
this.clientConfig = clientConfig;
|
|
1973
|
-
this.client = new Client2(clientConfig);
|
|
1974
|
-
this.beginTransactionPromise = this.beginTransaction();
|
|
1975
|
-
}
|
|
1976
|
-
async beginTransaction() {
|
|
1977
|
-
return this.query("BEGIN");
|
|
1978
|
-
}
|
|
1979
|
-
async rollback() {
|
|
1980
|
-
return this.query("ROLLBACK");
|
|
1981
|
-
}
|
|
1982
|
-
async commit() {
|
|
1983
|
-
return this.query("COMMIT");
|
|
1984
|
-
}
|
|
1985
|
-
async release() {
|
|
1986
|
-
return this.client.end();
|
|
1987
|
-
}
|
|
1988
|
-
async query(query, values) {
|
|
1989
|
-
await this.client.connect();
|
|
1990
|
-
await this.beginTransactionPromise;
|
|
1991
|
-
return this.client.query(query, values);
|
|
1992
|
-
}
|
|
1993
|
-
};
|
|
2188
|
+
}
|
|
1994
2189
|
|
|
1995
2190
|
// src/restura/compareSchema.ts
|
|
1996
2191
|
import cloneDeep from "lodash.clonedeep";
|
|
1997
2192
|
var CompareSchema = class {
|
|
1998
2193
|
async diffSchema(newSchema, latestSchema, psqlEngine) {
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2194
|
+
const endPoints = this.diffEndPoints(newSchema.endpoints[0].routes, latestSchema.endpoints[0].routes);
|
|
2195
|
+
const globalParams = this.diffStringArray(newSchema.globalParams, latestSchema.globalParams);
|
|
2196
|
+
const roles = this.diffStringArray(newSchema.roles, latestSchema.roles);
|
|
2002
2197
|
let commands = "";
|
|
2003
2198
|
if (JSON.stringify(newSchema.database) !== JSON.stringify(latestSchema.database))
|
|
2004
2199
|
commands = await psqlEngine.diffDatabaseToSchema(newSchema);
|
|
2005
|
-
|
|
2200
|
+
const customTypes = newSchema.customTypes !== latestSchema.customTypes;
|
|
2006
2201
|
const schemaPreview = { endPoints, globalParams, roles, commands, customTypes };
|
|
2007
2202
|
return schemaPreview;
|
|
2008
2203
|
}
|
|
2009
2204
|
diffStringArray(newArray, originalArray) {
|
|
2010
|
-
|
|
2011
|
-
|
|
2205
|
+
const stringsDiff = [];
|
|
2206
|
+
const originalClone = new Set(originalArray);
|
|
2012
2207
|
newArray.forEach((item) => {
|
|
2013
|
-
|
|
2208
|
+
const originalIndex = originalClone.has(item);
|
|
2014
2209
|
if (!originalIndex) {
|
|
2015
2210
|
stringsDiff.push({
|
|
2016
2211
|
name: item,
|
|
@@ -2029,11 +2224,11 @@ var CompareSchema = class {
|
|
|
2029
2224
|
return stringsDiff;
|
|
2030
2225
|
}
|
|
2031
2226
|
diffEndPoints(newEndPoints, originalEndpoints) {
|
|
2032
|
-
|
|
2033
|
-
|
|
2227
|
+
const originalClone = cloneDeep(originalEndpoints);
|
|
2228
|
+
const diffObj = [];
|
|
2034
2229
|
newEndPoints.forEach((endPoint) => {
|
|
2035
|
-
|
|
2036
|
-
|
|
2230
|
+
const { path: path4, method } = endPoint;
|
|
2231
|
+
const endPointIndex = originalClone.findIndex((original) => {
|
|
2037
2232
|
return original.path === endPoint.path && original.method === endPoint.method;
|
|
2038
2233
|
});
|
|
2039
2234
|
if (endPointIndex === -1) {
|
|
@@ -2042,7 +2237,7 @@ var CompareSchema = class {
|
|
|
2042
2237
|
changeType: "NEW"
|
|
2043
2238
|
});
|
|
2044
2239
|
} else {
|
|
2045
|
-
|
|
2240
|
+
const original = originalClone.findIndex((original2) => {
|
|
2046
2241
|
return this.compareEndPoints(endPoint, original2);
|
|
2047
2242
|
});
|
|
2048
2243
|
if (original === -1) {
|
|
@@ -2055,7 +2250,7 @@ var CompareSchema = class {
|
|
|
2055
2250
|
}
|
|
2056
2251
|
});
|
|
2057
2252
|
originalClone.forEach((original) => {
|
|
2058
|
-
|
|
2253
|
+
const { path: path4, method } = original;
|
|
2059
2254
|
diffObj.push({
|
|
2060
2255
|
name: `${method} ${path4}`,
|
|
2061
2256
|
changeType: "DELETED"
|
|
@@ -2458,11 +2653,13 @@ var setupPgReturnTypes = () => {
|
|
|
2458
2653
|
};
|
|
2459
2654
|
setupPgReturnTypes();
|
|
2460
2655
|
var restura = new ResturaEngine();
|
|
2656
|
+
|
|
2657
|
+
// src/restura/sql/PsqlTransaction.ts
|
|
2658
|
+
import pg4 from "pg";
|
|
2659
|
+
var { Client: Client2 } = pg4;
|
|
2461
2660
|
export {
|
|
2462
2661
|
HtmlStatusCodes,
|
|
2463
|
-
PsqlConnection,
|
|
2464
2662
|
PsqlPool,
|
|
2465
|
-
PsqlTransaction,
|
|
2466
2663
|
RsError,
|
|
2467
2664
|
SQL,
|
|
2468
2665
|
escapeColumnName,
|