@reventlessdev/reventless-core 3.0.0-alpha.295 → 3.0.0-alpha.297

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 (29) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/package.json +6 -6
  3. package/src/admin/Platform_ComponentDefinitionsApi.res +3 -1
  4. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
  5. package/src/components/Api/GraphQL_FragmentGenerator.res +15 -0
  6. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +13 -0
  7. package/src/components/AutomationSlice/AutomationSlice.res +2 -0
  8. package/src/components/AutomationSlice/AutomationSlice_Builder.res +126 -9
  9. package/src/components/AutomationSlice/AutomationSlice_Builder.res.mjs +90 -10
  10. package/src/components/AutomationSlice/AutomationSlice_Callback.res +122 -1
  11. package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +101 -2
  12. package/src/components/AutomationSlice/AutomationSlice_Route.res +3 -1
  13. package/src/components/AutomationSlice/AutomationSlice_Route.res.mjs +2 -1
  14. package/src/components/AutomationSlice/AutomationSlice_Schedules.res +149 -0
  15. package/src/components/AutomationSlice/AutomationSlice_Schedules.res.mjs +193 -0
  16. package/src/components/Dcb/Dcb_Builder.res +35 -4
  17. package/src/components/Dcb/Dcb_Builder.res.mjs +33 -5
  18. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +1 -1
  19. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res.mjs +3 -1
  20. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +1 -1
  21. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res.mjs +3 -1
  22. package/src/plugin/component/Plugin_Builder.res +10 -3
  23. package/src/plugin/component/Plugin_Builder.res.mjs +8 -2
  24. package/src/plugin/component/Plugin_Structure.res +7 -0
  25. package/src/plugin/component/Plugin_Structure.res.mjs +5 -2
  26. package/tests/admin/AdminApiSchemaDriftTest.res +1 -0
  27. package/tests/admin/AdminApiSchemaDriftTest.res.mjs +4 -1
  28. package/tests/automationslice/AutomationSliceSchedulesTest.res +143 -0
  29. package/tests/automationslice/AutomationSliceSchedulesTest.res.mjs +247 -0
