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.
Files changed (32) hide show
  1. package/dist/authentication/authentication-device.js +27 -6
  2. package/dist/authentication/authentication-device.js.map +1 -1
  3. package/dist/authentication/authentication-session.js +27 -6
  4. package/dist/authentication/authentication-session.js.map +1 -1
  5. package/dist/authorization/authorization-keystore.d.ts +4 -5
  6. package/dist/authorization/authorization-keystore.js +42 -18
  7. package/dist/authorization/authorization-keystore.js.map +1 -1
  8. package/dist/http/router.d.ts +1 -2
  9. package/dist/http/router.js +14 -18
  10. package/dist/http/router.js.map +1 -1
  11. package/dist/jinaga-server.js +5 -6
  12. package/dist/jinaga-server.js.map +1 -1
  13. package/dist/postgres/postgres-store.d.ts +2 -5
  14. package/dist/postgres/postgres-store.js +44 -121
  15. package/dist/postgres/postgres-store.js.map +1 -1
  16. package/http/sans-auth/Feed sites.http +1 -1
  17. package/integration-test/jinaga-test/jinaga.test.js +247 -231
  18. package/integration-test/jinaga-test/package-lock.json +16 -59
  19. package/integration-test/jinaga-test/package.json +2 -5
  20. package/package.json +3 -4
  21. package/src/authentication/authentication-device.ts +29 -7
  22. package/src/authentication/authentication-session.ts +29 -7
  23. package/src/authorization/authorization-keystore.ts +42 -22
  24. package/src/http/router.ts +14 -24
  25. package/src/jinaga-server.ts +5 -6
  26. package/src/postgres/postgres-store.ts +63 -149
  27. package/test/authorization/authorizationSpec.ts +75 -89
  28. package/dist/postgres/sql.d.ts +0 -10
  29. package/dist/postgres/sql.js +0 -408
  30. package/dist/postgres/sql.js.map +0 -1
  31. package/src/postgres/sql.ts +0 -498
  32. package/test/postgres/postgresSpec.ts +0 -391
@@ -1,25 +1,18 @@
1
1
  import {
2
2
  canonicalPredecessors,
3
- Direction,
4
- ExistentialCondition,
5
3
  FactEnvelope,
6
4
  FactFeed,
7
- FactPath,
8
5
  FactRecord,
9
6
  FactReference,
10
7
  factReferenceEquals,
8
+ FactSignature,
11
9
  FactTuple,
12
10
  getAllFactTypes,
13
11
  getAllRoles,
14
- Join,
15
12
  PredecessorCollection,
16
13
  ProjectedResult,
17
- PropertyCondition,
18
- Query,
19
14
  Specification,
20
- Step,
21
- Storage,
22
- TopologicalSorter
15
+ Storage
23
16
  } from "jinaga";
24
17
  import { Pool, PoolClient } from "pg";
25
18
 
@@ -51,7 +44,6 @@ import {
51
44
  } from "./maps";
52
45
  import { ResultSetFact, ResultSetRow, ResultSetTree, resultSqlFromSpecification, SqlQueryTree } from "./specification-result-sql";
53
46
  import { sqlFromFeed } from "./specification-sql";
54
- import { sqlFromSteps } from "./sql";
55
47
 
56
48
  interface FactTypeResult {
57
49
  rows: {
@@ -96,6 +88,8 @@ interface AncestorResult {
96
88
  name: string;
97
89
  hash: string;
98
90
  data: string;
91
+ public_key: string;
92
+ signature: string;
99
93
  }[];
100
94
  }
101
95
 
@@ -125,21 +119,6 @@ function loadFactTuple(labels: SpecificationLabel[], row: Row): FactTuple {
125
119
  };
126
120
  }
127
121
 
