@reventlessdev/reventless-core 3.0.0-alpha.266 → 3.0.0-alpha.267

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 (31) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +6 -6
  3. package/src/Projection.res +0 -10
  4. package/src/Projection.res.mjs +0 -14
  5. package/src/ProjectionMapper.res +8 -2
  6. package/src/ProjectionMapper.res.mjs +3 -3
  7. package/src/admin/Platform_Admin_Structure.res.mjs +1 -1
  8. package/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs +3 -0
  9. package/src/components/EventMapper/EventMapper_Builder.res +19 -1
  10. package/src/components/EventMapper/EventMapper_Builder.res.mjs +10 -1
  11. package/src/components/StateViewSlice/StateViewSlice_Builder.res +11 -1
  12. package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +2 -1
  13. package/src/components/StateViewSlice/StateViewSlice_Callback.res +8 -1
  14. package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +2 -1
  15. package/src/plugin/component/Plugin_Structure.res +131 -15
  16. package/src/plugin/component/Plugin_Structure.res.mjs +108 -24
  17. package/tests/commandtopic/CommandOutcomeHookTest.res.mjs +0 -1
  18. package/tests/dcb/DcbStateChangeSliceTest.res.mjs +0 -3
  19. package/tests/eventmapper/EventMapperFixtures.res +2 -2
  20. package/tests/eventmapper/EventMapperFixtures.res.mjs +14 -2
  21. package/tests/plugin/IdentityReferenceTest.res +99 -0
  22. package/tests/plugin/IdentityReferenceTest.res.mjs +169 -0
  23. package/tests/plugin/PluginStructureAccessTest.res.mjs +14 -1
  24. package/tests/plugin/PluginStructureTest.res.mjs +42 -10
  25. package/tests/plugin/StateView/PsAnnotatedView.res.mjs +3 -0
  26. package/tests/plugin/StateView/PsAvailableProductsView.res.mjs +3 -0
  27. package/tests/plugin/StateView/PsCategoriesView.res.mjs +3 -0
  28. package/tests/plugin/StateView/PsCustomersView.res.mjs +3 -0
  29. package/tests/plugin/StateView/PsGatedView.res.mjs +3 -0
  30. package/tests/plugin/StateView/PsOrdersView.res.mjs +3 -0
  31. package/tests/plugin/StateView/PsShipmentsView.res.mjs +3 -0
@@ -12,6 +12,7 @@ import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js
12
12
  import * as Trait$Reventless from "@reventlessdev/reventless-spec/src/types/Trait.res.mjs";
13
13
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
14
14
  import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
15
+ import * as Semantic$Reventless from "@reventlessdev/reventless-spec/src/semantic/Semantic.res.mjs";
15
16
  import * as Lifecycle$Reventless from "@reventlessdev/reventless-spec/src/types/Lifecycle.res.mjs";
16
17
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
17
18
  import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
@@ -444,20 +445,62 @@ function labelFieldsFromStateSchema(entityName, stateSchema) {
444
445
  };
445
446
  }
446
447
 