@@ -0,0 +1,193 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Sury from "sury";
4
+ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
5
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Clock$Reventless from "@reventlessdev/reventless-spec/src/components/Clock.res.mjs";
8
+ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
9
+ import * as Schedule$Reventless from "@reventlessdev/reventless-spec/src/types/Schedule.res.mjs";
10
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
11
+ import * as ScheduleTime$ReventlessCore from "../../util/ScheduleTime.res.mjs";
12
+
13
+ let declared = "declared";
14
+
15
+ let rowSchema = Sury.$schema(s => ({
16
+ key: s.m(Sury.string),
17
+ time: s.m(Sury.$option(Sury.string)),
18
+ setBy: s.m(Sury.string),
19
+ setAt: s.m(Sury.string)
20
+ }));
21
+
22
+ function encodeTime(time) {
23
+ return JSON.stringify(Util_Sury$Reventless.toJson(time, Schedule$Reventless.timeSchema));
24
+ }
25
+
26
+ function decodeTime(text) {
27
+ return Util_Sury$Reventless.fromJson(JSON.parse(text), Schedule$Reventless.timeSchema);
28
+ }
29
+
30
+ function timeOf(row) {
31
+ return Stdlib_Option.map(row.time, decodeTime);
32
+ }
33
+
34
+ function iso(millis) {
35
+ return new Date(millis).toISOString();
36
+ }
37
+
38
+ function clearRow(rows, key, setBy, setAt) {
39
+ if (key === Clock$Reventless.defaultKey) {
40
+ rows[key] = {
41
+ key: key,
42
+ setBy: setBy,
43
+ setAt: setAt
44
+ };
45
+ return;
46
+ } else {
47
+ return Stdlib_Dict.$$delete(rows, key);
48
+ }
49
+ }
50
+
51
+ function apply(rows, change, setBy, recordedAt) {
52
+ if (change.TAG === "Set") {
53
+ let wanted = change.time;
54
+ let key = change.key;
55
+ let errs = ScheduleTime$ReventlessCore.errors(wanted);
56
+ if (errs.length !== 0) {
57
+ return {
58
+ TAG: "Error",
59
+ _0: `schedule ` + key + `: ` + errs.join("; ")
60
+ };
61
+ }
62
+ let resolved = ScheduleTime$ReventlessCore.resolveOnce(wanted, recordedAt);
63
+ rows[key] = {
64
+ key: key,
65
+ time: encodeTime(resolved),
66
+ setBy: setBy,
67
+ setAt: new Date(recordedAt).toISOString()
68
+ };
69
+ return {
70
+ TAG: "Ok",
71
+ _0: {
72
+ TAG: "Put",
73
+ key: key,
74
+ time: resolved
75
+ }
76
+ };
77
+ }
78
+ let cleared = change.key;
79
+ clearRow(rows, cleared, setBy, new Date(recordedAt).toISOString());
80
+ return {
81
+ TAG: "Ok",
82
+ _0: {
83
+ TAG: "Remove",
84
+ key: cleared
85
+ }
86
+ };
87
+ }
88
+
89
+ function fired(rows, key) {
90
+ Stdlib_Option.forEach(rows[key], row => {
91
+ let match = Stdlib_Option.map(row.time, decodeTime);
92
+ if (match === undefined) {
93
+ return;
94
+ }
95
+ switch (match.TAG) {
96
+ case "Every" :
97
+ case "Calendar" :
98
+ return;
99
+ case "Once" :
100
+ return clearRow(rows, key, row.setBy, row.setAt);
101
+ }
102
+ });
103
+ }
104
+
105
+ function declare(rows, declaredTime, now) {
106
+ let match = rows[Clock$Reventless.defaultKey];
107
+ if (declaredTime !== undefined) {
108
+ if (match !== undefined && !(match.setBy === declared && Primitive_object.notequal(match.time, encodeTime(declaredTime)))) {
109
+ return;
110
+ } else {
111
+ rows[Clock$Reventless.defaultKey] = {
112
+ key: Clock$Reventless.defaultKey,
113
+ time: encodeTime(declaredTime),
114
+ setBy: declared,
115
+ setAt: new Date(now).toISOString()
116
+ };
117
+ return;
118
+ }
119
+ } else if (match !== undefined && match.setBy === declared) {
120
+ return Stdlib_Dict.$$delete(rows, Clock$Reventless.defaultKey);
121
+ } else {
122
+ return;
123
+ }
124
+ }
125
+
126
+ function isPast(time, now) {
127
+ switch (time.TAG) {
128
+ case "Every" :
129
+ case "Calendar" :
130
+ return false;
131
+ case "Once" :
132
+ return Stdlib_Option.isNone(ScheduleTime$ReventlessCore.nextFiring(time, now));
133
+ }
134
+ }
135
+
136
+ async function reconcile(rows, platform, declaredTime, now) {
137
+ declare(rows, declaredTime, now);
138
+ let live = Stdlib_Array.filterMap(Object.values(rows), row => {
139
+ let time = Stdlib_Option.map(row.time, decodeTime);
140
+ if (time !== undefined) {
141
+ if (isPast(time, now)) {
142
+ clearRow(rows, row.key, row.setBy, row.setAt);
143
+ return;
144
+ } else {
145
+ return [
146
+ row,
147
+ time
148
+ ];
149
+ }
150
+ }
151
+ });
152
+ let held = await platform.names();
153
+ for (let i = 0, i_finish = live.length; i < i_finish; ++i) {
154
+ let match = live[i];
155
+ let row = match[0];
156
+ if (row.setBy === declared || !held.includes(platform.nameOf(row.key))) {
157
+ await platform.put(row.key, match[1]);
158
+ }
159
+ }
160
+ let wanted = live.map(param => platform.nameOf(param[0].key));
161
+ let extra = held.filter(name => !wanted.includes(name));
162
+ for (let i$1 = 0, i_finish$1 = extra.length; i$1 < i_finish$1; ++i$1) {
163
+ await platform.removeName(extra[i$1]);
164
+ }
165
+ }
166
+
167
+ async function tell(platform, changes) {
168
+ for (let i = 0, i_finish = changes.length; i < i_finish; ++i) {
169
+ let match = changes[i];
170
+ if (match.TAG === "Put") {
171
+ await platform.put(match.key, match.time);
172
+ } else {
173
+ await platform.removeName(platform.nameOf(match.key));
174
+ }
175
+ }
176
+ }
177
+
178
+ export {
179
+ declared,
180
+ rowSchema,
181
+ encodeTime,
182
+ decodeTime,
183
+ timeOf,
184
+ iso,
185
+ clearRow,
186
+ apply,
187
+ fired,
188
+ declare,
189
+ isPast,
190
+ reconcile,
191
+ tell,
192
+ }
193
+ /* rowSchema Not a pure module */
@@ -188,11 +188,13 @@ module Make = (
188
188
  // its own `sourceEventSchema`. Validation contributes one entry per
189
189
  // (slice, source) pair so unknown variants surface against the right
190
190
  // mapping context.
191
+ // The Clock's ticks come from the slice's schedules, not from a slice.
191
192
  automationSlices->Array.flatMap((module(A: AutomationSlice.T)) =>
192
- A.Automation.mappings->Array.map((module(M: A.Automation.Mapping)) => (
193
- A.Spec.name,
194
- M.sourceEventSchema->S.castToUnknown,
195
- ))
193
+ A.Automation.mappings->Array.filterMap((module(M: A.Automation.Mapping)) =>
194
+ M.sourceName == Reventless.Clock.name
195
+ ? None
196
+ : Some((A.Spec.name, M.sourceEventSchema->S.castToUnknown))
197
+ )
196
198
  ),
197
199
  )
