jinaga-server 3.1.5 → 3.3.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/.github/workflows/main.yml +1 -1
- package/.github/workflows/publish.yml +3 -3
- package/dist/http/router.js +37 -84
- package/dist/http/router.js.map +1 -1
- package/dist/jinaga-server.d.ts +2 -1
- package/dist/jinaga-server.js +18 -4
- package/dist/jinaga-server.js.map +1 -1
- package/dist/postgres/postgres-store.d.ts +2 -0
- package/dist/postgres/postgres-store.js +34 -0
- package/dist/postgres/postgres-store.js.map +1 -1
- package/dist/postgres/purge-sql.d.ts +7 -0
- package/dist/postgres/purge-sql.js +151 -0
- package/dist/postgres/purge-sql.js.map +1 -0
- package/dist/postgres/query-description.d.ts +1 -1
- package/dist/postgres/query-description.js +3 -3
- package/dist/postgres/query-description.js.map +1 -1
- package/dist/postgres/specification-result-sql.js +1 -1
- package/dist/postgres/specification-result-sql.js.map +1 -1
- package/dist/postgres/specification-sql.js +2 -2
- package/dist/postgres/specification-sql.js.map +1 -1
- package/integration-test/docker-compose.yaml +0 -1
- package/integration-test/jinaga-test/Dockerfile +2 -0
- package/integration-test/jinaga-test/orderModel.js +104 -0
- package/integration-test/jinaga-test/package-lock.json +147 -86
- package/integration-test/jinaga-test/package.json +3 -3
- package/integration-test/jinaga-test/purge.test.js +224 -0
- package/integration-test/postgres/1-setup.sql +8 -3
- package/integration-test/postgres/2-grant.sh +4 -4
- package/integration-test/postgres/Dockerfile +1 -1
- package/package.json +5 -4
- package/replicator/package-lock.json +139 -71
- package/replicator/package.json +2 -2
- package/scripts/migrate_purge.sql +45 -0
- package/scripts/purge.sql +33 -0
- package/setup.sh +4 -4
- package/setup.sql +59 -3
- package/src/http/router.ts +38 -84
- package/src/jinaga-server.ts +27 -5
- package/src/postgres/postgres-store.ts +37 -0
- package/src/postgres/purge-sql.ts +176 -0
- package/src/postgres/query-description.ts +1 -1
- package/src/postgres/specification-result-sql.ts +3 -3
- package/src/postgres/specification-sql.ts +4 -4
- package/test/authorization/authorizationSpec.ts +1 -1
- package/test/models/blog.ts +92 -0
- package/test/postgres/purgeDescendantsSqlSpec.ts +41 -0
- package/test/postgres/purgeSqlSpec.ts +246 -0
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@stablelib/base64": "^1.0.1",
|
|
17
|
-
"express": "^4.
|
|
17
|
+
"express": "^4.21.1",
|
|
18
18
|
"jest": "^29.7.0",
|
|
19
19
|
"jest-compact-reporter": "^1.2.9",
|
|
20
|
-
"jinaga": "^6.
|
|
20
|
+
"jinaga": "^6.5.0",
|
|
21
21
|
"node-forge": "^1.3.0",
|
|
22
|
-
"pg": "^8.
|
|
22
|
+
"pg": "^8.13.1"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
const { JinagaServer } = require("./jinaga-server");
|
|
2
|
+
const { createModel, Item, Order, OrderCancelled, OrderCancelledReason, Product, Store } = require("./orderModel");
|
|
3
|
+
|
|
4
|
+
const host = "db";
|
|
5
|
+
// const host = "localhost";
|
|
6
|
+
const connectionString = `postgresql://dev:devpw@${host}:5432/integrationtest`;
|
|
7
|
+
|
|
8
|
+
const model = createModel();
|
|
9
|
+
|
|
10
|
+
describe("After-the-fact purge", () => {
|
|
11
|
+
let jInit, jPurge;
|
|
12
|
+
let closeInit, closePurge;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
({ j: jInit, close: closeInit } = JinagaServer.create({
|
|
16
|
+
pgKeystore: connectionString,
|
|
17
|
+
pgStore: connectionString
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
({ j: jPurge, close: closePurge } = JinagaServer.create({
|
|
21
|
+
pgKeystore: connectionString,
|
|
22
|
+
pgStore: connectionString,
|
|
23
|
+
purgeConditions: p => p
|
|
24
|
+
.whenExists(model.given(Order).match((order, facts) => facts.ofType(OrderCancelled)
|
|
25
|
+
.join(orderCancelled => orderCancelled.order, order)
|
|
26
|
+
))
|
|
27
|
+
}));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(async () => {
|
|
31
|
+
await closeInit();
|
|
32
|
+
await closePurge();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("Should find descendants if purge condition is not met", async () => {
|
|
36
|
+
const store = await jInit.fact(new Store("storeId"));
|
|
37
|
+
const order = await jInit.fact(new Order(store, new Date()));
|
|
38
|
+
const item1 = await jInit.fact(new Item(order, new Product(store, "product1"), 1));
|
|
39
|
+
const item2 = await jInit.fact(new Item(order, new Product(store, "product2"), 1));
|
|
40
|
+
|
|
41
|
+
const itemsInOrder = model.given(Order).match(order =>
|
|
42
|
+
order.successors(Item, item => item.order)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
await jPurge.purge();
|
|
46
|
+
const items = await jPurge.query(itemsInOrder, order);
|
|
47
|
+
expect(items).toEqual([item1, item2]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("Should purge descendants when condition is met", async () => {
|
|
51
|
+
const store = await jInit.fact(new Store("storeId"));
|
|
52
|
+
const order = await jInit.fact(new Order(store, new Date()));
|
|
53
|
+
const item1 = await jInit.fact(new Item(order, new Product(store, "product1"), 1));
|
|
54
|
+
const item2 = await jInit.fact(new Item(order, new Product(store, "product2"), 1));
|
|
55
|
+
const orderCancelled = await jInit.fact(new OrderCancelled(order, new Date()));
|
|
56
|
+
|
|
57
|
+
const itemsInOrder = model.given(Order).match(order =>
|
|
58
|
+
order.successors(Item, item => item.order)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
await jPurge.purge();
|
|
62
|
+
const items = await jPurge.query(itemsInOrder, order);
|
|
63
|
+
expect(items).toEqual([]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("Should not purge the trigger fact", async () => {
|
|
67
|
+
const store = await jInit.fact(new Store("storeId"));
|
|
68
|
+
const order = await jInit.fact(new Order(store, new Date()));
|
|
69
|
+
const item1 = await jInit.fact(new Item(order, new Product(store, "product1"), 1));
|
|
70
|
+
const item2 = await jInit.fact(new Item(order, new Product(store, "product2"), 1));
|
|
71
|
+
const orderCancelled = await jInit.fact(new OrderCancelled(order, new Date()));
|
|
72
|
+
|
|
73
|
+
const cancelOfOrder = model.given(Order).match(order =>
|
|
74
|
+
order.successors(OrderCancelled, cancelled => cancelled.order)
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
await jPurge.purge();
|
|
78
|
+
const cancels = await jPurge.query(cancelOfOrder, order);
|
|
79
|
+
expect(cancels).toEqual([orderCancelled]);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("Real-time purge", () => {
|
|
84
|
+
let j;
|
|
85
|
+
let close;
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
({ j, close } = JinagaServer.create({
|
|
89
|
+
pgKeystore: connectionString,
|
|
90
|
+
pgStore: connectionString,
|
|
91
|
+
purgeConditions: p => p
|
|
92
|
+
.whenExists(model.given(Order).match((order, facts) => facts.ofType(OrderCancelled)
|
|
93
|
+
.join(orderCancelled => orderCancelled.order, order)
|
|
94
|
+
))
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
afterEach(async () => {
|
|
99
|
+
await close();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("Should find descendants if purge condition is not met", async () => {
|
|
103
|
+
const store = await j.fact(new Store("storeId"));
|
|
104
|
+
const order = await j.fact(new Order(store, new Date()));
|
|
105
|
+
const item1 = await j.fact(new Item(order, new Product(store, "product1"), 1));
|
|
106
|
+
const item2 = await j.fact(new Item(order, new Product(store, "product2"), 1));
|
|
107
|
+
|
|
108
|
+
const itemsInOrder = model.given(Order).match(order =>
|
|
109
|
+
order.successors(Item, item => item.order)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const items = await j.query(itemsInOrder, order);
|
|
113
|
+
expect(items).toEqual([item1, item2]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("Should purge descendants when condition is met", async () => {
|
|
117
|
+
const store = await j.fact(new Store("storeId"));
|
|
118
|
+
const order = await j.fact(new Order(store, new Date()));
|
|
119
|
+
const item1 = await j.fact(new Item(order, new Product(store, "product1"), 1));
|
|
120
|
+
const item2 = await j.fact(new Item(order, new Product(store, "product2"), 1));
|
|
121
|
+
const orderCancelled = await j.fact(new OrderCancelled(order, new Date()));
|
|
122
|
+
|
|
123
|
+
const itemsInOrder = model.given(Order).match(order =>
|
|
124
|
+
order.successors(Item, item => item.order)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const items = await j.query(itemsInOrder, order);
|
|
128
|
+
expect(items).toEqual([]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("Should not purge the trigger fact", async () => {
|
|
132
|
+
const store = await j.fact(new Store("storeId"));
|
|
133
|
+
const order = await j.fact(new Order(store, new Date()));
|
|
134
|
+
const item1 = await j.fact(new Item(order, new Product(store, "product1"), 1));
|
|
135
|
+
const item2 = await j.fact(new Item(order, new Product(store, "product2"), 1));
|
|
136
|
+
const orderCancelled = await j.fact(new OrderCancelled(order, new Date()));
|
|
137
|
+
|
|
138
|
+
const cancelOfOrder = model.given(Order).match(order =>
|
|
139
|
+
order.successors(OrderCancelled, cancelled => cancelled.order)
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const cancels = await j.query(cancelOfOrder, order);
|
|
143
|
+
expect(cancels).toEqual([orderCancelled]);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe("After-the-fact purge with deep trigger", () => {
|
|
148
|
+
let jInit, jPurge;
|
|
149
|
+
let closeInit, closePurge;
|
|
150
|
+
|
|
151
|
+
beforeEach(() => {
|
|
152
|
+
({ j: jInit, close: closeInit } = JinagaServer.create({
|
|
153
|
+
pgKeystore: connectionString,
|
|
154
|
+
pgStore: connectionString
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
({ j: jPurge, close: closePurge } = JinagaServer.create({
|
|
158
|
+
pgKeystore: connectionString,
|
|
159
|
+
pgStore: connectionString,
|
|
160
|
+
purgeConditions: p => p
|
|
161
|
+
.whenExists(model.given(Order).match((order, facts) => facts.ofType(OrderCancelledReason)
|
|
162
|
+
.join(orderCancelledReason => orderCancelledReason.orderCancelled.order, order)
|
|
163
|
+
))
|
|
164
|
+
}));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
afterEach(async () => {
|
|
168
|
+
await closeInit();
|
|
169
|
+
await closePurge();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("Should not purge ancestors of the trigger fact", async () => {
|
|
173
|
+
const store = await jInit.fact(new Store("storeId"));
|
|
174
|
+
const order = await jInit.fact(new Order(store, new Date()));
|
|
175
|
+
const item1 = await jInit.fact(new Item(order, new Product(store, "product1"), 1));
|
|
176
|
+
const item2 = await jInit.fact(new Item(order, new Product(store, "product2"), 1));
|
|
177
|
+
const orderCancelled = await jInit.fact(new OrderCancelled(order, new Date()));
|
|
178
|
+
const reason = await jInit.fact(new OrderCancelledReason(orderCancelled, "reason"));
|
|
179
|
+
|
|
180
|
+
const cancelOfOrder = model.given(Order).match(order =>
|
|
181
|
+
order.successors(OrderCancelled, cancelled => cancelled.order)
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
await jPurge.purge();
|
|
185
|
+
const cancels = await jPurge.query(cancelOfOrder, order);
|
|
186
|
+
expect(cancels).toEqual([orderCancelled]);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("Real-time purge with deep trigger", () => {
|
|
191
|
+
let j;
|
|
192
|
+
let close;
|
|
193
|
+
|
|
194
|
+
beforeEach(() => {
|
|
195
|
+
({ j, close } = JinagaServer.create({
|
|
196
|
+
pgKeystore: connectionString,
|
|
197
|
+
pgStore: connectionString,
|
|
198
|
+
purgeConditions: p => p
|
|
199
|
+
.whenExists(model.given(Order).match((order, facts) => facts.ofType(OrderCancelledReason)
|
|
200
|
+
.join(orderCancelledReason => orderCancelledReason.orderCancelled.order, order)
|
|
201
|
+
))
|
|
202
|
+
}));
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
afterEach(async () => {
|
|
206
|
+
await close();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("Should not purge ancestors of the trigger fact", async () => {
|
|
210
|
+
const store = await j.fact(new Store("storeId"));
|
|
211
|
+
const order = await j.fact(new Order(store, new Date()));
|
|
212
|
+
const item1 = await j.fact(new Item(order, new Product(store, "product1"), 1));
|
|
213
|
+
const item2 = await j.fact(new Item(order, new Product(store, "product2"), 1));
|
|
214
|
+
const orderCancelled = await j.fact(new OrderCancelled(order, new Date()));
|
|
215
|
+
const reason = await j.fact(new OrderCancelledReason(orderCancelled, "reason"));
|
|
216
|
+
|
|
217
|
+
const cancelOfOrder = model.given(Order).match(order =>
|
|
218
|
+
order.successors(OrderCancelled, cancelled => cancelled.order)
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const cancels = await j.query(cancelOfOrder, order);
|
|
222
|
+
expect(cancels).toEqual([orderCancelled]);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
@@ -106,11 +106,13 @@ IF (SELECT to_regclass('public.edge') IS NULL) THEN
|
|
|
106
106
|
successor_fact_id integer NOT NULL,
|
|
107
107
|
CONSTRAINT fk_successor_fact_id
|
|
108
108
|
FOREIGN KEY (successor_fact_id)
|
|
109
|
-
REFERENCES fact (fact_id)
|
|
109
|
+
REFERENCES fact (fact_id)
|
|
110
|
+
ON DELETE CASCADE,
|
|
110
111
|
predecessor_fact_id integer NOT NULL,
|
|
111
112
|
CONSTRAINT fk_predecessor_fact_id
|
|
112
113
|
FOREIGN KEY (predecessor_fact_id)
|
|
113
114
|
REFERENCES fact (fact_id)
|
|
115
|
+
ON DELETE CASCADE
|
|
114
116
|
);
|
|
115
117
|
|
|
116
118
|
|
|
@@ -135,11 +137,13 @@ IF (SELECT to_regclass('public.ancestor') IS NULL) THEN
|
|
|
135
137
|
fact_id integer NOT NULL,
|
|
136
138
|
CONSTRAINT fk_fact_id
|
|
137
139
|
FOREIGN KEY (fact_id)
|
|
138
|
-
REFERENCES fact (fact_id)
|
|
140
|
+
REFERENCES fact (fact_id)
|
|
141
|
+
ON DELETE CASCADE,
|
|
139
142
|
ancestor_fact_id integer NOT NULL,
|
|
140
143
|
CONSTRAINT fk_ancestor_fact_id
|
|
141
144
|
FOREIGN KEY (ancestor_fact_id)
|
|
142
145
|
REFERENCES fact (fact_id)
|
|
146
|
+
ON DELETE CASCADE
|
|
143
147
|
);
|
|
144
148
|
|
|
145
149
|
|
|
@@ -177,7 +181,8 @@ IF (SELECT to_regclass('public.signature') IS NULL) THEN
|
|
|
177
181
|
fact_id integer NOT NULL,
|
|
178
182
|
CONSTRAINT fk_fact_id
|
|
179
183
|
FOREIGN KEY (fact_id)
|
|
180
|
-
REFERENCES fact (fact_id)
|
|
184
|
+
REFERENCES fact (fact_id)
|
|
185
|
+
ON DELETE CASCADE,
|
|
181
186
|
public_key_id integer NOT NULL,
|
|
182
187
|
CONSTRAINT fk_public_key_id
|
|
183
188
|
FOREIGN KEY (public_key_id)
|
|
@@ -6,12 +6,12 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$APP_DATABASE" <<-
|
|
|
6
6
|
GRANT SELECT, USAGE ON SEQUENCE fact_type_fact_type_id_seq TO $APP_USERNAME;
|
|
7
7
|
GRANT SELECT,INSERT ON TABLE public.role TO $APP_USERNAME;
|
|
8
8
|
GRANT SELECT, USAGE ON SEQUENCE role_role_id_seq TO $APP_USERNAME;
|
|
9
|
-
GRANT SELECT,INSERT ON TABLE public.fact TO $APP_USERNAME;
|
|
9
|
+
GRANT SELECT,INSERT,DELETE ON TABLE public.fact TO $APP_USERNAME;
|
|
10
10
|
GRANT SELECT, USAGE ON SEQUENCE fact_fact_id_seq TO $APP_USERNAME;
|
|
11
|
-
GRANT SELECT,INSERT ON TABLE public.edge TO $APP_USERNAME;
|
|
12
|
-
GRANT SELECT,INSERT ON TABLE public.ancestor TO $APP_USERNAME;
|
|
11
|
+
GRANT SELECT,INSERT,DELETE ON TABLE public.edge TO $APP_USERNAME;
|
|
12
|
+
GRANT SELECT,INSERT,DELETE ON TABLE public.ancestor TO $APP_USERNAME;
|
|
13
13
|
GRANT SELECT,INSERT ON TABLE public.public_key TO $APP_USERNAME;
|
|
14
14
|
GRANT SELECT, USAGE ON SEQUENCE public_key_public_key_id_seq TO $APP_USERNAME;
|
|
15
|
-
GRANT SELECT,INSERT ON TABLE public.signature TO $APP_USERNAME;
|
|
15
|
+
GRANT SELECT,INSERT,DELETE ON TABLE public.signature TO $APP_USERNAME;
|
|
16
16
|
GRANT SELECT,INSERT ON TABLE public.user TO $APP_USERNAME;
|
|
17
17
|
EOSQL
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jinaga-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Data management for web and mobile applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"test": "tsc --noEmit --project tsconfig.test.json && jest",
|
|
11
11
|
"test:watch": "jest --watch",
|
|
12
12
|
"debug": "node ./dist/debug.js",
|
|
13
|
+
"prepack": "npm run clean && npm run build && npm run test",
|
|
13
14
|
"prepublishOnly": "npm run clean && npm run build && npm run test",
|
|
14
15
|
"integration": "npm run clean && npm run build && cd ./integration-test && ./run.sh",
|
|
15
16
|
"replicator": "npm run build && cd ./replicator && npm run build && npm run start && cd .."
|
|
@@ -46,8 +47,8 @@
|
|
|
46
47
|
"typescript": "^4.7.2"
|
|
47
48
|
},
|
|
48
49
|
"dependencies": {
|
|
49
|
-
"express": "^4.
|
|
50
|
-
"jinaga": "^6.
|
|
51
|
-
"pg": "^8.
|
|
50
|
+
"express": "^4.21.1",
|
|
51
|
+
"jinaga": "^6.5.0",
|
|
52
|
+
"pg": "^8.13.0"
|
|
52
53
|
}
|
|
53
54
|
}
|