@reventlessdev/reventless-core 3.0.0-alpha.236 → 3.0.0-alpha.238
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/CHANGELOG.md +23 -0
- package/package.json +6 -6
- package/src/Message.res +1 -1
- package/src/adapter/Monitoring/Monitoring.res +1 -1
- package/src/admin/Platform_Admin_Structure.res +5 -1
- package/src/admin/Platform_Admin_Structure.res.mjs +4 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res +4 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +11 -3
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res +1 -1
- package/src/components/Aggregate/Aggregate_Callback.res +2 -2
- package/src/components/Api/ApiAllowedStatesHelpers.res +2 -3
- package/src/components/Api/ApiTargetStateHelpers.res +4 -3
- package/src/components/Api/GraphQL_FragmentGenerator.res +14 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +1 -1
- package/src/components/Api/QueryDbListQuery.res +25 -1
- package/src/components/Api/QueryDbListQuery.res.mjs +11 -4
- package/src/components/Api/SuryToJsonSchema.res +39 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +34 -3
- package/src/components/Dcb/Dcb_Builder.res +79 -16
- package/src/components/Dcb/Dcb_Builder.res.mjs +41 -5
- package/src/components/EventLog/EventLog.res +1 -1
- package/src/plugin/component/Plugin_Structure.res +377 -18
- package/src/plugin/component/Plugin_Structure.res.mjs +204 -9
- package/src/plugin/connect/PluginExtensionPoint_UiFragment.res +1 -1
- package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +3 -2
- package/tests/admin/Platform_Admin_StructureTest.res +49 -0
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +24 -0
- package/tests/admin/Platform_BakedManifestTest.res +3 -1
- package/tests/admin/Platform_BakedManifestTest.res.mjs +3 -1
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +14 -8
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +24 -12
- package/tests/admin/Platform_PluginStructuresApiTest.res +3 -1
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +6 -2
- package/tests/aggregate/AggregateCacheTest.res +1 -1
- package/tests/aggregate/AggregateSnapshotTest.res +1 -1
- package/tests/api/Api_IdsTest.res.mjs +1 -1
- package/tests/api/SuryToJsonSchemaTest.res +141 -1
- package/tests/api/SuryToJsonSchemaTest.res.mjs +269 -54
- package/tests/commandgenerator/OwnerStampingTest.res +57 -0
- package/tests/commandgenerator/OwnerStampingTest.res.mjs +19 -0
- package/tests/message/MessageTest.res +1 -1
- package/tests/plugin/HeartbeatDisconnectGraceTest.res +1 -1
- package/tests/plugin/PluginStructureTest.res +332 -15
- package/tests/plugin/PluginStructureTest.res.mjs +316 -10
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res +31 -0
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res.mjs +73 -0
- package/tests/plugin/StateChangeSlice/PsShipOrder.res +16 -1
- package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +7 -2
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +3 -2
- package/tests/plugin/StateViewSlice/PsShipmentsView.res +26 -0
- package/tests/plugin/StateViewSlice/PsShipmentsView.res.mjs +101 -0
- package/tests/plugin/pluginDefinitionRequiredScalars.txt +2 -0
- package/tests/util/CsvStreamTest.res +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stdlib_Exn from "@rescript/runtime/lib/es6/Stdlib_Exn.js";
|
|
4
5
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
6
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
7
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
@@ -8,12 +9,14 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
8
9
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
9
10
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
10
11
|
import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
|
|
12
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
11
13
|
import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
|
|
12
14
|
import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
|
|
13
15
|
import * as Api_Naming$ReventlessCore from "../../src/components/Api/Api_Naming.res.mjs";
|
|
14
16
|
import * as PsShipOrder$ReventlessCore from "./StateChangeSlice/PsShipOrder.res.mjs";
|
|
15
17
|
import * as PsOrdersView$ReventlessCore from "./StateViewSlice/PsOrdersView.res.mjs";
|
|
16
18
|
import * as PsPlaceOrder$ReventlessCore from "./StateChangeSlice/PsPlaceOrder.res.mjs";
|
|
19
|
+
import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
|
|
17
20
|
import * as PsChangePhoto$ReventlessCore from "./StateChangeSlice/PsChangePhoto.res.mjs";
|
|
18
21
|
import * as CapabilityManifest$Reventless from "@reventlessdev/reventless-spec/src/components/CapabilityManifest.res.mjs";
|
|
19
22
|
import * as PsReserveStock$ReventlessCore from "./StateChangeSlice/PsReserveStock.res.mjs";
|
|
@@ -21,8 +24,10 @@ import * as PsUploadAvatar$ReventlessCore from "./StateChangeSlice/PsUploadAvata
|
|
|
21
24
|
import * as PsAnnotatedView$ReventlessCore from "./StateViewSlice/PsAnnotatedView.res.mjs";
|
|
22
25
|
import * as PsAttachInvoice$ReventlessCore from "./StateChangeSlice/PsAttachInvoice.res.mjs";
|
|
23
26
|
import * as PsCustomersView$ReventlessCore from "./StateViewSlice/PsCustomersView.res.mjs";
|
|
27
|
+
import * as PsShipmentsView$ReventlessCore from "./StateViewSlice/PsShipmentsView.res.mjs";
|
|
24
28
|
import * as Plugin_Structure$ReventlessCore from "../../src/plugin/component/Plugin_Structure.res.mjs";
|
|
25
29
|
import * as PsCategoriesView$ReventlessCore from "./StateViewSlice/PsCategoriesView.res.mjs";
|
|
30
|
+
import * as PsDispatchShipment$ReventlessCore from "./StateChangeSlice/PsDispatchShipment.res.mjs";
|
|
26
31
|
import * as Capability_Inference$ReventlessCore from "../../src/plugin/component/Capability_Inference.res.mjs";
|
|
27
32
|
import * as PsAvailableProductsView$ReventlessCore from "./StateViewSlice/PsAvailableProductsView.res.mjs";
|
|
28
33
|
|
|
@@ -336,6 +341,62 @@ let PsReserveStockSlice = {
|
|
|
336
341
|
make: make$10
|
|
337
342
|
};
|
|
338
343
|
|
|
344
|
+
let Behavior$6 = {
|
|
345
|
+
initialState: false,
|
|
346
|
+
evolve: PsDispatchShipment$ReventlessCore.evolve,
|
|
347
|
+
decide: PsDispatchShipment$ReventlessCore.decide,
|
|
348
|
+
moduleUrl: PsDispatchShipment$ReventlessCore.moduleUrl
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
function make$11(param, param$1, param$2, param$3, param$4, param$5) {
|
|
352
|
+
return 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let PsDispatchShipmentSlice_Spec = {
|
|
356
|
+
name: PsDispatchShipment$ReventlessCore.name,
|
|
357
|
+
moduleUrl: PsDispatchShipment$ReventlessCore.moduleUrl,
|
|
358
|
+
Id: Id$Reventless.$$String,
|
|
359
|
+
consumedEventSchema: PsDispatchShipment$ReventlessCore.consumedEventSchema,
|
|
360
|
+
errorSchema: PsDispatchShipment$ReventlessCore.errorSchema,
|
|
361
|
+
eventSchema: PsDispatchShipment$ReventlessCore.eventSchema,
|
|
362
|
+
commandSchema: PsDispatchShipment$ReventlessCore.commandSchema,
|
|
363
|
+
commandAuthorization: PsDispatchShipment$ReventlessCore.commandAuthorization,
|
|
364
|
+
readConsistency: PsDispatchShipment$ReventlessCore.readConsistency
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
let PsDispatchShipmentSlice = {
|
|
368
|
+
Spec: PsDispatchShipmentSlice_Spec,
|
|
369
|
+
Behavior: Behavior$6,
|
|
370
|
+
isAsync: false,
|
|
371
|
+
make: make$11
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
let Projection$5 = {
|
|
375
|
+
project: PsShipmentsView$ReventlessCore.project,
|
|
376
|
+
moduleUrl: PsShipmentsView$ReventlessCore.moduleUrl
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
function make$12(param, param$1, param$2) {
|
|
380
|
+
return 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let PsShipmentsViewSlice_Spec = {
|
|
384
|
+
name: PsShipmentsView$ReventlessCore.name,
|
|
385
|
+
moduleUrl: PsShipmentsView$ReventlessCore.moduleUrl,
|
|
386
|
+
stateSchema: PsShipmentsView$ReventlessCore.stateSchema,
|
|
387
|
+
consumedEventSchema: PsShipmentsView$ReventlessCore.consumedEventSchema,
|
|
388
|
+
config: PsShipmentsView$ReventlessCore.config,
|
|
389
|
+
subIdConfig: undefined,
|
|
390
|
+
authorization: PsShipmentsView$ReventlessCore.authorization,
|
|
391
|
+
visibility: PsShipmentsView$ReventlessCore.visibility
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
let PsShipmentsViewSlice = {
|
|
395
|
+
Spec: PsShipmentsViewSlice_Spec,
|
|
396
|
+
Projection: Projection$5,
|
|
397
|
+
make: make$12
|
|
398
|
+
};
|
|
399
|
+
|
|
339
400
|
let structure = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [
|
|
340
401
|
PsOrdersViewSlice,
|
|
341
402
|
PsAvailableProductsViewSlice,
|
|
@@ -346,6 +407,102 @@ let structure = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, un
|
|
|
346
407
|
PsShipOrderSlice
|
|
347
408
|
], undefined, undefined, undefined, undefined, undefined, undefined);
|
|
348
409
|
|
|
410
|
+
globalThis.describe("lifecycle topology", () => {
|
|
411
|
+
let ordersStates = Object.fromEntries([[
|
|
412
|
+
"Orders",
|
|
413
|
+
[
|
|
414
|
+
"Placed",
|
|
415
|
+
"Shipped",
|
|
416
|
+
"Cancelled"
|
|
417
|
+
]
|
|
418
|
+
]]);
|
|
419
|
+
let writableWith = commands => ({
|
|
420
|
+
name: "Ordering",
|
|
421
|
+
commands: commands,
|
|
422
|
+
producedEventTypes: [],
|
|
423
|
+
consumedEventTypes: [],
|
|
424
|
+
linkedViews: ["Orders"],
|
|
425
|
+
consistencyRead: undefined,
|
|
426
|
+
events: [],
|
|
427
|
+
errors: [],
|
|
428
|
+
chapter: undefined
|
|
429
|
+
});
|
|
430
|
+
let command = (name, allowedStates, targetState, param) => ({
|
|
431
|
+
name: name,
|
|
432
|
+
schema: "",
|
|
433
|
+
level: "Instance",
|
|
434
|
+
aggregateIdField: undefined,
|
|
435
|
+
mutationField: "",
|
|
436
|
+
references: [],
|
|
437
|
+
allowedStates: allowedStates,
|
|
438
|
+
targetState: targetState,
|
|
439
|
+
apiExposed: undefined,
|
|
440
|
+
requiredAccess: undefined,
|
|
441
|
+
ownerField: undefined
|
|
442
|
+
});
|
|
443
|
+
globalThis.test("names a state no command can reach", () => {
|
|
444
|
+
let findings = Plugin_Structure$ReventlessCore.lifecycleTopologyFindings([writableWith([command("Ship", ["Placed"], "Shipped", undefined)])], ordersStates);
|
|
445
|
+
globalThis.expect(findings.map(param => param[1].includes("Cancelled"))).toEqual([true]);
|
|
446
|
+
});
|
|
447
|
+
globalThis.test("says nothing when every state is reachable", () => {
|
|
448
|
+
let findings = Plugin_Structure$ReventlessCore.lifecycleTopologyFindings([writableWith([
|
|
449
|
+
command("Ship", ["Placed"], "Shipped", undefined),
|
|
450
|
+
command("Cancel", ["Placed"], "Cancelled", undefined)
|
|
451
|
+
])], ordersStates);
|
|
452
|
+
globalThis.expect(findings.length).toBe(0);
|
|
453
|
+
});
|
|
454
|
+
globalThis.test("does not call the initial state unreachable", () => {
|
|
455
|
+
let findings = Plugin_Structure$ReventlessCore.lifecycleTopologyFindings([writableWith([
|
|
456
|
+
command("Ship", ["Placed"], "Shipped", undefined),
|
|
457
|
+
command("Cancel", ["Placed"], "Cancelled", undefined)
|
|
458
|
+
])], ordersStates);
|
|
459
|
+
globalThis.expect(findings.map(param => param[1].includes("Placed"))).toEqual([]);
|
|
460
|
+
});
|
|
461
|
+
globalThis.test("does not report a state with no way out", () => {
|
|
462
|
+
let findings = Plugin_Structure$ReventlessCore.lifecycleTopologyFindings([writableWith([
|
|
463
|
+
command("Ship", ["Placed"], "Shipped", undefined),
|
|
464
|
+
command("Cancel", ["Placed"], "Cancelled", undefined)
|
|
465
|
+
])], ordersStates);
|
|
466
|
+
globalThis.expect(findings.length).toBe(0);
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
globalThis.describe("@transition cross-check", () => {
|
|
471
|
+
let buildBad = () => {
|
|
472
|
+
try {
|
|
473
|
+
Plugin_Structure$ReventlessCore.make("TransitionPlugin", undefined, undefined, [PsShipmentsViewSlice], [PsDispatchShipmentSlice], undefined, undefined, undefined, undefined, undefined, undefined);
|
|
474
|
+
return;
|
|
475
|
+
} catch (raw_e) {
|
|
476
|
+
let e = Primitive_exceptions.internalToException(raw_e);
|
|
477
|
+
if (e.RE_EXN_ID === Stdlib_Exn.$$Error) {
|
|
478
|
+
return Stdlib_Option.getOr(e._1.message, "");
|
|
479
|
+
} else {
|
|
480
|
+
return "";
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
globalThis.test("a state no linked view declares fails the build", () => {
|
|
485
|
+
globalThis.expect(Stdlib_Option.isSome(buildBad())).toBe(true);
|
|
486
|
+
});
|
|
487
|
+
globalThis.test("the failure names the command, the state and the view", () => {
|
|
488
|
+
let message = Stdlib_Option.getOr(buildBad(), "");
|
|
489
|
+
globalThis.expect([
|
|
490
|
+
message.includes("DispatchShipment"),
|
|
491
|
+
message.includes("Dispatchd"),
|
|
492
|
+
message.includes("Shipments"),
|
|
493
|
+
message.includes("Booked")
|
|
494
|
+
]).toEqual([
|
|
495
|
+
true,
|
|
496
|
+
true,
|
|
497
|
+
true,
|
|
498
|
+
true
|
|
499
|
+
]);
|
|
500
|
+
});
|
|
501
|
+
globalThis.test("a plugin whose views declare no lifecycle still builds", () => {
|
|
502
|
+
globalThis.expect(structure.stateChangeSlices.length).toBe(2);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
349
506
|
globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
350
507
|
globalThis.describe("stateChangeSlices", () => {
|
|
351
508
|
globalThis.test("produces two SCS entries in declaration order", () => {
|
|
@@ -423,7 +580,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
423
580
|
false
|
|
424
581
|
]);
|
|
425
582
|
});
|
|
426
|
-
globalThis.test("ShipOrder: @
|
|
583
|
+
globalThis.test("ShipOrder: @transition's target flows through the PPX to commandDef.targetState", () => {
|
|
427
584
|
let shipOrder = structure.stateChangeSlices[1];
|
|
428
585
|
let byName = name => shipOrder.commands.find(c => c.name === name);
|
|
429
586
|
globalThis.expect([
|
|
@@ -434,6 +591,17 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
434
591
|
undefined
|
|
435
592
|
]);
|
|
436
593
|
});
|
|
594
|
+
globalThis.test("ShipOrder: @transition's from-set flows through to commandDef.allowedStates", () => {
|
|
595
|
+
let shipOrder = structure.stateChangeSlices[1];
|
|
596
|
+
let byName = name => shipOrder.commands.find(c => c.name === name);
|
|
597
|
+
globalThis.expect([
|
|
598
|
+
Stdlib_Option.flatMap(byName("ShipOrder"), c => c.allowedStates),
|
|
599
|
+
Stdlib_Option.flatMap(byName("CancelShipment"), c => c.allowedStates)
|
|
600
|
+
]).toEqual([
|
|
601
|
+
["Placed"],
|
|
602
|
+
undefined
|
|
603
|
+
]);
|
|
604
|
+
});
|
|
437
605
|
globalThis.test("ShipOrder: the @noApi variant carries no callable mutation field (no sibling leak)", () => {
|
|
438
606
|
let shipOrder = structure.stateChangeSlices[1];
|
|
439
607
|
let byName = name => shipOrder.commands.find(c => c.name === name);
|
|
@@ -796,30 +964,166 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
796
964
|
]);
|
|
797
965
|
});
|
|
798
966
|
});
|
|
799
|
-
globalThis.describe("
|
|
800
|
-
|
|
967
|
+
globalThis.describe("lifecycleField — the name rule and the shape rule", () => {
|
|
968
|
+
let withLifecycle = (schema, field) => S.Metadata.set(schema, StateAnnotations$Reventless.stateAnnotationsId, {
|
|
969
|
+
ids: [],
|
|
970
|
+
compositeIds: [],
|
|
971
|
+
subIds: [],
|
|
972
|
+
compositeSubIds: [],
|
|
973
|
+
indexes: [],
|
|
974
|
+
hidden: [],
|
|
975
|
+
summary: [],
|
|
976
|
+
drillTargets: [],
|
|
977
|
+
drillTargetKeys: [],
|
|
978
|
+
collapsed: [],
|
|
979
|
+
scan: [],
|
|
980
|
+
scanSort: [],
|
|
981
|
+
semantic: [],
|
|
982
|
+
metric: [],
|
|
983
|
+
lifecycle: field,
|
|
984
|
+
groupBy: undefined,
|
|
985
|
+
visibility: undefined,
|
|
986
|
+
live: undefined,
|
|
987
|
+
retired: undefined
|
|
988
|
+
});
|
|
989
|
+
globalThis.test("a `lifecycle` field holding a closed set of values is the lifecycle field", () => {
|
|
801
990
|
let schema = S.schema(s => ({
|
|
802
|
-
|
|
991
|
+
lifecycle: s.m(S.union([
|
|
803
992
|
S.literal("Placed"),
|
|
804
993
|
S.literal("Shipped")
|
|
805
994
|
]))
|
|
806
995
|
}));
|
|
807
|
-
globalThis.expect(Plugin_Structure$ReventlessCore.
|
|
996
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.lifecycleFieldFromStateSchema("Test", schema)).toEqual("lifecycle");
|
|
808
997
|
});
|
|
809
998
|
globalThis.test("an optional one counts too", () => {
|
|
810
999
|
let schema = S.schema(s => ({
|
|
811
|
-
|
|
1000
|
+
lifecycle: s.m(S.option(S.union([
|
|
812
1001
|
S.literal("Placed"),
|
|
813
1002
|
S.literal("Shipped")
|
|
814
1003
|
])))
|
|
815
1004
|
}));
|
|
816
|
-
globalThis.expect(Plugin_Structure$ReventlessCore.
|
|
1005
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.lifecycleFieldFromStateSchema("Test", schema)).toEqual("lifecycle");
|
|
1006
|
+
});
|
|
1007
|
+
globalThis.test("free text named `lifecycle` is not a lifecycle", () => {
|
|
1008
|
+
let schema = S.schema(s => ({
|
|
1009
|
+
lifecycle: s.m(S.string)
|
|
1010
|
+
}));
|
|
1011
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.lifecycleFieldFromStateSchema("Test", schema)).toEqual(undefined);
|
|
1012
|
+
});
|
|
1013
|
+
globalThis.test("an unannotated field named `status` resolves to None", () => {
|
|
1014
|
+
let schema = S.schema(s => ({
|
|
1015
|
+
status: s.m(S.union([
|
|
1016
|
+
S.literal("Placed"),
|
|
1017
|
+
S.literal("Shipped")
|
|
1018
|
+
]))
|
|
1019
|
+
}));
|
|
1020
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.lifecycleFieldFromStateSchema("Test", schema)).toEqual(undefined);
|
|
1021
|
+
});
|
|
1022
|
+
globalThis.test("the annotation names a field the convention would never reach", () => {
|
|
1023
|
+
let schema = S.schema(s => ({
|
|
1024
|
+
locationStatus: s.m(S.union([
|
|
1025
|
+
S.literal("Pending"),
|
|
1026
|
+
S.literal("Located")
|
|
1027
|
+
]))
|
|
1028
|
+
}));
|
|
1029
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.lifecycleFieldFromStateSchema("Test", withLifecycle(schema, "locationStatus"))).toEqual("locationStatus");
|
|
1030
|
+
});
|
|
1031
|
+
});
|
|
1032
|
+
globalThis.describe("retiredField — annotation and nothing else", () => {
|
|
1033
|
+
let withRetired = (schema, field) => S.Metadata.set(schema, StateAnnotations$Reventless.stateAnnotationsId, {
|
|
1034
|
+
ids: [],
|
|
1035
|
+
compositeIds: [],
|
|
1036
|
+
subIds: [],
|
|
1037
|
+
compositeSubIds: [],
|
|
1038
|
+
indexes: [],
|
|
1039
|
+
hidden: [],
|
|
1040
|
+
summary: [],
|
|
1041
|
+
drillTargets: [],
|
|
1042
|
+
drillTargetKeys: [],
|
|
1043
|
+
collapsed: [],
|
|
1044
|
+
scan: [],
|
|
1045
|
+
scanSort: [],
|
|
1046
|
+
semantic: [],
|
|
1047
|
+
metric: [],
|
|
1048
|
+
lifecycle: undefined,
|
|
1049
|
+
groupBy: undefined,
|
|
1050
|
+
visibility: undefined,
|
|
1051
|
+
live: undefined,
|
|
1052
|
+
retired: {
|
|
1053
|
+
field: field,
|
|
1054
|
+
label: "",
|
|
1055
|
+
showWhenFalse: false,
|
|
1056
|
+
values: undefined
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
globalThis.test("publishes the retirement state beside the field", () => {
|
|
1060
|
+
let withState = schema => S.Metadata.set(schema, StateAnnotations$Reventless.stateAnnotationsId, {
|
|
1061
|
+
ids: [],
|
|
1062
|
+
compositeIds: [],
|
|
1063
|
+
subIds: [],
|
|
1064
|
+
compositeSubIds: [],
|
|
1065
|
+
indexes: [],
|
|
1066
|
+
hidden: [],
|
|
1067
|
+
summary: [],
|
|
1068
|
+
drillTargets: [],
|
|
1069
|
+
drillTargetKeys: [],
|
|
1070
|
+
collapsed: [],
|
|
1071
|
+
scan: [],
|
|
1072
|
+
scanSort: [],
|
|
1073
|
+
semantic: [],
|
|
1074
|
+
metric: [],
|
|
1075
|
+
lifecycle: "accountStatus",
|
|
1076
|
+
groupBy: undefined,
|
|
1077
|
+
visibility: undefined,
|
|
1078
|
+
live: undefined,
|
|
1079
|
+
retired: {
|
|
1080
|
+
field: "accountStatus",
|
|
1081
|
+
label: "",
|
|
1082
|
+
showWhenFalse: false,
|
|
1083
|
+
values: [
|
|
1084
|
+
"Deactivated",
|
|
1085
|
+
"Closed"
|
|
1086
|
+
]
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
let schema = withState(S.schema(s => ({
|
|
1090
|
+
accountStatus: s.m(S.string)
|
|
1091
|
+
})));
|
|
1092
|
+
globalThis.expect([
|
|
1093
|
+
Plugin_Structure$ReventlessCore.retiredFieldFromStateSchema(schema),
|
|
1094
|
+
Plugin_Structure$ReventlessCore.retiredValuesFromStateSchema(schema)
|
|
1095
|
+
]).toEqual([
|
|
1096
|
+
"accountStatus",
|
|
1097
|
+
[
|
|
1098
|
+
"Deactivated",
|
|
1099
|
+
"Closed"
|
|
1100
|
+
]
|
|
1101
|
+
]);
|
|
1102
|
+
});
|
|
1103
|
+
globalThis.test("publishes no states on the boolean form", () => {
|
|
1104
|
+
let schema = S.schema(s => ({
|
|
1105
|
+
archived: s.m(S.bool)
|
|
1106
|
+
}));
|
|
1107
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.retiredValuesFromStateSchema(withRetired(schema, "archived"))).toEqual(undefined);
|
|
1108
|
+
});
|
|
1109
|
+
globalThis.test("names the annotated field", () => {
|
|
1110
|
+
let schema = S.schema(s => ({
|
|
1111
|
+
archived: s.m(S.bool)
|
|
1112
|
+
}));
|
|
1113
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.retiredFieldFromStateSchema(withRetired(schema, "archived"))).toEqual("archived");
|
|
1114
|
+
});
|
|
1115
|
+
globalThis.test("declines a conventionally-named boolean nobody annotated", () => {
|
|
1116
|
+
let schema = S.schema(s => ({
|
|
1117
|
+
archived: s.m(S.bool),
|
|
1118
|
+
deactivated: s.m(S.bool)
|
|
1119
|
+
}));
|
|
1120
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.retiredFieldFromStateSchema(schema)).toEqual(undefined);
|
|
817
1121
|
});
|
|
818
|
-
globalThis.test("
|
|
1122
|
+
globalThis.test("declines a schema carrying no annotation spec at all", () => {
|
|
819
1123
|
let schema = S.schema(s => ({
|
|
820
|
-
|
|
1124
|
+
id: s.m(S.string)
|
|
821
1125
|
}));
|
|
822
|
-
globalThis.expect(Plugin_Structure$ReventlessCore.
|
|
1126
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.retiredFieldFromStateSchema(schema)).toEqual(undefined);
|
|
823
1127
|
});
|
|
824
1128
|
});
|
|
825
1129
|
let getProperty = (json, key) => {
|
|
@@ -1138,6 +1442,8 @@ export {
|
|
|
1138
1442
|
PsUploadAvatarSlice,
|
|
1139
1443
|
PsChangePhotoSlice,
|
|
1140
1444
|
PsReserveStockSlice,
|
|
1445
|
+
PsDispatchShipmentSlice,
|
|
1446
|
+
PsShipmentsViewSlice,
|
|
1141
1447
|
structure,
|
|
1142
1448
|
}
|
|
1143
1449
|
/* structure Not a pure module */
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Test fixture spec for the @transition cross-check — the failing case.
|
|
2
|
+
//
|
|
3
|
+
// `Dispatchd` is a deliberate typo. It compiles: the PPX strips the attribute
|
|
4
|
+
// before the typechecker sees it, so a misspelled state is a well-formed string
|
|
5
|
+
// all the way down. That is exactly the hole the structure-side check closes,
|
|
6
|
+
// and a fixture that could not compile would not demonstrate it.
|
|
7
|
+
|
|
8
|
+
@@reventless.spec("DispatchShipment")
|
|
9
|
+
|
|
10
|
+
type shipmentStatus =
|
|
11
|
+
| Booked
|
|
12
|
+
| Dispatched
|
|
13
|
+
|
|
14
|
+
@schema
|
|
15
|
+
type consumedEvent = ShipmentBooked({shipmentId: string})
|
|
16
|
+
|
|
17
|
+
let evolve = (_state, _event) => true
|
|
18
|
+
|
|
19
|
+
type state = bool
|
|
20
|
+
let initialState = false
|
|
21
|
+
|
|
22
|
+
@schema
|
|
23
|
+
type command = @transition(([Booked]) => Dispatchd) DispatchShipment({shipmentId: string})
|
|
24
|
+
|
|
25
|
+
@schema
|
|
26
|
+
type error = ShipmentNotFound
|
|
27
|
+
|
|
28
|
+
@schema
|
|
29
|
+
type event = ShipmentDispatched({shipmentId: string})
|
|
30
|
+
|
|
31
|
+
let decide = (_state, _command): result<array<event>, error> => Ok([])
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
|
+
import * as Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
|
|
6
|
+
|
|
7
|
+
let consumedEventSchema = S.schema(s => ({
|
|
8
|
+
TAG: "ShipmentBooked",
|
|
9
|
+
shipmentId: s.m(DcbTag$Reventless.string)
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
function evolve(_state, _event) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let commandSchema = S.schema(s => ({
|
|
17
|
+
TAG: "DispatchShipment",
|
|
18
|
+
shipmentId: s.m(DcbTag$Reventless.string)
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
let errorSchema = S.literal("ShipmentNotFound");
|
|
22
|
+
|
|
23
|
+
let eventSchema = S.schema(s => ({
|
|
24
|
+
TAG: "ShipmentDispatched",
|
|
25
|
+
shipmentId: s.m(DcbTag$Reventless.string)
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
function decide(_state, _command) {
|
|
29
|
+
return {
|
|
30
|
+
TAG: "Ok",
|
|
31
|
+
_0: []
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let commandSchema$1 = Api$ReventlessInfra.markAllowedStates(commandSchema, [[
|
|
36
|
+
"DispatchShipment",
|
|
37
|
+
["Booked"]
|
|
38
|
+
]]);
|
|
39
|
+
|
|
40
|
+
let commandSchema$2 = Api$ReventlessInfra.markTargetState(commandSchema$1, [[
|
|
41
|
+
"DispatchShipment",
|
|
42
|
+
"Dispatchd"
|
|
43
|
+
]]);
|
|
44
|
+
|
|
45
|
+
function commandAuthorization(param) {
|
|
46
|
+
return "AllowAuthenticated";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let name = "DispatchShipment";
|
|
50
|
+
|
|
51
|
+
let Id;
|
|
52
|
+
|
|
53
|
+
let initialState = false;
|
|
54
|
+
|
|
55
|
+
let moduleUrl = "@reventlessdev/reventless-core/tests/plugin/StateChangeSlice/PsDispatchShipment.res.mjs";
|
|
56
|
+
|
|
57
|
+
let readConsistency = "EscalateOnRetry";
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
name,
|
|
61
|
+
Id,
|
|
62
|
+
consumedEventSchema,
|
|
63
|
+
evolve,
|
|
64
|
+
initialState,
|
|
65
|
+
errorSchema,
|
|
66
|
+
eventSchema,
|
|
67
|
+
decide,
|
|
68
|
+
commandSchema$2 as commandSchema,
|
|
69
|
+
moduleUrl,
|
|
70
|
+
commandAuthorization,
|
|
71
|
+
readConsistency,
|
|
72
|
+
}
|
|
73
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -18,8 +18,23 @@ let evolve = (_state, _event) => true
|
|
|
18
18
|
// It is also `@noApi` (internal/admin only): a two-variant slice with exactly
|
|
19
19
|
// one API-exposed command, which is the case that used to leak the exposed
|
|
20
20
|
// command's slice mutation field onto the non-exposed variant.
|
|
21
|
+
// The lifecycle this slice's commands move a shipment through. The PPX strips
|
|
22
|
+
// @transition before the typechecker sees it, so this enum is not what makes the
|
|
23
|
+
// reference below valid — the structure-side check against the linked view is.
|
|
24
|
+
// It is here because a state is a constructor, and naming one in a string is a
|
|
25
|
+
// reference nothing can check at any layer.
|
|
26
|
+
type shipmentStatus = Placed | Shipped | Cancelled
|
|
27
|
+
|
|
28
|
+
// CancelShipment is payload-less — it compiles to a bare string literal, so it
|
|
29
|
+
// exercises toCommandDef's payload-less branch (still surfaced in `commands`).
|
|
30
|
+
// It is also `@noApi` (internal/admin only): a two-variant slice with exactly
|
|
31
|
+
// one API-exposed command, which is the case that used to leak the exposed
|
|
32
|
+
// command's slice mutation field onto the non-exposed variant. It carries no
|
|
33
|
+
// @transition, which is the un-annotated case the resolver still guesses for.
|
|
21
34
|
@schema
|
|
22
|
-
type command =
|
|
35
|
+
type command =
|
|
36
|
+
| @transition(([Placed]) => Shipped) ShipOrder({orderId: string})
|
|
37
|
+
| @noApi CancelShipment
|
|
23
38
|
|
|
24
39
|
// Two variants, one payload-less and one carrying a field — the same two branches
|
|
25
40
|
// `events` exercises, so the structure's `errors` list is pinned on both.
|
|
@@ -47,7 +47,12 @@ function decide(_state, _command) {
|
|
|
47
47
|
|
|
48
48
|
let commandSchema$1 = Api$ReventlessInfra.markNoApiVariants(commandSchema, ["CancelShipment"]);
|
|
49
49
|
|
|
50
|
-
let commandSchema$2 = Api$ReventlessInfra.
|
|
50
|
+
let commandSchema$2 = Api$ReventlessInfra.markAllowedStates(commandSchema$1, [[
|
|
51
|
+
"ShipOrder",
|
|
52
|
+
["Placed"]
|
|
53
|
+
]]);
|
|
54
|
+
|
|
55
|
+
let commandSchema$3 = Api$ReventlessInfra.markTargetState(commandSchema$2, [[
|
|
51
56
|
"ShipOrder",
|
|
52
57
|
"Shipped"
|
|
53
58
|
]]);
|
|
@@ -75,7 +80,7 @@ export {
|
|
|
75
80
|
errorSchema,
|
|
76
81
|
eventSchema,
|
|
77
82
|
decide,
|
|
78
|
-
commandSchema$
|
|
83
|
+
commandSchema$3 as commandSchema,
|
|
79
84
|
moduleUrl,
|
|
80
85
|
commandAuthorization,
|
|
81
86
|
readConsistency,
|
|
@@ -79,10 +79,11 @@ let stateSchema$1 = S.Metadata.set(stateSchema, StateAnnotations$Reventless.stat
|
|
|
79
79
|
label: "Revenue"
|
|
80
80
|
}
|
|
81
81
|
]],
|
|
82
|
-
|
|
82
|
+
lifecycle: undefined,
|
|
83
83
|
groupBy: undefined,
|
|
84
84
|
visibility: undefined,
|
|
85
|
-
live: false
|
|
85
|
+
live: false,
|
|
86
|
+
retired: undefined
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
let name = "AnnotatedView";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Test fixture spec for the @transition cross-check.
|
|
2
|
+
// A view that declares a lifecycle, so a command's declared states have
|
|
3
|
+
// something to be checked against. `PsOrdersView` deliberately declares none —
|
|
4
|
+
// the two together cover both the checked and the unvalidated path.
|
|
5
|
+
|
|
6
|
+
@@reventless.spec("Shipments")
|
|
7
|
+
|
|
8
|
+
@schema
|
|
9
|
+
type consumedEvent =
|
|
10
|
+
| ShipmentBooked({shipmentId: string})
|
|
11
|
+
| ShipmentDispatched({shipmentId: string})
|
|
12
|
+
|
|
13
|
+
@schema
|
|
14
|
+
type shipmentStatus =
|
|
15
|
+
| Booked
|
|
16
|
+
| Dispatched
|
|
17
|
+
|
|
18
|
+
@schema
|
|
19
|
+
type state = {shipmentId: string, @lifecycle shipmentStatus: shipmentStatus}
|
|
20
|
+
|
|
21
|
+
let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
|
|
22
|
+
switch event {
|
|
23
|
+
| ShipmentBooked({shipmentId}) => [Set(shipmentId, {shipmentId, shipmentStatus: Booked})]
|
|
24
|
+
| ShipmentDispatched({shipmentId}) =>
|
|
25
|
+
[Update(shipmentId, s => {...s, shipmentStatus: Dispatched})]
|
|
26
|
+
}
|