198
200
  ->Array.concat(
@@ -664,6 +666,12 @@ module Make = (
664
666
  automationSlices->Array.forEach((module(A: AutomationSlice.T)) => {
665
667
  let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=A.queryDbName)
666
668
  Plugin_Helpers.queryFieldNamesRegistry->Dict.set(A.queryDbName, qn)
669
+ A.schedulesDbName->Option.forEach(queryDbName =>
670
+ Plugin_Helpers.queryFieldNamesRegistry->Dict.set(
671
+ queryDbName,
672
+ Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName),
673
+ )
674
+ )
667
675
  })
668
676
 
669
677
  outboundTranslationSlices->Array.forEach((module(O: OutboundTranslationSlice.T)) => {
@@ -1130,6 +1138,9 @@ module Make = (
1130
1138
  }
1131
1139
  })
1132
1140
 
1141
+ // What the framework keeps for a slice is read by the deployment's operators.
1142
+ let operators = Reventless.OwnerScope.operators()
1143
+
1133
1144
  let automationEntries = automationSlices->Array.map((module(A: AutomationSlice.T)) => {
1134
1145
  let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=A.queryDbName)
1135
1146
  {
@@ -1140,10 +1151,27 @@ module Make = (
1140
1151
  A.Spec.todoItemSchema,
1141
1152
  )->S.castToUnknown,
1142
1153
  authorization: None,
1154
+ permission: operators,
1143
1155
  connectionSpec: true,
1144
1156
  }
1145
1157
  })
1146
1158
 
1159
+ // A scheduled slice's schedules: when it runs, and who set it.
1160
+ let scheduleEntries = automationSlices->Array.filterMap((module(A: AutomationSlice.T)) =>
1161
+ A.schedulesDbName->Option.map(queryDbName => {
1162
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName)
1163
+ {
1164
+ ReventlessInfra.Api.singleFieldName: qn.singleFieldName,
1165
+ listFieldName: qn.listFieldName,
1166
+ returnTypeName: qn.returnTypeName,
1167
+ stateSchema: AutomationSlice_Schedules.rowSchema->S.castToUnknown,
1168
+ authorization: None,
1169
+ permission: operators,
1170
+ connectionSpec: true,
1171
+ }
1172
+ })
1173
+ )
1174
+
1147
1175
  let outboundEntries =
1148
1176
  outboundTranslationSlices->Array.map((module(O: OutboundTranslationSlice.T)) => {
1149
1177
  let qn = Api_Naming.queryFieldNamesForSliceQueryDb(
@@ -1158,6 +1186,7 @@ module Make = (
1158
1186
  O.Spec.outboundItemSchema,
1159
1187
  )->S.castToUnknown,
1160
1188
  authorization: None,
1189
+ permission: operators,
1161
1190
  connectionSpec: true,
1162
1191
  }
1163
1192
  })