128
- function loadFactPath(pathLength: number, factTypeNames: string[], r: Row): FactPath {
129
- let path: FactPath = [];
130
- for (let i = 0; i < pathLength; i++) {
131
- const hash = r['hash' + (i + 2)];
132
- if (!hash) {
133
- throw new Error(`Cannot find column 'hash${i + 2}'`);
134
- }
135
- path.push({
136
- type: factTypeNames[i],
137
- hash: hash
138
- });
139
- }
140
- return path;
141
- }
142
-
143
122
  export class PostgresStore implements Storage {
144
123
  private connectionFactory: ConnectionFactory;
145
124
  private factTypeMap: FactTypeMap = emptyFactTypeMap();
@@ -204,32 +183,6 @@ export class PostgresStore implements Storage {
204
183
  }
205
184
  }
206
185
 
207
- async query(start: FactReference, query: Query): Promise<FactPath[]> {
208
- if (query.steps.length === 0) {
209
- return [[start]];
210
- }
211
-
212
- try {
213
- const factTypes = await this.loadFactTypesFromSteps(query.steps, start.type);
214
- const roleMap = await this.loadRolesFromSteps(query.steps, factTypes, start.type);
215
-
216
- const sqlQuery = sqlFromSteps(start, query.steps, factTypes, roleMap, this.schema);
217
- if (!sqlQuery) {
218
- throw new Error(`Could not generate SQL for query "${query.toDescriptiveString()}" starting at "${start.type}"`);
219
- }
220
- if (sqlQuery.empty) {
221
- return [];
222
- }
223
- const { rows } = await this.connectionFactory.with(async (connection) => {
224
- return await connection.query(sqlQuery.sql, sqlQuery.parameters);
225
- });
226
- return rows.map(row => loadFactPath(sqlQuery.pathLength, sqlQuery.factTypeNames, row));
227
- }
228
- catch (e) {
229
- throw new Error(`Could not generate SQL for query "${query.toDescriptiveString()}" starting at "${start.type}": ${e}`);
230
- }
231
- }
232
-
233
186
  async read(start: FactReference[], specification: Specification): Promise<ProjectedResult[]> {
234
187
  const factTypes = await this.loadFactTypesFromSpecification(specification);
235
188
  const roleMap = await this.loadRolesFromSpecification(specification, factTypes);
@@ -253,7 +206,8 @@ export class PostgresStore implements Storage {
253
206
  const factReferences: FactReference[] = composer.findFactReferences(resultSets);
254
207
 
255
208
  // Load the references into a fact tree
256
- const factRecords = await this.load(factReferences);
209
+ const factEnvelopes = await this.load(factReferences);
210
+ const factRecords = factEnvelopes.map(envelope => envelope.fact);
257
211
 
258
212
  return composer.compose(resultSets, factRecords);
259
213
  }
@@ -279,22 +233,6 @@ export class PostgresStore implements Storage {
279
233
  };
280
234
  }
281
235
 
