jinaga-server 2.6.1 → 3.0.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/authentication/authentication-device.js +27 -6
- package/dist/authentication/authentication-device.js.map +1 -1
- package/dist/authentication/authentication-session.js +27 -6
- package/dist/authentication/authentication-session.js.map +1 -1
- package/dist/authorization/authorization-keystore.d.ts +4 -5
- package/dist/authorization/authorization-keystore.js +42 -18
- package/dist/authorization/authorization-keystore.js.map +1 -1
- package/dist/http/router.d.ts +1 -2
- package/dist/http/router.js +14 -18
- package/dist/http/router.js.map +1 -1
- package/dist/jinaga-server.js +5 -6
- package/dist/jinaga-server.js.map +1 -1
- package/dist/postgres/postgres-store.d.ts +2 -5
- package/dist/postgres/postgres-store.js +44 -121
- package/dist/postgres/postgres-store.js.map +1 -1
- package/http/sans-auth/Feed sites.http +1 -1
- package/integration-test/jinaga-test/jinaga.test.js +247 -231
- package/integration-test/jinaga-test/package-lock.json +16 -59
- package/integration-test/jinaga-test/package.json +2 -5
- package/package.json +3 -4
- package/src/authentication/authentication-device.ts +29 -7
- package/src/authentication/authentication-session.ts +29 -7
- package/src/authorization/authorization-keystore.ts +42 -22
- package/src/http/router.ts +14 -24
- package/src/jinaga-server.ts +5 -6
- package/src/postgres/postgres-store.ts +63 -149
- package/test/authorization/authorizationSpec.ts +75 -89
- package/dist/postgres/sql.d.ts +0 -10
- package/dist/postgres/sql.js +0 -408
- package/dist/postgres/sql.js.map +0 -1
- package/src/postgres/sql.ts +0 -498
- package/test/postgres/postgresSpec.ts +0 -391
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import "source-map-support/register";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
dehydrateReference,
|
|
5
|
-
Direction,
|
|
6
|
-
ExistentialCondition,
|
|
7
|
-
fromDescriptiveString,
|
|
8
|
-
Join,
|
|
9
|
-
PropertyCondition,
|
|
10
|
-
Step,
|
|
11
|
-
} from "jinaga";
|
|
12
|
-
|
|
13
|
-
import { addFactType, addRole, emptyFactTypeMap, emptyRoleMap, ensureGetFactTypeId, getFactTypeId, getRoleId } from "../../src/postgres/maps";
|
|
14
|
-
import { sqlFromSteps } from "../../src/postgres/sql";
|
|
15
|
-
import { distinct } from "../../src/util/fn";
|
|
16
|
-
|
|
17
|
-
describe('Postgres', () => {
|
|
18
|
-
|
|
19
|
-
const start = dehydrateReference({ type: 'Root' });
|
|
20
|
-
const startHash = 'fSS1hK7OGAeSX4ocN3acuFF87jvzCdPN3vLFUtcej0lOAsVV859UIYZLRcHUoMbyd/J31TdVn5QuE7094oqUPg==';
|
|
21
|
-
|
|
22
|
-
function sqlFor(descriptiveString: string) {
|
|
23
|
-
const query = fromDescriptiveString(descriptiveString);
|
|
24
|
-
const factTypes = allFactTypes(query.steps).filter(t => t !== 'Unknown').reduce(
|
|
25
|
-
(f, factType, i) => addFactType(f, factType, i + 1),
|
|
26
|
-
emptyFactTypeMap());
|
|
27
|
-
let roleMap = allRoles(query.steps, 'Root').filter(r => r.type !== 'Unknown' && r.role !== 'unknown').reduce(
|
|
28
|
-
(r, role, i) => addRole(r, ensureGetFactTypeId(factTypes, role.type), role.role, i + 1),
|
|
29
|
-
emptyRoleMap());
|
|
30
|
-
const sqlQuery = sqlFromSteps(start, query.steps, factTypes, roleMap, "public");
|
|
31
|
-
return sqlQuery ? { sql: sqlQuery.sql, parameters: sqlQuery.parameters, pathLength: sqlQuery.pathLength, empty: sqlQuery.empty, factTypes, roleMap } : null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
it('should parse empty query', () => {
|
|
35
|
-
expect(sqlFor('')).toEqual(null);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should error on successor query', () => {
|
|
39
|
-
const parse = () => sqlFor('S.predecessor');
|
|
40
|
-
expect(parse).toThrow('Missing type for role predecessor');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should error on predecessor query', () => {
|
|
44
|
-
const parse = () => sqlFor('P.parent');
|
|
45
|
-
expect(parse).toThrow(/Missing type of "Root.parent"/);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should error on double predecessor query', () => {
|
|
49
|
-
const parse = () => sqlFor('P.parent P.grandparent');
|
|
50
|
-
expect(parse).toThrow(/Missing type of "Root.parent"/);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should parse successor query with type', () => {
|
|
54
|
-
const { sql, parameters, pathLength, factTypes, roleMap } = sqlFor('S.predecessor F.type="IntegrationTest.Successor"')!;
|
|
55
|
-
expect(sql).toEqual(
|
|
56
|
-
'SELECT f2.hash as hash2 ' +
|
|
57
|
-
'FROM public.fact f1 ' +
|
|
58
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
59
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
60
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2'
|
|
61
|
-
);
|
|
62
|
-
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
63
|
-
expect(parameters[1]).toEqual(startHash);
|
|
64
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Successor'), 'predecessor'));
|
|
65
|
-
expect(pathLength).toEqual(1);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should parse predecessor query with type', () => {
|
|
69
|
-
const { sql, parameters, pathLength, factTypes, roleMap } = sqlFor('P.parent F.type="Parent"')!;
|
|
70
|
-
expect(sql).toEqual(
|
|
71
|
-
'SELECT f2.hash as hash2 ' +
|
|
72
|
-
'FROM public.fact f1 ' +
|
|
73
|
-
'JOIN public.edge e1 ON e1.successor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
74
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.predecessor_fact_id ' +
|
|
75
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2'
|
|
76
|
-
);
|
|
77
|
-
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
78
|
-
expect(parameters[1]).toEqual(startHash);
|
|
79
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Root'), 'parent'));
|
|
80
|
-
expect(pathLength).toEqual(1);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should parse successor query with existential', () => {
|
|
84
|
-
const { sql, parameters, pathLength, factTypes, roleMap } = sqlFor('S.predecessor F.type="IntegrationTest.Successor" E(S.successor F.type="IntegrationTest.Grandchild")')!;
|
|
85
|
-
expect(sql).toEqual(
|
|
86
|
-
'SELECT f2.hash as hash2 ' +
|
|
87
|
-
'FROM public.fact f1 ' +
|
|
88
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
89
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
90
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2 ' +
|
|
91
|
-
'AND EXISTS (SELECT 1 ' +
|
|
92
|
-
'FROM public.edge e2 ' +
|
|
93
|
-
'WHERE e2.predecessor_fact_id = e1.successor_fact_id AND e2.role_id = $4)'
|
|
94
|
-
);
|
|
95
|
-
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
96
|
-
expect(parameters[1]).toEqual(startHash);
|
|
97
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Successor'), 'predecessor'));
|
|
98
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Grandchild'), 'successor'));
|
|
99
|
-
expect(pathLength).toEqual(1);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should parse successor query with negative existential', () => {
|
|
103
|
-
const { sql, parameters, pathLength, factTypes, roleMap } = sqlFor('S.predecessor F.type="IntegrationTest.Successor" N(S.successor F.type="IntegrationTest.Grandchild")')!;
|
|
104
|
-
expect(sql).toEqual(
|
|
105
|
-
'SELECT f2.hash as hash2 ' +
|
|
106
|
-
'FROM public.fact f1 ' +
|
|
107
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
108
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
109
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2 ' +
|
|
110
|
-
'AND NOT EXISTS (SELECT 1 ' +
|
|
111
|
-
'FROM public.edge e2 ' +
|
|
112
|
-
'WHERE e2.predecessor_fact_id = e1.successor_fact_id AND e2.role_id = $4)'
|
|
113
|
-
);
|
|
114
|
-
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
115
|
-
expect(parameters[1]).toEqual(startHash);
|
|
116
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Successor'), 'predecessor'));
|
|
117
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Grandchild'), 'successor'));
|
|
118
|
-
expect(pathLength).toEqual(1);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should parse successor query with existential predecessor', () => {
|
|
122
|
-
const { sql, parameters, pathLength, factTypes, roleMap } = sqlFor('S.parent F.type="Child" E(P.uncle F.type="Uncle")')!;
|
|
123
|
-
expect(sql).toEqual(
|
|
124
|
-
'SELECT f2.hash as hash2 ' +
|
|
125
|
-
'FROM public.fact f1 ' +
|
|
126
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
127
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
128
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2 ' +
|
|
129
|
-
'AND EXISTS (SELECT 1 ' +
|
|
130
|
-
'FROM public.edge e2 ' +
|
|
131
|
-
'WHERE e2.successor_fact_id = e1.successor_fact_id AND e2.role_id = $4)'
|
|
132
|
-
);
|
|
133
|
-
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
134
|
-
expect(parameters[1]).toEqual(startHash);
|
|
135
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'parent'));
|
|
136
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'uncle'));
|
|
137
|
-
expect(pathLength).toEqual(1);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should parse successor query with negative existential predecessor', () => {
|
|
141
|
-
const { sql, parameters, pathLength, factTypes, roleMap } = sqlFor('S.parent F.type="Child" N(P.uncle F.type="Uncle")')!;
|
|
142
|
-
expect(sql).toEqual(
|
|
143
|
-
'SELECT f2.hash as hash2 ' +
|
|
144
|
-
'FROM public.fact f1 ' +
|
|
145
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
146
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
147
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2 ' +
|
|
148
|
-
'AND NOT EXISTS (SELECT 1 ' +
|
|
149
|
-
'FROM public.edge e2 ' +
|
|
150
|
-
'WHERE e2.successor_fact_id = e1.successor_fact_id AND e2.role_id = $4)'
|
|
151
|
-
);
|
|
152
|
-
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
153
|
-
expect(parameters[1]).toEqual(startHash);
|
|
154
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'parent'));
|
|
155
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'uncle'));
|
|
156
|
-
expect(pathLength).toEqual(1);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should parse consecutive existential queries', () => {
|
|
160
|
-
const { sql, parameters, factTypes, roleMap } = sqlFor('S.parent F.type="Child" N(S.condition F.type="Condition") N(S.other F.type="Other")')!;
|
|
161
|
-
expect(sql).toEqual(
|
|
162
|
-
'SELECT f2.hash as hash2 ' +
|
|
163
|
-
'FROM public.fact f1 ' +
|
|
164
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
165
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
166
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2 ' +
|
|
167
|
-
'AND NOT EXISTS (SELECT 1 ' +
|
|
168
|
-
'FROM public.edge e2 ' +
|
|
169
|
-
'WHERE e2.predecessor_fact_id = e1.successor_fact_id AND e2.role_id = $4) ' +
|
|
170
|
-
'AND NOT EXISTS (SELECT 1 ' +
|
|
171
|
-
'FROM public.edge e3 ' +
|
|
172
|
-
'WHERE e3.predecessor_fact_id = e1.successor_fact_id AND e3.role_id = $5)'
|
|
173
|
-
);
|
|
174
|
-
expect(parameters).toEqual([
|
|
175
|
-
getFactTypeId(factTypes, 'Root'),
|
|
176
|
-
startHash,
|
|
177
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'parent'),
|
|
178
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Condition'), 'condition'),
|
|
179
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Other'), 'other')
|
|
180
|
-
]);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should parse existential query form predecessor', () => {
|
|
184
|
-
const { sql, parameters, factTypes, roleMap } = sqlFor('S.root F.type="Identifier" N(S.prior F.type="Identifier") P.identified F.type="Identified" N(S.identified F.type="Delete")')!;
|
|
185
|
-
expect(sql).toEqual(
|
|
186
|
-
'SELECT f2.hash as hash2, f3.hash as hash3 ' +
|
|
187
|
-
'FROM public.fact f1 ' +
|
|
188
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
189
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
190
|
-
'JOIN public.edge e3 ON e3.successor_fact_id = e1.successor_fact_id AND e3.role_id = $5 ' +
|
|
191
|
-
'JOIN public.fact f3 ON f3.fact_id = e3.predecessor_fact_id ' +
|
|
192
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2 ' +
|
|
193
|
-
'AND NOT EXISTS (SELECT 1 ' +
|
|
194
|
-
'FROM public.edge e2 ' +
|
|
195
|
-
'WHERE e2.predecessor_fact_id = e1.successor_fact_id AND e2.role_id = $4) ' +
|
|
196
|
-
'AND NOT EXISTS (SELECT 1 ' +
|
|
197
|
-
'FROM public.edge e4 ' +
|
|
198
|
-
'WHERE e4.predecessor_fact_id = e3.predecessor_fact_id AND e4.role_id = $6)'
|
|
199
|
-
);
|
|
200
|
-
expect(parameters).toEqual([
|
|
201
|
-
getFactTypeId(factTypes, 'Root'),
|
|
202
|
-
startHash,
|
|
203
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Identifier'), 'root'),
|
|
204
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Identifier'), 'prior'),
|
|
205
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Identifier'), 'identified'),
|
|
206
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Delete'), 'identified')
|
|
207
|
-
]);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('should error on existential query on predecessor with no type', () => {
|
|
211
|
-
const parse = () => sqlFor('S.root F.type="Identifier" N(S.prior F.type="Identifier") P.identified N(S.identified F.type="Delete")');
|
|
212
|
-
expect(parse).toThrow(/Missing type of "Identifier.identified"/);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('should parse zig-zag pipeline', () => {
|
|
216
|
-
const { sql, parameters, pathLength, factTypes, roleMap } = sqlFor('S.user F.type="Assignment" P.project F.type="Project" S.project F.type="Task" S.task F.type="Task.Title"')!;
|
|
217
|
-
expect(sql).toEqual(
|
|
218
|
-
'SELECT f2.hash as hash2, f3.hash as hash3, f4.hash as hash4, f5.hash as hash5 ' +
|
|
219
|
-
'FROM public.fact f1 ' +
|
|
220
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
221
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
222
|
-
'JOIN public.edge e2 ON e2.successor_fact_id = e1.successor_fact_id AND e2.role_id = $4 ' +
|
|
223
|
-
'JOIN public.fact f3 ON f3.fact_id = e2.predecessor_fact_id ' +
|
|
224
|
-
'JOIN public.edge e3 ON e3.predecessor_fact_id = e2.predecessor_fact_id AND e3.role_id = $5 ' +
|
|
225
|
-
'JOIN public.fact f4 ON f4.fact_id = e3.successor_fact_id ' +
|
|
226
|
-
'JOIN public.edge e4 ON e4.predecessor_fact_id = e3.successor_fact_id AND e4.role_id = $6 ' +
|
|
227
|
-
'JOIN public.fact f5 ON f5.fact_id = e4.successor_fact_id ' +
|
|
228
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2'
|
|
229
|
-
);
|
|
230
|
-
expect(parameters).toEqual([
|
|
231
|
-
getFactTypeId(factTypes, 'Root'),
|
|
232
|
-
startHash,
|
|
233
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'user'),
|
|
234
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'project'),
|
|
235
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Task'), 'project'),
|
|
236
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Task.Title'), 'task')
|
|
237
|
-
]);
|
|
238
|
-
expect(pathLength).toEqual(4);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should parse query with unknown successor type', () => {
|
|
242
|
-
const { empty } = sqlFor('S.root F.type="Unknown"')!;
|
|
243
|
-
expect(empty).toBeTruthy();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should parse query with unknown predecessor type', () => {
|
|
247
|
-
const { empty } = sqlFor('S.root F.type="Assignment" P.pred F.type="Unknown" P.user F.type="Jinaga.User"')!;
|
|
248
|
-
expect(empty).toBeTruthy();
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('should parse query with some unknown types', () => {
|
|
252
|
-
const { empty } = sqlFor('S.root F.type="Unknown" P.user F.type="Jinaga.User"')!;
|
|
253
|
-
expect(empty).toBeTruthy();
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it('should parse query with unknown successor role', () => {
|
|
257
|
-
const { empty } = sqlFor('S.unknown F.type="Successor"')!;
|
|
258
|
-
expect(empty).toBeTruthy();
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it('should parse query with unknown predecessor role', () => {
|
|
262
|
-
const { empty } = sqlFor('S.root F.type="Assignment" P.unknown F.type="Jinaga.User"')!;
|
|
263
|
-
expect(empty).toBeTruthy();
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it('should parse query with some unknown predecessor roles', () => {
|
|
267
|
-
const { empty } = sqlFor('S.root F.type="Assignment" P.predecessor F.type="Predecessor" P.unknown F.type="Jinaga.User"')!;
|
|
268
|
-
expect(empty).toBeTruthy();
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('should ignore negative existential with unknown type', () => {
|
|
272
|
-
const { empty, sql, parameters, factTypes, roleMap } = sqlFor('S.root F.type="Assignment" N(S.assignment F.type="Unknown")')!;
|
|
273
|
-
expect(empty).toBeFalsy();
|
|
274
|
-
expect(sql).toEqual(
|
|
275
|
-
'SELECT f2.hash as hash2 ' +
|
|
276
|
-
'FROM public.fact f1 ' +
|
|
277
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
278
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
279
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2'
|
|
280
|
-
);
|
|
281
|
-
expect(parameters).toEqual([
|
|
282
|
-
getFactTypeId(factTypes, 'Root'),
|
|
283
|
-
startHash,
|
|
284
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'root')
|
|
285
|
-
]);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('should ignore negative existential with unknown role', () => {
|
|
289
|
-
const { empty, sql, parameters, factTypes, roleMap } = sqlFor('S.root F.type="Assignment" N(S.unknown F.type="Assignment.Delete")')!;
|
|
290
|
-
expect(empty).toBeFalsy();
|
|
291
|
-
expect(sql).toEqual(
|
|
292
|
-
'SELECT f2.hash as hash2 ' +
|
|
293
|
-
'FROM public.fact f1 ' +
|
|
294
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
295
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
296
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2'
|
|
297
|
-
);
|
|
298
|
-
expect(parameters).toEqual([
|
|
299
|
-
getFactTypeId(factTypes, 'Root'),
|
|
300
|
-
startHash,
|
|
301
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'root')
|
|
302
|
-
]);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it('should elevate positive existential with unknown type', () => {
|
|
306
|
-
const { empty } = sqlFor('S.root F.type="Assignment" E(S.assignment F.type="Unknown")')!;
|
|
307
|
-
expect(empty).toBeTruthy();
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('should elevate positive existential with unknown role', () => {
|
|
311
|
-
const { empty } = sqlFor('S.root F.type="Assignment" E(S.unknown F.type="Assignment.Delete")')!;
|
|
312
|
-
expect(empty).toBeTruthy();
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('should parse nested existential condition', () => {
|
|
316
|
-
const { sql, parameters, factTypes, roleMap } = sqlFor('S.catalog F.type="ImprovingU.Idea" N(S.idea F.type="ImprovingU.Idea.Deletion" N(S.ideaDeletion F.type="ImprovingU.Idea.Restore"))')!;
|
|
317
|
-
|
|
318
|
-
expect(sql).toEqual(
|
|
319
|
-
'SELECT f2.hash as hash2 ' +
|
|
320
|
-
'FROM public.fact f1 ' +
|
|
321
|
-
'JOIN public.edge e1 ON e1.predecessor_fact_id = f1.fact_id AND e1.role_id = $3 ' +
|
|
322
|
-
'JOIN public.fact f2 ON f2.fact_id = e1.successor_fact_id ' +
|
|
323
|
-
'WHERE f1.fact_type_id = $1 AND f1.hash = $2 ' +
|
|
324
|
-
'AND NOT EXISTS (' +
|
|
325
|
-
'SELECT 1 ' +
|
|
326
|
-
'FROM public.edge e2 ' +
|
|
327
|
-
'WHERE e2.predecessor_fact_id = e1.successor_fact_id AND e2.role_id = $4 ' +
|
|
328
|
-
'AND NOT EXISTS (' +
|
|
329
|
-
'SELECT 1 ' +
|
|
330
|
-
'FROM public.edge e3 ' +
|
|
331
|
-
'WHERE e3.predecessor_fact_id = e2.successor_fact_id AND e3.role_id = $5' +
|
|
332
|
-
')' +
|
|
333
|
-
')');
|
|
334
|
-
expect(parameters).toEqual([
|
|
335
|
-
getFactTypeId(factTypes, 'Root'),
|
|
336
|
-
startHash,
|
|
337
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'ImprovingU.Idea'), 'catalog'),
|
|
338
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'ImprovingU.Idea.Deletion'), 'idea'),
|
|
339
|
-
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'ImprovingU.Idea.Restore'), 'ideaDeletion')
|
|
340
|
-
]);
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
function allFactTypes(steps: Step[]): string[] {
|
|
345
|
-
const factTypes = steps
|
|
346
|
-
.filter(step => step instanceof PropertyCondition && step.name === 'type')
|
|
347
|
-
.map(step => (step as PropertyCondition).value);
|
|
348
|
-
const childFactTypes = steps
|
|
349
|
-
.filter(step => step instanceof ExistentialCondition)
|
|
350
|
-
.flatMap(step => allFactTypes((step as ExistentialCondition).steps));
|
|
351
|
-
return ["Root", ...factTypes, ...childFactTypes].filter(distinct);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function allRoles(steps: Step[], initialType: string) {
|
|
355
|
-
let roles: { type: string; role: string }[] = [];
|
|
356
|
-
let type: string | undefined = initialType;
|
|
357
|
-
let role: string | undefined = undefined;
|
|
358
|
-
|
|
359
|
-
for (const step of steps) {
|
|
360
|
-
if (step instanceof PropertyCondition) {
|
|
361
|
-
if (step.name === 'type') {
|
|
362
|
-
type = step.value;
|
|
363
|
-
if (role) {
|
|
364
|
-
if (type) {
|
|
365
|
-
roles.push({ type, role });
|
|
366
|
-
}
|
|
367
|
-
role = undefined;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
else if (step instanceof Join) {
|
|
372
|
-
if (step.direction === Direction.Predecessor) {
|
|
373
|
-
if (type) {
|
|
374
|
-
roles.push({ type, role: step.role });
|
|
375
|
-
}
|
|
376
|
-
role = undefined;
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
role = step.role;
|
|
380
|
-
}
|
|
381
|
-
type = undefined;
|
|
382
|
-
}
|
|
383
|
-
else if (step instanceof ExistentialCondition) {
|
|
384
|
-
if (type) {
|
|
385
|
-
roles = roles.concat(allRoles(step.steps, type));
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return roles;
|
|
391
|
-
}
|