@@ -1174,6 +1203,7 @@ module Make = (
1174
1203
  returnTypeName: qn.returnTypeName,
1175
1204
  stateSchema: InboundTranslationSlice_Callback.auditRowSchema->S.castToUnknown,
1176
1205
  authorization: None,
1206
+ permission: operators,
1177
1207
  connectionSpec: true,
1178
1208
  }
1179
1209
  })
@@ -1234,6 +1264,7 @@ module Make = (
1234
1264
  mutationEntries: Array.concat(mutationEntriesFromSlices, mutationEntriesFromInboundSlices),
1235
1265
  queryEntries: stateViewEntries
1236
1266
  ->Array.concat(automationEntries)
1267
+ ->Array.concat(scheduleEntries)
1237
1268
  ->Array.concat(outboundEntries)
1238
1269
  ->Array.concat(inboundEntries),
1239
1270
  eventLogEntries: if allProducedSchemas->Array.length > 0 {
@@ -8,10 +8,12 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
8
  import * as Effect from "effect/Effect";
9
9
  import * as Pulumi from "@pulumi/pulumi";
10
10
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
11
+ import * as Clock$Reventless from "@reventlessdev/reventless-spec/src/components/Clock.res.mjs";
11
12
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
12
13
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
13
14
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
14
15
  import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
16
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
15
17
  import * as Authorization$Reventless from "@reventlessdev/reventless-spec/src/types/Authorization.res.mjs";
16
18
  import * as Component$ReventlessCore from "../Component.res.mjs";
17
19
  import * as DcbValidation$Reventless from "@reventlessdev/reventless-spec/src/components/DcbValidation.res.mjs";
@@ -25,6 +27,7 @@ import * as Plugin_Structure$ReventlessCore from "../../plugin/component/Plugin_
25
27
  import * as DcbEventLog_Builder$ReventlessCore from "../DcbEventLog/DcbEventLog_Builder.res.mjs";
26
28
  import * as CommandTopic_Builder$ReventlessCore from "../CommandTopic/CommandTopic_Builder.res.mjs";
27
29
  import * as AutomationSlice_Callback$ReventlessCore from "../AutomationSlice/AutomationSlice_Callback.res.mjs";
30
+ import * as AutomationSlice_Schedules$ReventlessCore from "../AutomationSlice/AutomationSlice_Schedules.res.mjs";
28
31
  import * as CommandGenerator_Callback$ReventlessCore from "../CommandGenerator/CommandGenerator_Callback.res.mjs";
29
32
  import * as InboundTranslationSlice_Callback$ReventlessCore from "../InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs";
30
33
  import * as OutboundTranslationSlice_Callback$ReventlessCore from "../OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
@@ -150,10 +153,16 @@ function Make(DcbEventLogStorage) {
150
153
  ]).concat(stateViewSlices.map(V => [
151
154
  V.Spec.name,
152
155
  V.Spec.consumedEventSchema
153
- ])).concat(automationSlices.flatMap(A => A.Automation.mappings.map(M => [
154
- A.Spec.name,
155
- M.sourceEventSchema
156
- ]))).concat(outboundTranslationSlices.map(O => [
156
+ ])).concat(automationSlices.flatMap(A => Stdlib_Array.filterMap(A.Automation.mappings, M => {
157
+ if (M.sourceName === Clock$Reventless.name) {
158
+ return;
159
+ } else {
160
+ return [
161
+ A.Spec.name,
162
+ M.sourceEventSchema
163
+ ];
164
+ }
165
+ }))).concat(outboundTranslationSlices.map(O => [
157
166
  O.Spec.name,
158
167
  O.Spec.consumedEventSchema
159
168
  ]));
@@ -352,6 +361,9 @@ function Make(DcbEventLogStorage) {
352
361
  automationSlices.forEach(A => {
353
362
  let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, A.queryDbName, undefined);
354
363
  Plugin_Helpers$ReventlessCore.queryFieldNamesRegistry[A.queryDbName] = qn;
364
+ Stdlib_Option.forEach(A.schedulesDbName, queryDbName => {
365
+ Plugin_Helpers$ReventlessCore.queryFieldNamesRegistry[queryDbName] = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, queryDbName, undefined);
366
+ });
355
367
  });