447
- function extractReferences(properties) {
448
- return Object.entries(properties).flatMap(param => Reference$Reventless.collectFieldTargets(param[0], param[1]).map(param => {
449
- let target = param[1];
450
- return {
451
- fieldName: param[0],
452
- entity: target.entity,
453
- plugin: target.plugin
454
- };
455
- }));
448
+ function identityReference(identityViews, fieldName, fieldSchema, declared) {
449
+ let key = Semantic$Reventless.fieldIdentityKey(fieldSchema);
450
+ if (key === undefined) {
451
+ return declared;
452
+ }
453
+ let keyed = Stdlib_Option.getOr(identityViews.viewsByKey[key], []);
454
+ if (declared.length !== 0) {
455
+ declared.forEach(param => {
456
+ let target = param[1];
457
+ if (Stdlib_Option.isNone(target.plugin) && identityViews.viewNames.includes(target.entity) && !keyed.includes(target.entity)) {
458
+ return identityViews.report(fieldName + ` is a ` + key + `, and its @ref names ` + target.entity + `, which is not keyed by it.`);
459
+ }
460
+ });
461
+ return declared;
462
+ }
463
+ let len = keyed.length;
464
+ if (len !== 1) {
465
+ if (len !== 0) {
466
+ identityViews.report(fieldName + ` is a ` + key + `, and ` + keyed.join(" and ") + ` are all keyed by it. Say which it references with @ref.`);
467
+ return [];
468
+ } else {
469
+ identityViews.report(fieldName + ` is a ` + key + `, and no view in this plugin is keyed by it, so it references nothing. List the identity in a view, or reference another plugin's with @ref("Plugin.View").`);
470
+ return [];
471
+ }
472
+ }
473
+ let view = keyed[0];
474
+ return [[
475
+ fieldName,
476
+ {
477
+ entity: view,
478
+ plugin: undefined,
479
+ identity: key
480
+ }
481
+ ]];
456
482
  }
457
483
 
458
- function toEventDef(v) {
484
+ function extractReferences(identityViews, properties) {
485
+ return Object.entries(properties).flatMap(param => {
486
+ let fieldSchema = param[1];
487
+ let fieldName = param[0];
488
+ let declared = Reference$Reventless.collectFieldTargets(fieldName, fieldSchema);
489
+ let targets = identityViews !== undefined ? identityReference(identityViews, fieldName, fieldSchema, declared) : declared;
490
+ return targets.map(param => {
491
+ let target = param[1];
492
+ return {
493
+ fieldName: param[0],
494
+ entity: target.entity,
495
+ plugin: target.plugin
496
+ };
497
+ });
498
+ });
499
+ }
500
+
501
+ function toEventDef(identityViews, v) {
459
502
  let mkDef = (variantName, properties) => {
460
- let references = extractReferences(properties);
503
+ let references = extractReferences(identityViews, properties);
461
504
  return {
462
505
  name: variantName,
463
506
  schema: JSON.stringify(SuryToJsonSchema$ReventlessCore.deriveObjectSchema(undefined, undefined, v)),
@@ -488,16 +531,16 @@ function toEventDef(v) {
488
531
  }
489
532
  }
490
533
 
491
- function extractEventDefs(eventSchema) {
534
+ function extractEventDefs(identityViews, eventSchema) {
492
535
  if (eventSchema.type === "anyOf") {
493
- return Stdlib_Array.filterMap(eventSchema.anyOf, toEventDef);
536
+ return Stdlib_Array.filterMap(eventSchema.anyOf, v => toEventDef(identityViews, v));
494
537
  } else {
495
- return Stdlib_Option.mapOr(toEventDef(eventSchema), [], def => [def]);
538
+ return Stdlib_Option.mapOr(toEventDef(identityViews, eventSchema), [], def => [def]);
496
539
  }
497
540
  }
498
541
 
499
542
  function extractErrorDefs(errorSchema) {
500
- return extractEventDefs(errorSchema).map(param => ({
543
+ return extractEventDefs(undefined, errorSchema).map(param => ({
501
544
  name: param.name,
502
545
  schema: param.schema,
503
546
  references: param.references
@@ -597,10 +640,10 @@ function annotateArgTypes(schema, argTypes) {
597
640
  return schema;
598
641
  }
599
642
 
600
- function toCommandDef(isAggregate, partitionKey, mutationFieldFor, parentSchema, commandAuthorization, commandTransition, derivedEdgeFor, v) {
643
+ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor, parentSchema, commandAuthorization, commandTransition, derivedEdgeFor, v) {
601
644
  let mkDef = (variantName, properties) => {
602
645
  let match = commandLevelAndId(isAggregate, partitionKey, variantName, properties);
603
- let references = extractReferences(properties);
646
+ let references = extractReferences(identityViews, properties);
604
647
  let syntheticCommand = DcbTag$Reventless.isVariantPayloadBearing(parentSchema, variantName) ? ({
605
648
  TAG: variantName
606
649
  }) : variantName;
@@ -683,12 +726,12 @@ function toCommandDef(isAggregate, partitionKey, mutationFieldFor, parentSchema,
683
726
  }
684
727
  }
685
728
 
686
- function extractCommandDefs(isAggregate, partitionKey, mutationFieldFor, commandAuthorization, commandTransition, derivedEdgeForOpt, commandSchema) {
729
+ function extractCommandDefs(identityViews, isAggregate, partitionKey, mutationFieldFor, commandAuthorization, commandTransition, derivedEdgeForOpt, commandSchema) {
687
730
  let derivedEdgeFor = derivedEdgeForOpt !== undefined ? derivedEdgeForOpt : param => {};
688
731
  if (commandSchema.type === "anyOf") {
689
- return Stdlib_Array.filterMap(commandSchema.anyOf, v => toCommandDef(isAggregate, partitionKey, mutationFieldFor, commandSchema, commandAuthorization, commandTransition, derivedEdgeFor, v));
732
+ return Stdlib_Array.filterMap(commandSchema.anyOf, v => toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor, commandSchema, commandAuthorization, commandTransition, derivedEdgeFor, v));
690
733
  } else {
691
- return Stdlib_Option.mapOr(toCommandDef(isAggregate, partitionKey, mutationFieldFor, commandSchema, commandAuthorization, commandTransition, derivedEdgeFor, commandSchema), [], def => [def]);
734
+ return Stdlib_Option.mapOr(toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor, commandSchema, commandAuthorization, commandTransition, derivedEdgeFor, commandSchema), [], def => [def]);
692
735
  }
693
736
  }
694
737
 
@@ -1101,6 +1144,46 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
1101
1144
  };
1102
1145
  }
1103
1146
  let partitionFailures = [];
1147
+ let keyIdentityOf = (stateSchema, idField) => Stdlib_Option.map(idField, field => {
1148
+ let tmp;
1149
+ tmp = stateSchema.type === "object" ? Stdlib_Option.flatMap(stateSchema.properties[field], Semantic$Reventless.identityKey) : undefined;
1150
+ return Stdlib_Option.getOr(tmp, field);
1151
+ });
1152
+ let viewKeys = readModels.map((R, i) => {
1153
+ let def = readModelDefs[i];
1154
+ let declared = Semantic$Reventless.identityKey(R.Spec.Id.schema);
1155
+ let key = declared !== undefined ? declared : keyIdentityOf(R.Spec.stateSchema, def.idField);
1156
+ return [
1157
+ R.Spec.name,
1158
+ key
1159
+ ];
1160
+ }).concat(stateViewSlices.map((SVS, i) => {
1161
+ let def = stateViewDefs[i];
1162
+ return [
1163
+ SVS.Spec.name,
1164
+ keyIdentityOf(SVS.Spec.stateSchema, def.idField)
1165
+ ];
1166
+ }));
1167
+ let viewsByKey = {};
1168
+ viewKeys.forEach(param => {
1169
+ let view = param[0];
1170
+ Stdlib_Option.forEach(param[1], k => {
1171
+ viewsByKey[k] = Stdlib_Option.getOr(viewsByKey[k], []).concat([view]);
1172
+ });
1173
+ });
1174
+ let reported = new Set();
1175
+ let identityViews_viewNames = viewKeys.map(param => param[0]);
1176
+ let identityViews_report = message => {
1177
+ if (!reported.has(message)) {
1178
+ reported.add(message);
1179
+ return log.warn("Plugin_Structure", undefined, name + `: ` + message);
1180
+ }
1181
+ };
1182
+ let identityViews = {
1183
+ viewsByKey: viewsByKey,
1184
+ viewNames: identityViews_viewNames,
1185
+ report: identityViews_report
1186
+ };
1104
1187
  let stateChangeDefs = stateChangeSlices.map((SCS, i) => {
1105
1188
  let match = scsProduced[i];
1106
1189
  let produced = match[1];
@@ -1108,12 +1191,12 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
1108
1191
  let consumed = match$1[1];
1109
1192
  return {
1110
1193
  name: SCS.Spec.name,
1111
- commands: extractCommandDefs(false, partitionBySlice[SCS.Spec.name], variantName => Api_Naming$ReventlessCore.sliceMutationFieldFor(name, SCS.Spec.name, SCS.Spec.commandSchema, variantName), SCS.Spec.commandAuthorization, SCS.Spec.commandTransition, extra => derivedEdgeFor(SCS.Spec.name, extra), SCS.Spec.commandSchema),
1194
+ commands: extractCommandDefs(identityViews, false, partitionBySlice[SCS.Spec.name], variantName => Api_Naming$ReventlessCore.sliceMutationFieldFor(name, SCS.Spec.name, SCS.Spec.commandSchema, variantName), SCS.Spec.commandAuthorization, SCS.Spec.commandTransition, extra => derivedEdgeFor(SCS.Spec.name, extra), SCS.Spec.commandSchema),
1112
1195
  producedEventTypes: produced,
1113
1196
  consumedEventTypes: consumed,
1114
1197
  linkedViews: linkedSvsFor(produced),
1115
1198
  consistencyRead: consistencyReadFor(consumed),
1116
- events: extractEventDefs(SCS.Spec.eventSchema),
1199
+ events: extractEventDefs(identityViews, SCS.Spec.eventSchema),
1117
1200
  errors: extractErrorDefs(SCS.Spec.errorSchema),
1118
1201
  chapter: componentChapters[SCS.Spec.name]
1119
1202
  };
@@ -1123,12 +1206,12 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
1123
1206
  let produced = match[1];
1124
1207
  return {
1125
1208
  name: A.Spec.name,
1126
- commands: extractCommandDefs(true, undefined, variantName => Api_Naming$ReventlessCore.aggregateMutationField(name, A.Spec.name, variantName), A.Spec.commandAuthorization, A.Spec.commandTransition, extra => derivedEdgeFor(A.Spec.name, extra), A.Spec.commandSchema),
1209
+ commands: extractCommandDefs(identityViews, true, undefined, variantName => Api_Naming$ReventlessCore.aggregateMutationField(name, A.Spec.name, variantName), A.Spec.commandAuthorization, A.Spec.commandTransition, extra => derivedEdgeFor(A.Spec.name, extra), A.Spec.commandSchema),
1127
1210
  producedEventTypes: produced,
1128
1211
  consumedEventTypes: [],
1129
1212
  linkedViews: linkedSvsFor(produced).concat(linkedReadModelsFor(A.Spec.name)),
1130
1213
  consistencyRead: undefined,
1131
- events: extractEventDefs(A.Spec.eventSchema),
1214
+ events: extractEventDefs(identityViews, A.Spec.eventSchema),
1132
1215
  errors: extractErrorDefs(A.Spec.errorSchema),
1133
1216
  chapter: componentChapters[A.Spec.name]
1134
1217
  };
@@ -1452,6 +1535,7 @@ export {
1452
1535
  checkLifecycleTopology,
1453
1536
  labelFieldSourceToString,
1454
1537
  labelFieldsFromStateSchema,
1538
+ identityReference,
1455
1539
  extractReferences,
1456
1540
  toEventDef,
1457
1541
  extractEventDefs,
@@ -59,7 +59,6 @@ let SliceBehavior = {
59
59
  let SliceHandler = StateChangeSlice_Callback$ReventlessCore.Make({
60
60
  name: DcbFixtures$ReventlessCore.TestCommandSpec.name,
61
61
  moduleUrl: DcbFixtures$ReventlessCore.TestCommandSpec.moduleUrl,
62
- Id: Id$Reventless.$$String,
63
62
  consumedEventSchema: DcbFixtures$ReventlessCore.TestCommandSpec.consumedEventSchema,
64
63
  errorSchema: DcbFixtures$ReventlessCore.TestCommandSpec.errorSchema,
65
64
  eventSchema: DcbFixtures$ReventlessCore.TestCommandSpec.eventSchema,
@@ -55,7 +55,6 @@ let TestCommandBehavior = {
55
55
  let TestHandler = StateChangeSlice_Callback$ReventlessCore.Make({
56
56
  name: DcbFixtures$ReventlessCore.TestCommandSpec.name,
57
57
  moduleUrl: DcbFixtures$ReventlessCore.TestCommandSpec.moduleUrl,
58
- Id: Id$Reventless.$$String,
59
58
  consumedEventSchema: DcbFixtures$ReventlessCore.TestCommandSpec.consumedEventSchema,
60
59
  errorSchema: DcbFixtures$ReventlessCore.TestCommandSpec.errorSchema,
61
60
  eventSchema: DcbFixtures$ReventlessCore.TestCommandSpec.eventSchema,
@@ -155,7 +154,6 @@ let EventualSpec = {
155
154
  let StrongHandler = StateChangeSlice_Callback$ReventlessCore.Make({
156
155
  name: name,
157
156
  moduleUrl: moduleUrl$1,
158
- Id: Id$Reventless.$$String,
159
157
  consumedEventSchema: consumedEventSchema,
160
158
  errorSchema: errorSchema,
161
159
  eventSchema: eventSchema,
@@ -169,7 +167,6 @@ let StrongHandler = StateChangeSlice_Callback$ReventlessCore.Make({
169
167
  let EventualHandler = StateChangeSlice_Callback$ReventlessCore.Make({
170
168
  name: name$1,
171
169
  moduleUrl: moduleUrl$2,
172
- Id: Id$Reventless.$$String,
173
170
  consumedEventSchema: consumedEventSchema$1,
174
171
  errorSchema: errorSchema$1,
175
172
  eventSchema: eventSchema$1,
@@ -5,7 +5,7 @@ open Reventless.Projection
5
5
  // ─────────────────────────────────────────────────────────────
6
6
 
7
7
  module SourceSpec = {
8
- module Id = Reventless.Id.String
8
+ module Id = Reventless.Id.StringPure
9
9
  let name = "SourceAggregate"
10
10
 
11
11
  @schema
@@ -22,7 +22,7 @@ module SourceSpec = {
22
22
  // ─────────────────────────────────────────────────────────────
23
23
 
24
24
  module TargetSpec = {
25
- module Id = Reventless.Id.String
25
+ module Id = Reventless.Id.StringPure
26
26
  let name = "ItemCatalog"
27
27
 
28
28
  @schema
@@ -81,11 +81,23 @@ function project(msg) {
81
81
  }
82
82
 
83
83
  let ItemMapping = Projection$Reventless.Mapping.Make({
84
- Id: Id$Reventless.$$String,
84
+ Id: {
85
+ schema: Id$Reventless.StringPure.schema,
86
+ make: prim => prim,
87
+ makeFromString: prim => prim,
88
+ toString: prim => prim,
89
+ cmp: Id$Reventless.StringPure.cmp
90
+ },
85
91
  name: name,
86
92
  eventSchema: eventSchema
87
93
  })({
88
- Id: Id$Reventless.$$String,
94
+ Id: {
95
+ schema: Id$Reventless.StringPure.schema,
96
+ make: prim => prim,
97
+ makeFromString: prim => prim,
98
+ toString: prim => prim,
99
+ cmp: Id$Reventless.StringPure.cmp
100
+ },
89
101
  name: name$1,
90
102
  stateSchema: stateSchema,
91
103
  subIdConfig: undefined
@@ -0,0 +1,99 @@
1
+ // A typed id finds the view that lists it without an `@ref`, whenever exactly one
2
+ // view is keyed by its identity. Where the type cannot decide, it says so rather
3
+ // than guessing: which list a reference means is a product decision.
4
+
5
+ open JestGlobals
6
+
7
+ module ProductId = Reventless.Id.Make({
8
+ let key = "productId"
9
+ })
10
+ module CustomerId = Reventless.Id.Make({
11
+ let key = "customerId"
12
+ })
13
+
14
+ let refsOf = (~viewsByKey, schema: S.t<'a>) => {
15
+ let reports = []
16
+ let identityViews: Plugin_Structure.identityViews = {
17
+ viewsByKey: Dict.fromArray(viewsByKey),
18
+ viewNames: viewsByKey->Array.flatMap(((_, views)) => views),
19
+ report: message => reports->Array.push(message),
20
+ }
21
+ let properties = switch schema->S.castToUnknown {
22
+ | Object({properties}) => properties
23
+ | _ => Dict.make()
24
+ }
25
+ let refs =
26
+ Plugin_Structure.extractReferences(~identityViews, properties)->Array.map(r => (
27
+ r.fieldName,
28
+ r.entity,
29
+ ))
30
+ (refs, reports)
31
+ }
32
+
33
+ let order = S.schema(s =>
34
+ {
35
+ "buyer": s.matches(CustomerId.schema),
36
+ "productIds": s.matches(S.array(ProductId.schema)),
37
+ "note": s.matches(S.string),
38
+ }
39
+ )
40
+
41
+ describe("a reference derived from the type", () => {
42
+ testSync("exactly one view keyed by the identity is the reference", () => {
43
+ let (refs, reports) = refsOf(
44
+ ~viewsByKey=[("customerId", ["Customers"]), ("productId", ["AvailableProducts"])],
45
+ order,
46
+ )
47
+ expect(refs)->toEqual([("buyer", "Customers"), ("productIds", "AvailableProducts")])
48
+ expect(reports)->toEqual([])
49
+ })
50
+
51
+ testSync("several views keyed by it ask for @ref instead of guessing", () => {
52
+ let (refs, reports) = refsOf(
53
+ ~viewsByKey=[("customerId", ["Customers"]), ("productId", ["Products", "ProductDemand"])],
54
+ order,
55
+ )
56
+ expect(refs)->toEqual([("buyer", "Customers")])
57
+ expect(reports->Array.some(r => r->String.includes("Products and ProductDemand")))->toBe(true)
58
+ })
59
+
60
+ testSync("no view keyed by it is reported", () => {
61
+ let (refs, reports) = refsOf(~viewsByKey=[("productId", ["AvailableProducts"])], order)
62
+ expect(refs)->toEqual([("productIds", "AvailableProducts")])
63
+ expect(reports->Array.some(r => r->String.includes("no view in this plugin")))->toBe(true)
64
+ })
65
+
66
+ testSync("an untyped field is left to its name, as before", () => {
67
+ let (refs, reports) = refsOf(
68
+ ~viewsByKey=[("customerId", ["Customers"])],
69
+ S.schema(s => {"customerId": s.matches(S.string)}),
70
+ )
71
+ expect((refs, reports))->toEqual(([], []))
72
+ })
73
+ })
74
+
75
+ describe("a declared @ref on a typed id", () => {
76
+ let referenced = S.schema(s =>
77
+ {"buyer": s.matches(CustomerId.schema->Reventless.Reference.mark("Products"))}
78
+ )
79
+
80
+ testSync("is kept, and checked against the views keyed by the identity", () => {
81
+ let (refs, reports) = refsOf(
82
+ ~viewsByKey=[("customerId", ["Customers"]), ("productId", ["Products"])],
83
+ referenced,
84
+ )
85
+ expect(refs)->toEqual([("buyer", "Products")])
86
+ expect(reports->Array.some(r => r->String.includes("not keyed by it")))->toBe(true)
87
+ })
88
+
89
+ testSync("naming a view keyed by the identity is fine", () => {
90
+ let chosen = S.schema(
91
+ s => {"buyer": s.matches(CustomerId.schema->Reventless.Reference.mark("Customers"))},
92
+ )
93
+ let (refs, reports) = refsOf(
94
+ ~viewsByKey=[("customerId", ["Customers", "VipCustomers"])],
95
+ chosen,
96
+ )
97
+ expect((refs, reports))->toEqual(([("buyer", "Customers")], []))
98
+ })
99
+ })
@@ -0,0 +1,169 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Sury from "sury";
4
+ import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
5
+ import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
6
+ import * as Plugin_Structure$ReventlessCore from "../../src/plugin/component/Plugin_Structure.res.mjs";
7
+
8
+ let ProductId = Id$Reventless.Make({
9
+ key: "productId"
10
+ });
11
+
12
+ let CustomerId = Id$Reventless.Make({
13
+ key: "customerId"
14
+ });
15
+
16
+ function refsOf(viewsByKey, schema) {
17
+ let reports = [];
18
+ let identityViews_viewsByKey = Object.fromEntries(viewsByKey);
19
+ let identityViews_viewNames = viewsByKey.flatMap(param => param[1]);
20
+ let identityViews_report = message => {
21
+ reports.push(message);
22
+ };
23
+ let identityViews = {
24
+ viewsByKey: identityViews_viewsByKey,
25
+ viewNames: identityViews_viewNames,
26
+ report: identityViews_report
27
+ };
28
+ let properties;
29
+ properties = schema.type === "object" ? schema.properties : ({});
30
+ let refs = Plugin_Structure$ReventlessCore.extractReferences(identityViews, properties).map(r => [
31
+ r.fieldName,
32
+ r.entity
33
+ ]);
34
+ return [
35
+ refs,
36
+ reports
37
+ ];
38
+ }
39
+
40
+ let order = Sury.$schema(s => ({
41
+ buyer: s.m(CustomerId.schema),
42
+ productIds: s.m(Sury.array(ProductId.schema)),
43
+ note: s.m(Sury.string)
44
+ }));
45
+
46
+ globalThis.describe("a reference derived from the type", () => {
47
+ globalThis.test("exactly one view keyed by the identity is the reference", () => {
48
+ let match = refsOf([
49
+ [
50
+ "customerId",
51
+ ["Customers"]
52
+ ],
53
+ [
54
+ "productId",
55
+ ["AvailableProducts"]
56
+ ]
57
+ ], order);
58
+ globalThis.expect(match[0]).toEqual([
59
+ [
60
+ "buyer",
61
+ "Customers"
62
+ ],
63
+ [
64
+ "productIds",
65
+ "AvailableProducts"
66
+ ]
67
+ ]);
68
+ globalThis.expect(match[1]).toEqual([]);
69
+ });
70
+ globalThis.test("several views keyed by it ask for @ref instead of guessing", () => {
71
+ let match = refsOf([
72
+ [
73
+ "customerId",
74
+ ["Customers"]
75
+ ],
76
+ [
77
+ "productId",
78
+ [
79
+ "Products",
80
+ "ProductDemand"
81
+ ]
82
+ ]
83
+ ], order);
84
+ globalThis.expect(match[0]).toEqual([[
85
+ "buyer",
86
+ "Customers"
87
+ ]]);
88
+ globalThis.expect(match[1].some(r => r.includes("Products and ProductDemand"))).toBe(true);
89
+ });
90
+ globalThis.test("no view keyed by it is reported", () => {
91
+ let match = refsOf([[
92
+ "productId",
93
+ ["AvailableProducts"]
94
+ ]], order);
95
+ globalThis.expect(match[0]).toEqual([[
96
+ "productIds",
97
+ "AvailableProducts"
98
+ ]]);
99
+ globalThis.expect(match[1].some(r => r.includes("no view in this plugin"))).toBe(true);
100
+ });
101
+ globalThis.test("an untyped field is left to its name, as before", () => {
102
+ let match = refsOf([[
103
+ "customerId",
104
+ ["Customers"]
105
+ ]], Sury.$schema(s => ({
106
+ customerId: s.m(Sury.string)
107
+ })));
108
+ globalThis.expect([
109
+ match[0],
110
+ match[1]
111
+ ]).toEqual([
112
+ [],
113
+ []
114
+ ]);
115
+ });
116
+ });
117
+
118
+ globalThis.describe("a declared @ref on a typed id", () => {
119
+ let referenced = Sury.$schema(s => ({
120
+ buyer: s.m(Reference$Reventless.mark(CustomerId.schema, undefined, undefined, "Products"))
121
+ }));
122
+ globalThis.test("is kept, and checked against the views keyed by the identity", () => {
123
+ let match = refsOf([
124
+ [
125
+ "customerId",
126
+ ["Customers"]
127
+ ],
128
+ [
129
+ "productId",
130
+ ["Products"]
131
+ ]
132
+ ], referenced);
133
+ globalThis.expect(match[0]).toEqual([[
134
+ "buyer",
135
+ "Products"
136
+ ]]);
137
+ globalThis.expect(match[1].some(r => r.includes("not keyed by it"))).toBe(true);
138
+ });
139
+ globalThis.test("naming a view keyed by the identity is fine", () => {
140
+ let chosen = Sury.$schema(s => ({
141
+ buyer: s.m(Reference$Reventless.mark(CustomerId.schema, undefined, undefined, "Customers"))
142
+ }));
143
+ let match = refsOf([[
144
+ "customerId",
145
+ [
146
+ "Customers",
147
+ "VipCustomers"
148
+ ]
149
+ ]], chosen);
150
+ globalThis.expect([
151
+ match[0],
152
+ match[1]
153
+ ]).toEqual([
154
+ [[
155
+ "buyer",
156
+ "Customers"
157
+ ]],
158
+ []
159
+ ]);
160
+ });
161
+ });
162
+
163
+ export {
164
+ ProductId,
165
+ CustomerId,
166
+ refsOf,
167
+ order,
168
+ }
169
+ /* ProductId Not a pure module */
@@ -22,7 +22,6 @@ function make(param, param$1, param$2, param$3, param$4, param$5, param$6) {
22
22
  let PsGatedCommandsSlice_Spec = {
23
23
  name: PsGatedCommands$ReventlessCore.name,
24
24
  moduleUrl: PsGatedCommands$ReventlessCore.moduleUrl,
25
- Id: Id$Reventless.$$String,
26
25
  consumedEventSchema: PsGatedCommands$ReventlessCore.consumedEventSchema,
27
26
  errorSchema: PsGatedCommands$ReventlessCore.errorSchema,
28
27
  eventSchema: PsGatedCommands$ReventlessCore.eventSchema,
@@ -53,6 +52,13 @@ let PsGatedViewSlice_Spec = {
53
52
  name: PsGatedView$ReventlessCore.name,
54
53
  moduleUrl: PsGatedView$ReventlessCore.moduleUrl,
55
54
  stateSchema: PsGatedView$ReventlessCore.stateSchema,
55
+ Key: {
56
+ schema: Id$Reventless.StringPure.schema,
57
+ make: prim => prim,
58
+ makeFromString: prim => prim,
59
+ toString: prim => prim,
60
+ cmp: Id$Reventless.StringPure.cmp
61
+ },
56
62
  consumedEventSchema: PsGatedView$ReventlessCore.consumedEventSchema,
57
63
  config: PsGatedView$ReventlessCore.config,
58
64
  subIdConfig: undefined,
@@ -79,6 +85,13 @@ let PsOrdersViewSlice_Spec = {
79
85
  name: PsOrdersView$ReventlessCore.name,
80
86
  moduleUrl: PsOrdersView$ReventlessCore.moduleUrl,
81
87
  stateSchema: PsOrdersView$ReventlessCore.stateSchema,
88
+ Key: {
89
+ schema: Id$Reventless.StringPure.schema,
90
+ make: prim => prim,
91
+ makeFromString: prim => prim,
92
+ toString: prim => prim,
93
+ cmp: Id$Reventless.StringPure.cmp
94
+ },
82
95
  consumedEventSchema: PsOrdersView$ReventlessCore.consumedEventSchema,
83
96
  config: PsOrdersView$ReventlessCore.config,
84
97
  subIdConfig: undefined,