282
- private async loadFactTypesFromSteps(steps: Step[], startType: string): Promise<FactTypeMap> {
283
- const factTypes = this.factTypeMap;
284
- const unknownFactTypes = [...allFactTypes(steps), startType]
285
- .filter(factType => !factTypes.has(factType))
286
- .filter(distinct);
287
- if (unknownFactTypes.length > 0) {
288
- const loadedFactTypes = await this.connectionFactory.with(async (connection) => {
289
- return await loadFactTypes(unknownFactTypes, connection, this.schema);
290
- });
291
- const merged = mergeFactTypes(this.factTypeMap, loadedFactTypes);
292
- this.factTypeMap = merged;
293
- return merged;
294
- }
295
- return factTypes;
296
- }
297
-
298
236
  async loadFactTypesFromFeed(feed: Specification): Promise<FactTypeMap> {
299
237
  const factTypes = this.factTypeMap;
300
238
  const unknownFactTypes = getAllFactTypes(feed)
@@ -325,21 +263,6 @@ export class PostgresStore implements Storage {
325
263
  return factTypes;
326
264
  }
327
265
 
328
- private async loadRolesFromSteps(steps: Step[], factTypes: FactTypeMap, initialType: string) {
329
- const roleMap = this.roleMap;
330
- const unknownRoles = allRoles(steps, factTypes, getFactTypeId(factTypes, initialType))
331
- .filter(r => !hasRole(roleMap, r.successor_type_id, r.role));
332
- if (unknownRoles.length > 0) {
333
- const loadedRoles = await this.connectionFactory.with(async (connection) => {
334
- return await loadRoles(unknownRoles, roleMap, connection, this.schema);
335
- });
336
- const merged = mergeRoleMaps(this.roleMap, loadedRoles);
337
- this.roleMap = merged;
338
- return merged;
339
- }
340
- return roleMap;
341
- }
342
-
343
266
  async loadRolesFromSpecification(specification: Specification, factTypes: FactTypeMap): Promise<RoleMap> {
344
267
  const roleMap = this.roleMap;
345
268
  const unknownRoles = getAllRoles(specification)
@@ -407,7 +330,7 @@ export class PostgresStore implements Storage {
407
330
  return existing;
408
331
  }
409
332
 
410
- async load(references: FactReference[]): Promise<FactRecord[]> {
333
+ async load(references: FactReference[]): Promise<FactEnvelope[]> {
411
334
  if (references.length === 0) {
412
335
  return [];
413
336
  }
@@ -419,23 +342,30 @@ export class PostgresStore implements Storage {
419
342
  const factParameters = flatten(references, (f) =>
420
343
  [f.hash, factTypes.get(f.type)]);
421
344
  const sql =
422
- 'SELECT f.fact_type_id, t.name, f.hash, f.data ' +
423
- `FROM ${this.schema}.fact f ` +
424
- `JOIN ${this.schema}.fact_type t ` +
425
- ' ON f.fact_type_id = t.fact_type_id ' +
426
- 'JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id) ' +
427
- ' ON v.fact_type_id = f.fact_type_id AND v.hash = f.hash ' +
428
- 'UNION ' +
429
- 'SELECT f2.fact_type_id, t.name, f2.hash, f2.data ' +
430
- `FROM ${this.schema}.fact f1 ` +
431
- 'JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id) ' +
432
- ' ON v.fact_type_id = f1.fact_type_id AND v.hash = f1.hash ' +
433
- `JOIN ${this.schema}.ancestor a ` +
434
- ' ON a.fact_id = f1.fact_id ' +
435
- `JOIN ${this.schema}.fact f2 ` +
436
- ' ON f2.fact_id = a.ancestor_fact_id ' +
437
- `JOIN ${this.schema}.fact_type t ` +
438
- ' ON t.fact_type_id = f2.fact_type_id;';
345
+ 'SELECT f.fact_type_id, f.name, f.hash, f.data, s.public_key, s.signature ' +
346
+ 'FROM (SELECT f.fact_id, f.fact_type_id, t.name, f.hash, f.data ' +
347
+ `FROM ${this.schema}.fact f ` +
348
+ `JOIN ${this.schema}.fact_type t ` +
349
+ ' ON f.fact_type_id = t.fact_type_id ' +
350
+ 'JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id) ' +
351
+ ' ON v.fact_type_id = f.fact_type_id AND v.hash = f.hash ' +
352
+ 'UNION ' +
353
+ 'SELECT f2.fact_id, f2.fact_type_id, t.name, f2.hash, f2.data ' +
354
+ `FROM ${this.schema}.fact f1 ` +
355
+ 'JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id) ' +
356
+ ' ON v.fact_type_id = f1.fact_type_id AND v.hash = f1.hash ' +
357
+ `JOIN ${this.schema}.ancestor a ` +
358
+ ' ON a.fact_id = f1.fact_id ' +
359
+ `JOIN ${this.schema}.fact f2 ` +
360
+ ' ON f2.fact_id = a.ancestor_fact_id ' +
361
+ `JOIN ${this.schema}.fact_type t ` +
362
+ ' ON t.fact_type_id = f2.fact_type_id) as f ' +
363
+ 'LEFT JOIN (SELECT p.public_key, s.signature, s.fact_id ' +
364
+ `FROM ${this.schema}.signature s ` +
365
+ `JOIN ${this.schema}.public_key p ` +
366
+ ' ON p.public_key_id = s.public_key_id) as s ' +
367
+ 'ON s.fact_id = f.fact_id ' +
368
+ 'ORDER BY f.fact_id, s.public_key;';
439
369
  const result: AncestorResult = await this.connectionFactory.with(async (connection) => {
440
370
  return await connection.query(sql, factParameters);
441
371
  })
@@ -444,17 +374,46 @@ export class PostgresStore implements Storage {
444
374
  emptyFactTypeMap()
445
375
  );
446
376
  this.factTypeMap = mergeFactTypes(this.factTypeMap, resultFactTypes);
447
- const sorter = new TopologicalSorter<FactRecord>();
448
377
  const records = result.rows.map((r) => {
449
378
  const { fields, predecessors }: { fields: {}, predecessors: PredecessorCollection } = r.data as any;
450
- return <FactRecord>{
379
+ const fact: FactRecord = {
451
380
  type: r.name,
452
381
  hash: r.hash,
453
382
  fields,
454
383
  predecessors
384
+ };
385
+ const signature: FactSignature = {
386
+ publicKey: r.public_key,
387
+ signature: r.signature
388
+ };
389
+ return {
390
+ fact,
391
+ signature
455
392
  }
456
393
  });
457
- return sorter.sort(records, (p, r) => r);
394
+ // Group the records by fact type and hash.
395
+ const envelopes: FactEnvelope[] = records.reduce((acc: FactEnvelope[], record) => {
396
+ // Because the records are sorted by fact ID, we can just look at the last envelope.
397
+ const last = acc.length > 0 ? acc[acc.length - 1] : undefined;
398
+ if (last && last.fact.type === record.fact.type && last.fact.hash === record.fact.hash) {
399
+ last.signatures.push(record.signature);
400
+ }
401
+ else if (record.signature.signature === null) {
402
+ acc.push({
403
+ fact: record.fact,
404
+ signatures: []
405
+ });
406
+ }
407
+ else {
408
+ acc.push({
409
+ fact: record.fact,
410
+ signatures: [record.signature]
411
+ });
412
+ }
413
+ return acc;
414
+ }, []);
415
+ // Assuming that the facts were inserted in topological order, they don't need to be sorted.
416
+ return envelopes;
458
417
  }
459
418
 
460
419
  private async loadFactTypesFromReferences(references: FactReference[]): Promise<FactTypeMap> {
@@ -894,51 +853,6 @@ async function insertSignatures(envelopes: FactEnvelope[], allFacts: FactMap, fa
894
853
  }
895
854
  }
896
855
 
897
- function allFactTypes(steps: Step[]): string[] {
898
- const factTypes = steps
899
- .filter(step => step instanceof PropertyCondition && step.name === 'type')
900
- .map(step => (step as PropertyCondition).value);
901
- const childFactTypes = steps
902
- .filter(step => step instanceof ExistentialCondition)
903
- .flatMap(step => allFactTypes((step as ExistentialCondition).steps));
904
- return [...factTypes, ...childFactTypes].filter(distinct);
905
- }
906
-
907
- function allRoles(steps: Step[], factTypes: FactTypeMap, initialTypeId: number | undefined) {
908
- let roles: { successor_type_id: number; role: string }[] = [];
909
- let successor_type_id = initialTypeId;
910
- let role: string | undefined = undefined;
911
-
912
- for (const step of steps) {
913
- if (step instanceof PropertyCondition) {
914
- if (step.name === 'type') {
915
- successor_type_id = getFactTypeId(factTypes, step.value);
916
- if (successor_type_id && role) {
917
- roles.push({ successor_type_id: successor_type_id, role });
918
- }
919
- role = undefined;
920
- }
921
- }
922
- else if (step instanceof Join) {
923
- if (step.direction === Direction.Predecessor) {
924
- if (successor_type_id) {
925
- roles.push({ successor_type_id: successor_type_id, role: step.role });
926
- }
927
- role = undefined;
928
- }
929
- else {
930
- role = step.role;
931
- }
932
- successor_type_id = undefined;
933
- }
934
- else if (step instanceof ExistentialCondition) {
935
- roles = roles.concat(allRoles(step.steps, factTypes, successor_type_id));
936
- }
937
- }
938
-
939
- return roles;
940
- }
941
-
942
856
  function predecessorsOf(fact: FactRecord): FactReference[] {
943
857
  const references = Object.values(fact.predecessors).flatMap(predecessor =>
944
858
  Array.isArray(predecessor) ? predecessor : [predecessor]
@@ -1,21 +1,59 @@
1
1
  import {
2
- AuthenticationNoOp,
3
2
  AuthorizationRules,
3
+ buildModel,
4
4
  dehydrateFact,
5
- ensure,
5
+ FactEnvelope,
6
6
  FactManager,
7
7
  FactRecord,
8
8
  hydrate,
9
- Jinaga as j,
10
9
  MemoryStore,
11
10
  NetworkNoOp,
12
11
  ObservableSource,
13
- PassThroughFork
12
+ PassThroughFork,
13
+ User
14
14
  } from "jinaga";
15
15
 
16
16
  import { AuthorizationKeystore } from "../../src/authorization/authorization-keystore";
17
17
  import { MemoryKeystore } from "../../src/memory/memory-keystore";
18
18
 
19
+ class Hashtag {
20
+ public static Type = "Hashtag" as const;
21
+ public type = Hashtag.Type;
22
+
23
+ constructor(
24
+ public word: string
25
+ ) { }
26
+ }
27
+
28
+ class Tweet {
29
+ public static Type = "Tweet" as const;
30
+ public type = Tweet.Type;
31
+
32
+ constructor(
33
+ public message: string,
34
+ public sender: User
35
+ ) { }
36
+ }
37
+
38
+ class Like {
39
+ public static Type = "Like" as const;
40
+ public type = Like.Type;
41
+
42
+ constructor(
43
+ public user: User,
44
+ public tweet: Tweet
45
+ ) { }
46
+ }
47
+
48
+ class Delete {
49
+ public static Type = "Delete" as const;
50
+ public type = Delete.Type;
51
+
52
+ constructor(
53
+ public tweet: Tweet
54
+ ) { }
55
+ }
56
+
19
57
  describe('Authorization', () => {
20
58
  it('should authorize empty save', async () => {
21
59
  const authorization = givenAuthorization();
@@ -25,19 +63,13 @@ describe('Authorization', () => {
25
63
 
26
64
  it('should save a new fact', async () => {
27
65
  const authorization = givenAuthorization();
28
- const result = await whenSave(authorization, dehydrateFact({
29
- type: 'Hashtag',
30
- word: 'vorpal'
31
- }));
66
+ const result = await whenSave(authorization, dehydrateFact(new Hashtag('vorpal')));
32
67
  expect(result.length).toEqual(1);
33
68
  });
34
69
 
35
70
  it('should save a fact once', async () => {
36
71
  const authorization = givenAuthorization();
37
- const facts = dehydrateFact({
38
- type: 'Hashtag',
39
- word: 'vorpal'
40
- });
72
+ const facts = dehydrateFact(new Hashtag('vorpal'));
41
73
  await whenSave(authorization, facts);
42
74
  const result = await whenSave(authorization, facts);
43
75
  expect(result.length).toEqual(0);
@@ -46,64 +78,36 @@ describe('Authorization', () => {
46
78
  it('should reject a fact from an unauthorized user', async () => {
47
79
  const authorization = givenAuthorization();
48
80
  const mickeyMouse = givenOtherUser();
49
- const promise = whenSave(authorization, dehydrateFact({
50
- type: 'Tweet',
51
- message: 'Twas brillig',
52
- sender: mickeyMouse
53
- }));
81
+ const promise = whenSave(authorization, dehydrateFact(new Tweet("Twas brillig", mickeyMouse)));
54
82
  await expect(promise).rejects.not.toBeNull();
55
83
  });
56
84
 
57
85
  it('should accept a fact from an authorized user', async () => {
58
86
  const authorization = givenAuthorization();
59
87
  const lewiscarrol = await givenLoggedInUser(authorization);
60
- const result = await whenSave(authorization, dehydrateFact({
61
- type: 'Tweet',
62
- message: 'Twas brillig',
63
- sender: lewiscarrol
64
- }));
88
+ const result = await whenSave(authorization, dehydrateFact(new Tweet("Twas brillig", lewiscarrol)));
65
89
  expect(result.length).toBeGreaterThan(0);
66
90
  });
67
91
 
68
92
  it('should accept a predecessor from an authorized user', async () => {
69
93
  const authorization = givenAuthorization();
70
94
  const lewiscarrol = await givenLoggedInUser(authorization);
71
- const result = await whenSave(authorization, dehydrateFact({
72
- type: 'Like',
73
- user: lewiscarrol,
74
- tweet: {
75
- type: 'Tweet',
76
- message: 'Twas Brillig',
77
- sender: lewiscarrol
78
- }
79
- }));
80
- expect(result.filter(r => r.type === 'Tweet').length).toEqual(1);
95
+ const result = await whenSave(authorization, dehydrateFact(new Like(lewiscarrol, new Tweet('Twas Brillig', lewiscarrol))));
96
+ expect(result.filter(r => r.fact.type === 'Tweet').length).toEqual(1);
81
97
  });
82
98
 
83
99
  it('should reject a predecessor from an unauthorized user', async () => {
84
100
  const authorization = givenAuthorization();
85
101
  const lewiscarrol = await givenLoggedInUser(authorization);
86
102
  const mickeyMouse = givenOtherUser();
87
- const promise = whenSave(authorization, dehydrateFact({
88
- type: 'Like',
89
- user: lewiscarrol,
90
- tweet: {
91
- type: 'Tweet',
92
- message: 'Hiya, Pal!',
93
- sender: mickeyMouse
94
- }
95
- }));
103
+ const promise = whenSave(authorization, dehydrateFact(new Like(lewiscarrol, new Tweet("Hiya, Pal!", mickeyMouse))));
96
104
  await expect(promise).rejects.not.toBeNull();
97
105
  });
98
106
 
99
107
  it('should accept a pre-existing predecessor from an unauthorized user', async () => {
100
108
  const storage = givenStorage();
101
109
  const mickeyMouse = givenOtherUser();
102
- const tweet = {
103
- type: 'Tweet',
104
- message: 'Hiya, Pal!',
105
- sender: mickeyMouse
106
- };
110
+ const tweet = new Tweet("Hiya, Pal!", mickeyMouse);
107
111
  await storage.save(dehydrateFact(tweet).map(f => ({ fact: f, signatures: [] })));
108
112
 
109
113
  const authorization = givenAuthorizationWithStorage(storage);
@@ -114,17 +118,13 @@ describe('Authorization', () => {
114
118
  tweet: tweet
115
119
  }));
116
120
  expect(result.length).toBeGreaterThan(0);
117
- expect(result.filter(r => r.type === 'Tweet').length).toEqual(0);
121
+ expect(result.filter(r => r.fact.type === 'Tweet').length).toEqual(0);
118
122
  });
119
123
 
120
124
  it('should accept a fact authorized by predecessor', async () => {
121
125
  const authorization = givenAuthorization();
122
126
  const lewiscarrol = await givenLoggedInUser(authorization);
123
- const tweet = {
124
- type: 'Tweet',
125
- message: 'Twas brillig',
126
- sender: lewiscarrol
127
- };
127
+ const tweet = new Tweet("Twas brillig", lewiscarrol);
128
128
  await whenSave(authorization, dehydrateFact(tweet));
129
129
 
130
130
  const result = await whenSave(authorization, dehydrateFact({
@@ -137,11 +137,7 @@ describe('Authorization', () => {
137
137
  it('should accept a fact based on in-flight predecessor', async () => {
138
138
  const authorization = givenAuthorization();
139
139
  const lewiscarrol = await givenLoggedInUser(authorization);
140
- const tweet = {
141
- type: 'Tweet',
142
- message: 'Twas brillig',
143
- sender: lewiscarrol
144
- };
140
+ const tweet = new Tweet("Twas brillig", lewiscarrol);
145
141
  // Note that this is missing.
146
142
  // await whenSave(authorization, dehydrateFact(tweet));
147
143
 
@@ -165,32 +161,39 @@ function givenStorage() {
165
161
  function givenAuthorizationWithStorage(storage: MemoryStore) {
166
162
  const keystore = new MemoryKeystore();
167
163
  keystore.getOrCreateUserFact(givenMockUserIdentity());
168
- const authentication = new AuthenticationNoOp();
169
164
  const fork = new PassThroughFork(storage);
170
165
  const observableSource = new ObservableSource(storage);
171
166
  const network = new NetworkNoOp();
172
- const factManager = new FactManager(authentication, fork, observableSource, storage, network);
173
- const authorizationRules = new AuthorizationRules(undefined)
174
- .any('Hashtag')
175
- .no('Jinaga.User')
176
- .type('Tweet', j.for(tweetSender))
177
- .type('Like', j.for(likeUser))
178
- .type('Delete', j.for(deleteSender))
167
+ const factManager = new FactManager(fork, observableSource, storage, network);
168
+ const model = buildModel(b => b
169
+ .type(Hashtag)
170
+ .type(User)
171
+ .type(Tweet, m => m
172
+ .predecessor("sender", User))
173
+ .type(Like, m => m
174
+ .predecessor("user", User)
175
+ .predecessor("tweet", Tweet))
176
+ .type(Delete, m => m
177
+ .predecessor("tweet", Tweet))
178
+ );
179
+ const authorizationRules = new AuthorizationRules(model)
180
+ .any(Hashtag)
181
+ .no(User)
182
+ .type(Tweet, t => t.sender)
183
+ .type(Like, l => l.user)
184
+ .type(Delete, d => d.tweet.sender)
179
185
  ;
180
186
  return new AuthorizationKeystore(factManager, storage, keystore, authorizationRules, null);
181
187
  }
182
188
 
183
189
  function givenOtherUser() {
184
- return {
185
- type: 'Jinaga.User',
186
- publicKey: 'other'
187
- };
190
+ return new User('other');
188
191
  }
189
192
 
190
193
  async function givenLoggedInUser(authorization: AuthorizationKeystore) {
191
194
  const userIdentity = givenMockUserIdentity();
192
195
  const userFact = await authorization.getOrCreateUserFact(userIdentity);
193
- const user = hydrate<{}>(userFact);
196
+ const user = hydrate<User>(userFact);
194
197
  return user;
195
198
  }
196
199
 
@@ -203,24 +206,7 @@ function givenMockUserIdentity() {
203
206
 
204
207
  async function whenSave(authorization: AuthorizationKeystore, facts: FactRecord[]) {
205
208
  const userIdentity = givenMockUserIdentity();
206
- const result = await authorization.save(userIdentity, facts);
209
+ const envelopes: FactEnvelope[] = facts.map(f => ({ fact: f, signatures: [] }));
210
+ const result = await authorization.save(userIdentity, envelopes);
207
211
  return result;
208
- }
209
-
210
- function tweetSender(t: { sender: {} }) {
211
- ensure(t).has("sender", "Jinaga.User");
212
-
213
- return j.match(t.sender);
214
- }
215
-
216
- function likeUser(l: { user: {} }) {
217
- ensure(l).has("user", "Jinaga.User");
218
-
219
- return j.match(l.user);
220
- }
221
-
222
- function deleteSender(d: { tweet: { sender: {} } }) {
223
- ensure(d).has("tweet", "Tweet").has("sender", "Jinaga.User");
224
-
225
- return j.match(d.tweet.sender);
226
212
  }
@@ -1,10 +0,0 @@
1
- import { FactReference, Step } from "jinaga";
2
- import { FactTypeMap, RoleMap } from "./maps";
3
- export declare type SqlQuery = {
4
- empty: boolean;
5
- sql: string;
6
- parameters: any[];
7
- pathLength: number;
8
- factTypeNames: string[];
9
- };
10
- export declare function sqlFromSteps(start: FactReference, steps: Step[], factTypes: FactTypeMap, roleMap: RoleMap, schema: string): SqlQuery | null;