356
368
  outboundTranslationSlices.forEach(O => {
357
369
  let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, O.queryDbName, undefined);
@@ -601,6 +613,7 @@ function Make(DcbEventLogStorage) {
601
613
  resolvedFields: resolvedFields
602
614
  };
603
615
  });
616
+ let operators = OwnerScope$Reventless.operators();
604
617
  let automationEntries = automationSlices.map(A => {
605
618
  let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, A.queryDbName, undefined);
606
619
  return {
@@ -609,9 +622,22 @@ function Make(DcbEventLogStorage) {
609
622
  returnTypeName: qn.returnTypeName,
610
623
  stateSchema: AutomationSlice_Callback$ReventlessCore.todoRowSchemaFor(A.Spec.todoItemSchema),
611
624
  authorization: undefined,
625
+ permission: operators,
612
626
  connectionSpec: true
613
627
  };
614
628
  });
629
+ let scheduleEntries = Stdlib_Array.filterMap(automationSlices, A => Stdlib_Option.map(A.schedulesDbName, queryDbName => {
630
+ let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, queryDbName, undefined);
631
+ return {
632
+ singleFieldName: qn.singleFieldName,
633
+ listFieldName: qn.listFieldName,
634
+ returnTypeName: qn.returnTypeName,
635
+ stateSchema: AutomationSlice_Schedules$ReventlessCore.rowSchema,
636
+ authorization: undefined,
637
+ permission: operators,
638
+ connectionSpec: true
639
+ };
640
+ }));
615
641
  let outboundEntries = outboundTranslationSlices.map(O => {
616
642
  let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, O.queryDbName, undefined);
617
643
  return {
@@ -620,6 +646,7 @@ function Make(DcbEventLogStorage) {
620
646
  returnTypeName: qn.returnTypeName,
621
647
  stateSchema: OutboundTranslationSlice_Callback$ReventlessCore.todoRowSchemaFor(O.Spec.outboundItemSchema),
622
648
  authorization: undefined,
649
+ permission: operators,
623
650
  connectionSpec: true
624
651
  };
625
652
  });
@@ -631,6 +658,7 @@ function Make(DcbEventLogStorage) {
631
658
  returnTypeName: qn.returnTypeName,
632
659
  stateSchema: InboundTranslationSlice_Callback$ReventlessCore.auditRowSchema,
633
660
  authorization: undefined,
661
+ permission: operators,
634
662
  connectionSpec: true
635
663
  };
636
664
  });
@@ -655,7 +683,7 @@ function Make(DcbEventLogStorage) {
655
683
  dcbPublishJsons: publishJsons,
656
684
  dcbCommandTopicQueueUrl: dcbCommandTopicQueueUrl,
657
685
  mutationEntries: mutationEntriesFromSlices.concat(mutationEntriesFromInboundSlices),
658
- queryEntries: stateViewEntries.concat(automationEntries).concat(outboundEntries).concat(inboundEntries),
686
+ queryEntries: stateViewEntries.concat(automationEntries).concat(scheduleEntries).concat(outboundEntries).concat(inboundEntries),
659
687
  eventLogEntries: allProducedSchemas.length !== 0 ? [{
660
688
  busKey: name + "DcbEventLog",
661
689
  displayName: name,
@@ -36,7 +36,7 @@ module Make = (
36
36
  let config = Reventless.ReadModel.config()
37
37
  let subIdConfig: option<Reventless.ReadModel.subIdConfig<state>> = None
38
38
  type role = Reventless.Role.name
39
- let authorization: Reventless.Authorization.rule<role> = AllowAuthenticated
39
+ let authorization: Reventless.Authorization.rule<role> = Reventless.OwnerScope.operators()
40
40
  let visibility: Reventless.Visibility.t = Public
41
41
  }
42
42
 
@@ -3,6 +3,7 @@
3
3
  import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
4
4
  import * as Pulumi from "@pulumi/pulumi";
5
5
  import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
6
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
6
7
  import * as Component$ReventlessCore from "../Component.res.mjs";
7
8
  import * as ComponentType$ReventlessCore from "../../ComponentType.res.mjs";
8
9
  import * as QueryDb_Builder$ReventlessCore from "../QueryDb/QueryDb_Builder.res.mjs";
@@ -16,6 +17,7 @@ function Make(QueryDbStorage) {
16
17
  let queryDbName = Spec.name + "Audit";
17
18
  let moduleUrl = import.meta.url;
18
19
  let config = ReadModel$Reventless.config(undefined, undefined, undefined);
20
+ let authorization = OwnerScope$Reventless.operators();
19
21
  let SpecificQueryDb = QueryDb_Builder$ReventlessCore.Make({
20
22
  Id: Id$Reventless.$$String,
21
23
  name: queryDbName,
@@ -23,7 +25,7 @@ function Make(QueryDbStorage) {
23
25
  stateSchema: InboundTranslationSlice_Callback$ReventlessCore.auditRowSchema,
24
26
  config: config,
25
27
  subIdConfig: undefined,
26
- authorization: "AllowAuthenticated",
28
+ authorization: authorization,
27
29
  visibility: "Public"
28
30
  })(QueryDbStorage)(QueryDbResolvers);
29
31
  let syncToQueryDb = async (queryDbOps, requestId) => {
@@ -52,7 +52,7 @@ module Make = (
52
52
  let config = Reventless.ReadModel.config()
53
53
  let subIdConfig: option<Reventless.ReadModel.subIdConfig<state>> = None
54
54
  type role = Reventless.Role.name
55
- let authorization: Reventless.Authorization.rule<role> = AllowAuthenticated
55
+ let authorization: Reventless.Authorization.rule<role> = Reventless.OwnerScope.operators()
56
56
  let visibility: Reventless.Visibility.t = Public
57
57
  }
58
58
 
@@ -17,6 +17,7 @@ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js
17
17
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
18
18
  import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
19
19
  import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
20
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
20
21
  import * as Message$ReventlessCore from "../../Message.res.mjs";
21
22
  import * as Component$ReventlessCore from "../Component.res.mjs";
22
23
  import * as EventTopic$ReventlessCore from "../EventTopic/EventTopic.res.mjs";
@@ -35,6 +36,7 @@ function Make(RuntimeEnvironment) {
35
36
  let queryDbName = Spec.name + "Todo";
36
37
  let moduleUrl = import.meta.url;
37
38
  let config = ReadModel$Reventless.config(undefined, undefined, undefined);
39
+ let authorization = OwnerScope$Reventless.operators();
38
40
  let SpecificQueryDb = QueryDb_Builder$ReventlessCore.Make({
39
41
  Id: Id$Reventless.$$String,
40
42
  name: queryDbName,
@@ -42,7 +44,7 @@ function Make(RuntimeEnvironment) {
42
44
  stateSchema: OutboundTranslationSlice_Callback$ReventlessCore.todoRowSchema,
43
45
  config: config,
44
46
  subIdConfig: undefined,
45
- authorization: "AllowAuthenticated",
47
+ authorization: authorization,
46
48
  visibility: "Public"
47
49
  })(QueryDbStorage)(QueryDbResolvers);
48
50
  let SpecificEventCollector = EventCollector_Builder$ReventlessCore.Make(RuntimeEnvironment)(EventCollectorChannel);
@@ -382,7 +382,7 @@ module Make = (
382
382
  // and idResolver paths). Slice output dicts are keyed by Spec.name; the
383
383
  // queryDb name appends a suffix per slice kind:
384
384
  // StateViewSlice — no suffix
385
- // AutomationSlice — "Todo"
385
+ // AutomationSlice — "Todo", and "Schedule" when it maps the Clock
386
386
  // OutboundTranslationSlice — "Todo"
387
387
  // InboundTranslationSlice — "Audit"
388
388
  dcbResult.stateViewSlicesOutputs
@@ -393,7 +393,10 @@ module Make = (
393
393
  ->Array.forEach(((k, v)) => allQueryDbs->Dict.set(k ++ "Audit", v.queryDb))
394
394
  dcbResult.automationSlicesOutputs
395
395
  ->Dict.toArray
396
- ->Array.forEach(((k, v)) => allQueryDbs->Dict.set(k ++ "Todo", v.queryDb))
396
+ ->Array.forEach(((k, v)) => {
397
+ allQueryDbs->Dict.set(k ++ "Todo", v.queryDb)
398
+ v.schedulesDb->Option.forEach(db => allQueryDbs->Dict.set(k ++ "Schedule", db))
399
+ })
397
400
  dcbResult.outboundTranslationSlicesOutputs
398
401
  ->Dict.toArray
399
402
  ->Array.forEach(((k, v)) => allQueryDbs->Dict.set(k ++ "Todo", v.queryDb))
@@ -466,9 +469,13 @@ module Make = (
466
469
  let extractEventTypes = schema => Reventless.DcbTag.extractVariantNames(schema)
467
470
  let routingSchemas: Dict.t<Plugin_Helpers.pluginDeployedSchema> = Dict.make()
468
471
  automationSlices->Array.forEach((module(As: ReventlessInfra.AutomationSlice.T)) => {
469
- // Union of source-event variants across every per-source mapping.
472
+ // Union of source-event variants across every per-source mapping. The
473
+ // Clock's tick is no event of the plugin's; its schedule says when it comes.
470
474
  let consumedEventTypes =
471
475
  As.Automation.mappings
476
+ ->Array.filter((module(M: As.Automation.Mapping)) =>
477
+ M.sourceName != Reventless.Clock.name
478
+ )
472
479
  ->Array.flatMap((module(M: As.Automation.Mapping)) =>
473
480
  extractEventTypes(M.sourceEventSchema->S.castToUnknown)
474
481
  )
@@ -6,6 +6,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
6
  import * as Pulumi from "@pulumi/pulumi";
7
7
  import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
8
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
+ import * as Clock$Reventless from "@reventlessdev/reventless-spec/src/components/Clock.res.mjs";
9
10
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
10
11
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
11
12
  import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
@@ -284,7 +285,12 @@ function Make(Spec) {
284
285
  allQueryDbs[param[0] + "Audit"] = param[1].queryDb;
285
286
  });
286
287
  Object.entries(dcbResult.automationSlicesOutputs).forEach(param => {
287
- allQueryDbs[param[0] + "Todo"] = param[1].queryDb;
288
+ let v = param[1];
289
+ let k = param[0];
290
+ allQueryDbs[k + "Todo"] = v.queryDb;
291
+ Stdlib_Option.forEach(v.schedulesDb, db => {
292
+ allQueryDbs[k + "Schedule"] = db;
293
+ });
288
294
  });
289
295
  Object.entries(dcbResult.outboundTranslationSlicesOutputs).forEach(param => {
290
296
  allQueryDbs[param[0] + "Todo"] = param[1].queryDb;
@@ -348,7 +354,7 @@ function Make(Spec) {
348
354
  });
349
355
  let routingSchemas = {};
350
356
  automationSlices.forEach(As => {
351
- let consumedEventTypes = Belt_SetString.toArray(Belt_SetString.fromArray(As.Automation.mappings.flatMap(M => DcbTag$Reventless.extractVariantNames(M.sourceEventSchema))));
357
+ let consumedEventTypes = Belt_SetString.toArray(Belt_SetString.fromArray(As.Automation.mappings.filter(M => M.sourceName !== Clock$Reventless.name).flatMap(M => DcbTag$Reventless.extractVariantNames(M.sourceEventSchema))));
352
358
  routingSchemas[As.Spec.name] = {
353
359
  consumedEventTypes: consumedEventTypes,
354
360
  producedCommandTypes: DcbTag$Reventless.extractAllVariantNames(As.Spec.commandSchema),
@@ -1775,6 +1775,8 @@ let make = (
1775
1775
  // contributes that one source's variants.
1776
1776
  let allConsumedVariants =
1777
1777
  AS.Automation.mappings
1778
+ // The Clock's tick is no event of the plugin's; `schedule` says when it comes.
1779
+ ->Array.filter((module(M: AS.Automation.Mapping)) => M.sourceName != Reventless.Clock.name)
1778
1780
  ->Array.flatMap((module(M: AS.Automation.Mapping)) =>
1779
1781
  eventVariantNames(M.sourceEventSchema->S.castToUnknown)
1780
1782
  )
@@ -1788,6 +1790,11 @@ let make = (
1788
1790
  producedCommandTypes: qualify(~prefix=name, commandVariantNames(AS.Spec.commandSchema)),
1789
1791
  targetName: AS.Spec.targetName,
1790
1792
  chapter: chapterOf(AS.Spec.name),
1793
+ schedule: ?(
1794
+ AS.Spec.schedule->Option.map(time =>
1795
+ time->Reventless.Util_Sury.toJson(Reventless.Schedule.timeSchema)->JSON.stringify
1796
+ )
1797
+ ),
1791
1798
  }: Reventless.Plugin.automationSliceDef
1792
1799
  )
1793
1800
  })
@@ -6,12 +6,14 @@ import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
6
6
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
7
  import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
8
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
+ import * as Clock$Reventless from "@reventlessdev/reventless-spec/src/components/Clock.res.mjs";
9
10
  import * as Owner$Reventless from "@reventlessdev/reventless-spec/src/components/Owner.res.mjs";
10
11
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
11
12
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
12
13
  import * as Trait$Reventless from "@reventlessdev/reventless-spec/src/types/Trait.res.mjs";
13
14
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
14
15
  import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
16
+ import * as Schedule$Reventless from "@reventlessdev/reventless-spec/src/types/Schedule.res.mjs";
15
17
  import * as Semantic$Reventless from "@reventlessdev/reventless-spec/src/semantic/Semantic.res.mjs";
16
18
  import * as Lifecycle$Reventless from "@reventlessdev/reventless-spec/src/types/Lifecycle.res.mjs";
17
19
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
@@ -1252,13 +1254,14 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
1252
1254
  };
1253
1255
  });
1254
1256
  let automationSliceDefs = automationSlices.map(AS => {
1255
- let allConsumedVariants = Belt_SetString.toArray(Belt_SetString.fromArray(AS.Automation.mappings.flatMap(M => DcbTag$Reventless.extractVariantNames(M.sourceEventSchema))));
1257
+ let allConsumedVariants = Belt_SetString.toArray(Belt_SetString.fromArray(AS.Automation.mappings.filter(M => M.sourceName !== Clock$Reventless.name).flatMap(M => DcbTag$Reventless.extractVariantNames(M.sourceEventSchema))));
1256
1258
  return {
1257
1259
  name: AS.Spec.name,
1258
1260
  consumedEventTypes: qualify(name, allConsumedVariants),
1259
1261
  producedCommandTypes: qualify(name, DcbTag$Reventless.extractAllVariantNames(AS.Spec.commandSchema)),
1260
1262
  targetName: AS.Spec.targetName,
1261
- chapter: componentChapters[AS.Spec.name]
1263
+ chapter: componentChapters[AS.Spec.name],
1264
+ schedule: Stdlib_Option.map(AS.Spec.schedule, time => JSON.stringify(Util_Sury$Reventless.toJson(time, Schedule$Reventless.timeSchema)))
1262
1265
  };
1263
1266
  });
1264
1267
  let outboundTranslationSliceDefs = outboundTranslationSlices.map(OTS => ({
@@ -120,6 +120,7 @@ let automation: automationSliceDef = {
120
120
  producedCommandTypes: ["Restock"],
121
121
  targetName: "Product",
122
122
  chapter: Some("Replenishment"),
123
+ schedule: `{"TAG":"Every","_0":{"TAG":"Minutes","_0":15}}`,
123
124
  }
124
125
 
125
126
  let outbound: outboundTranslationSliceDef = {
@@ -184,12 +184,15 @@ let automation_producedCommandTypes = ["Restock"];
184
184
 
185
185
  let automation_chapter = "Replenishment";
186
186
 
187
+ let automation_schedule = `{"TAG":"Every","_0":{"TAG":"Minutes","_0":15}}`;
188
+
187
189
  let automation = {
188
190
  name: "Restocker",
189
191
  consumedEventTypes: automation_consumedEventTypes,
190
192
  producedCommandTypes: automation_producedCommandTypes,
191
193
  targetName: "Product",
192
- chapter: automation_chapter
194
+ chapter: automation_chapter,
195
+ schedule: automation_schedule
193
196
  };
194
197
 
195
198
  let outbound_consumedEventTypes = ["OrderPlaced"];