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

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.
@@ -1,12 +1,14 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Sury from "sury";
4
+ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.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 Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
7
8
  import * as Clock from "effect/Clock";
8
9
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
10
  import * as Effect from "effect/Effect";
11
+ import * as Clock$Reventless from "@reventlessdev/reventless-spec/src/components/Clock.res.mjs";
10
12
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11
13
  import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
12
14
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
@@ -14,6 +16,7 @@ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/U
14
16
  import * as Message$ReventlessCore from "../../Message.res.mjs";
15
17
  import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
16
18
  import * as AutomationSlice_Route$ReventlessCore from "./AutomationSlice_Route.res.mjs";
19
+ import * as AutomationSlice_Schedules$ReventlessCore from "./AutomationSlice_Schedules.res.mjs";
17
20
 
18
21
  let todoStatusSchema = Sury.union([
19
22
  Sury.literal("Pending"),
@@ -45,10 +48,43 @@ function todoRowSchemaFor(itemSchema) {
45
48
  }));
46
49
  }
47
50
 
51
+ function tickEvent(key, at) {
52
+ let newrecord = {...Message$ReventlessCore.generateMeta(Clock$Reventless.name, undefined, undefined, undefined, undefined, undefined, undefined, undefined)};
53
+ return Object.fromEntries([
54
+ [
55
+ "id",
56
+ key
57
+ ],
58
+ [
59
+ "meta",
60
+ Util_Sury$Reventless.toJson((newrecord.time = new Date(at).toISOString(), newrecord), Message$Reventless.metaSchema)
61
+ ],
62
+ [
63
+ "event",
64
+ Util_Sury$Reventless.toJson({
65
+ TAG: "Tick",
66
+ key: key,
67
+ at: at
68
+ }, Clock$Reventless.eventSchema)
69
+ ]
70
+ ]);
71
+ }
72
+
48
73
  function Make(Spec) {
49
74
  return Automation => {
50
75
  let todoItems = {};
76
+ let schedules = {};
77
+ let scheduleChanges = {
78
+ contents: []
79
+ };
80
+ let takeScheduleChanges = () => {
81
+ let changes = scheduleChanges.contents;
82
+ scheduleChanges.contents = [];
83
+ return changes;
84
+ };
85
+ let readsClock = Automation.mappings.some(M => M.sourceName === Clock$Reventless.name);
51
86
  let makeMeta = () => Message$ReventlessCore.generateMeta(Spec.targetName, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
87
+ let processingTimeoutMs = 15 * 60 * 1000;
52
88
  let afterFailedAttempt = row => {
53
89
  let retryCount = row.retryCount + 1 | 0;
54
90
  let newrecord = {...row};
@@ -91,6 +127,22 @@ function Make(Spec) {
91
127
  todoItems[id] = newrecord;
92
128
  }));
93
129
  };
130
+ let reschedule = (routed, setBy, recordedAt) => {
131
+ routed.forEach(r => {
132
+ r.rescheduled.forEach(change => {
133
+ if (!readsClock) {
134
+ return Effect.runSync(EffectLogger$ReventlessCore.logError(`AutomationSlice(` + Spec.name + `)`, undefined, `a mapping changed a schedule, but no mapping reads the Clock, so no tick would reach the slice; the change is skipped`));
135
+ }
136
+ let platformChange = AutomationSlice_Schedules$ReventlessCore.apply(schedules, change, setBy, recordedAt);
137
+ if (platformChange.TAG === "Ok") {
138
+ scheduleChanges.contents = scheduleChanges.contents.concat([platformChange._0]);
139
+ return;
140
+ } else {
141
+ return Effect.runSync(EffectLogger$ReventlessCore.logError(`AutomationSlice(` + Spec.name + `)`, undefined, platformChange._0 + `; the change is skipped`));
142
+ }
143
+ });
144
+ });
145
+ };
94
146
  let phase1 = (events, sliceCtx) => {
95
147
  events.forEach(json => {
96
148
  let context = Message$ReventlessCore.decode(json, Message$Reventless.contextSchema);
@@ -106,12 +158,55 @@ function Make(Spec) {
106
158
  let sourceName = context.meta.service;
107
159
  let dict = Stdlib_JSON.Decode.object(json);
108
160
  let eventPayload = dict !== undefined ? Stdlib_Option.getOr(dict["event"], json) : json;
109
- Route.route(eventPayload, sourceName, context.id, ctx).forEach(apply);
161
+ let routed = Route.route(eventPayload, sourceName, context.id, ctx);
162
+ routed.forEach(apply);
163
+ reschedule(routed, context.meta.msgId, Stdlib_Option.getOr(ctx.recordedAt, Date.now()));
164
+ if (sourceName === Clock$Reventless.name) {
165
+ return AutomationSlice_Schedules$ReventlessCore.fired(schedules, context.id);
166
+ }
110
167
  });
111
168
  };
169
+ let rebuildSchedules = events => {
170
+ Object.keys(schedules).forEach(key => Stdlib_Dict.$$delete(schedules, key));
171
+ events.forEach(json => {
172
+ let context = Message$ReventlessCore.decode(json, Message$Reventless.contextSchema);
173
+ let dict = Stdlib_JSON.Decode.object(json);
174
+ let eventPayload = dict !== undefined ? Stdlib_Option.getOr(dict["event"], json) : json;
175
+ let recordedAt = Stdlib_Option.getOr(recordedAtOf(context.meta.time), Date.now());
176
+ reschedule(Route.route(eventPayload, context.meta.service, context.id, {
177
+ environment: "",
178
+ platformName: "",
179
+ pluginName: "",
180
+ sliceName: Spec.name,
181
+ recordedAt: recordedAt
182
+ }), context.meta.msgId, recordedAt);
183
+ });
184
+ scheduleChanges.contents = [];
185
+ };
112
186
  let phase2 = async publishJsons => {
113
187
  let currentTime = await Effect.runPromise(Clock.currentTimeMillis);
114
188
  let newlyAbandoned = [];
189
+ Object.entries(todoItems).forEach(param => {
190
+ let row = param[1];
191
+ let id = param[0];
192
+ let match = row.status;
193
+ let match$1 = Stdlib_Option.map(row.processedAt, t => new Date(t).getTime());
194
+ if (match !== "Processing") {
195
+ return;
196
+ }
197
+ if (match$1 === undefined) {
198
+ return;
199
+ }
200
+ if (currentTime - match$1 < processingTimeoutMs) {
201
+ return;
202
+ }
203
+ let updated = afterFailedAttempt(row);
204
+ todoItems[id] = updated;
205
+ if (updated.status === "Abandoned") {
206
+ newlyAbandoned.push(id);
207
+ return;
208
+ }
209
+ });
115
210
  Object.entries(todoItems).forEach(param => {
116
211
  let row = param[1];
117
212
  if (!(row.status === "Failed" && row.retryCount >= Spec.maxRetries)) {
@@ -244,7 +339,10 @@ function Make(Spec) {
244
339
  Spec: Spec,
245
340
  todoItems: todoItems,
246
341
  phase1: phase1,
247
- phase2: phase2
342
+ phase2: phase2,
343
+ schedules: schedules,
344
+ takeScheduleChanges: takeScheduleChanges,
345
+ rebuildSchedules: rebuildSchedules
248
346
  };
249
347
  };
250
348
  }
@@ -253,6 +351,7 @@ export {
253
351
  todoStatusSchema,
254
352
  todoRowSchema,
255
353
  todoRowSchemaFor,
354
+ tickEvent,
256
355
  Make,
257
356
  }
258
357
  /* todoStatusSchema Not a pure module */
@@ -1,4 +1,4 @@
1
- // Decode one source event and run its mappings' `collect`/`resolve`, with no
1
+ // Decode one source event and run its mappings' `collect`/`resolve`/`reschedule`, with no
2
2
  // side effects. Shared by the runtime callback, which applies the result to its
3
3
  // to-do list, and by the GWT sweep, which must route exactly as the runtime does.
4
4
 
@@ -6,6 +6,7 @@
6
6
  type routed<'item> = {
7
7
  collected: array<(string, 'item)>,
8
8
  resolved: option<string>,
9
+ rescheduled: array<Reventless.AutomationSlice.scheduleChange>,
9
10
  }
10
11
 
11
12
  module Make = (
@@ -31,6 +32,7 @@ module Make = (
31
32
  decoder.decode(~eventType, ~data=dataDict)->Option.map(event => {
32
33
  collected: M.collect(event, ~sourceId, ctx),
33
34
  resolved: M.resolve(event),
35
+ rescheduled: M.reschedule(event),
34
36
  })
35
37
  }
36
38
  {sourceName: M.sourceName, handle}
@@ -13,7 +13,8 @@ function Make(Spec) {
13
13
  let match = Message$ReventlessCore.splitMessage(json);
14
14
  return Stdlib_Option.map(decoder.decode(match[0], match[1]), event => ({
15
15
  collected: M.collect(event, sourceId, ctx),
16
- resolved: M.resolve(event)
16
+ resolved: M.resolve(event),
17
+ rescheduled: M.reschedule(event)
17
18
  }));
18
19
  };
19
20
  return {
@@ -0,0 +1,149 @@
1
+ // The schedules of one automation slice: a row per key, kept in the slice's schedules
2
+ // store. The rows are the truth and the platform's schedules follow them. Shared by
3
+ // the runtime and the GWT DSL, so a scenario folds `reschedule` as the runtime does.
4
+
5
+ /** The `setBy` of the row a spec's declared `schedule` writes. */
6
+ let declared = "declared"
7
+
8
+ @schema
9
+ type row = {
10
+ key: string,
11
+ /** The time as the JSON text of `Reventless.Schedule.timeSchema`, an `After` set by an event
12
+ already counted from when the event was recorded. Absent when an event cleared
13
+ the slice-wide key, so a deploy does not put the declared time back. */
14
+ time?: string,
15
+ /** The id of the event that set or cleared it, or `declared`. */
16
+ setBy: string,
17
+ setAt: string,
18
+ }
19
+
20
+ /** What the platform is told after a row changed. */
21
+ type platformChange =
22
+ | Put({key: string, time: Reventless.Schedule.time})
23
+ | Remove({key: string})
24
+
25
+ let encodeTime = (time: Reventless.Schedule.time) =>
26
+ time->Reventless.Util_Sury.toJson(Reventless.Schedule.timeSchema)->JSON.stringify
27
+ let decodeTime = (text: string): Reventless.Schedule.time =>
28
+ text->JSON.parseOrThrow->Reventless.Util_Sury.fromJson(Reventless.Schedule.timeSchema)
29
+ let timeOf = (row: row) => row.time->Option.map(decodeTime)
30
+
31
+ let iso = millis => millis->Date.fromTime->Date.toISOString
32
+
33
+ // The slice-wide key keeps a row saying it was cleared; any other key's row goes.
34
+ let clearRow = (rows: dict<row>, ~key, ~setBy, ~setAt) =>
35
+ if key == Reventless.Clock.defaultKey {
36
+ rows->Dict.set(key, {key, setBy, setAt})
37
+ } else {
38
+ rows->Dict.delete(key)
39
+ }
40
+
41
+ /**
42
+ Applies one change an event made, recorded at `recordedAt`, and returns what the
43
+ platform is told. A time that cannot fire is refused with every problem it has, and
44
+ changes nothing.
45
+ */
46
+ let apply = (
47
+ rows: dict<row>,
48
+ change: Reventless.AutomationSlice.scheduleChange,
49
+ ~setBy,
50
+ ~recordedAt,
51
+ ): result<platformChange, string> =>
52
+ switch change {
53
+ | Set({key, time: wanted}) =>
54
+ switch ScheduleTime.errors(wanted) {
55
+ | [] =>
56
+ let resolved = wanted->ScheduleTime.resolveOnce(~from=recordedAt)
57
+ rows->Dict.set(key, {key, time: encodeTime(resolved), setBy, setAt: iso(recordedAt)})
58
+ Ok(Put({key, time: resolved}))
59
+ | errs => Error(`schedule ${key}: ${errs->Array.join("; ")}`)
60
+ }
61
+ | Clear({key: cleared}) =>
62
+ clearRow(rows, ~key=cleared, ~setBy, ~setAt=iso(recordedAt))
63
+ Ok(Remove({key: cleared}))
64
+ }
65
+
66
+ /** A tick of `key` arrived: a once-only schedule has fired, so its row goes. */
67
+ let fired = (rows: dict<row>, ~key) =>
68
+ rows
69
+ ->Dict.get(key)
70
+ ->Option.forEach(row =>
71
+ switch row->timeOf {
72
+ | Some(Once(_)) => clearRow(rows, ~key, ~setBy=row.setBy, ~setAt=row.setAt)
73
+ | _ => ()
74
+ }
75
+ )
76
+
77
+ /**
78
+ Brings the spec's declared time into the rows under `Reventless.Clock.defaultKey`: written when
79
+ no event has set or cleared that key, replaced when the declaration changed, and
80
+ removed when the spec no longer declares one.
81
+ */
82
+ let declare = (rows: dict<row>, declaredTime: option<Reventless.Schedule.time>, ~now) => {
83
+ let key = Reventless.Clock.defaultKey
84
+ let write = time =>
85
+ rows->Dict.set(key, {key, time: encodeTime(time), setBy: declared, setAt: iso(now)})
86
+ switch (declaredTime, rows->Dict.get(key)) {
87
+ | (Some(time), None) => write(time)
88
+ | (Some(time), Some(row)) if row.setBy == declared && row.time != Some(encodeTime(time)) =>
89
+ write(time)
90
+ | (None, Some(row)) if row.setBy == declared => rows->Dict.delete(key)
91
+ | _ => ()
92
+ }
93
+ }
94
+
95
+ // A once-only time already past fires no more.
96
+ let isPast = (time: Reventless.Schedule.time, ~now) =>
97
+ switch time {
98
+ | Once(_) => time->ScheduleTime.nextFiring(~after=now)->Option.isNone
99
+ | _ => false
100
+ }
101
+
102
+ /**
103
+ Brings the platform in line with the rows, after the declared time is brought in: a
104
+ schedule missing from the platform is put, the declared one is put again in case its
105
+ time changed, and one the rows do not hold is removed. A once-only time already past
106
+ is not put again, and its row goes. A row an event changed reached the platform when
107
+ it changed, so it is not put again.
108
+ */
109
+ let reconcile = async (
110
+ rows: dict<row>,
111
+ platform: ReventlessInfra.AutomationSlice.platformSchedules,
112
+ ~declaredTime,
113
+ ~now,
114
+ ) => {
115
+ declare(rows, declaredTime, ~now)
116
+ let live =
117
+ rows
118
+ ->Dict.valuesToArray
119
+ ->Array.filterMap(row =>
120
+ switch row->timeOf {
121
+ | Some(time) if isPast(time, ~now) =>
122
+ clearRow(rows, ~key=row.key, ~setBy=row.setBy, ~setAt=row.setAt)
123
+ None
124
+ | Some(time) => Some((row, time))
125
+ | None => None
126
+ }
127
+ )
128
+ let held = await platform.names()
129
+ for i in 0 to live->Array.length - 1 {
130
+ let (row, time) = live->Array.getUnsafe(i)
131
+ if row.setBy == declared || !(held->Array.includes(platform.nameOf(row.key))) {
132
+ await platform.put(~key=row.key, time)
133
+ }
134
+ }
135
+ let wanted = live->Array.map(((row, _)) => platform.nameOf(row.key))
136
+ let extra = held->Array.filter(name => !(wanted->Array.includes(name)))
137
+ for i in 0 to extra->Array.length - 1 {
138
+ await platform.removeName(extra->Array.getUnsafe(i))
139
+ }
140
+ }
141
+
142
+ /** Tells the platform what the rows' changes were, in order. */
143
+ let tell = async (platform: ReventlessInfra.AutomationSlice.platformSchedules, changes) =>
144
+ for i in 0 to changes->Array.length - 1 {
145
+ switch changes->Array.getUnsafe(i) {
146
+ | Put({key, time}) => await platform.put(~key, time)
147
+ | Remove({key}) => await platform.removeName(platform.nameOf(key))
148
+ }
149
+ }
@@ -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)) => {
@@ -1144,6 +1152,21 @@ module Make = (
1144
1152
  }
1145
1153
  })
1146
1154
 
1155
+ // A scheduled slice's schedules: when it runs, and who set it.
1156
+ let scheduleEntries = automationSlices->Array.filterMap((module(A: AutomationSlice.T)) =>
1157
+ A.schedulesDbName->Option.map(queryDbName => {
1158
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName)
1159
+ {
1160
+ ReventlessInfra.Api.singleFieldName: qn.singleFieldName,
1161
+ listFieldName: qn.listFieldName,
1162
+ returnTypeName: qn.returnTypeName,
1163
+ stateSchema: AutomationSlice_Schedules.rowSchema->S.castToUnknown,
1164
+ authorization: None,
1165
+ connectionSpec: true,
1166
+ }
1167
+ })
1168
+ )
1169
+
1147
1170
  let outboundEntries =
1148
1171
  outboundTranslationSlices->Array.map((module(O: OutboundTranslationSlice.T)) => {
1149
1172
  let qn = Api_Naming.queryFieldNamesForSliceQueryDb(
@@ -1234,6 +1257,7 @@ module Make = (
1234
1257
  mutationEntries: Array.concat(mutationEntriesFromSlices, mutationEntriesFromInboundSlices),
1235
1258
  queryEntries: stateViewEntries
1236
1259
  ->Array.concat(automationEntries)
1260
+ ->Array.concat(scheduleEntries)
1237
1261
  ->Array.concat(outboundEntries)
1238
1262
  ->Array.concat(inboundEntries),
1239
1263
  eventLogEntries: if allProducedSchemas->Array.length > 0 {
@@ -8,6 +8,7 @@ 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";
@@ -25,6 +26,7 @@ import * as Plugin_Structure$ReventlessCore from "../../plugin/component/Plugin_
25
26
  import * as DcbEventLog_Builder$ReventlessCore from "../DcbEventLog/DcbEventLog_Builder.res.mjs";
26
27
  import * as CommandTopic_Builder$ReventlessCore from "../CommandTopic/CommandTopic_Builder.res.mjs";
27
28
  import * as AutomationSlice_Callback$ReventlessCore from "../AutomationSlice/AutomationSlice_Callback.res.mjs";
29
+ import * as AutomationSlice_Schedules$ReventlessCore from "../AutomationSlice/AutomationSlice_Schedules.res.mjs";
28
30
  import * as CommandGenerator_Callback$ReventlessCore from "../CommandGenerator/CommandGenerator_Callback.res.mjs";
29
31
  import * as InboundTranslationSlice_Callback$ReventlessCore from "../InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs";
30
32
  import * as OutboundTranslationSlice_Callback$ReventlessCore from "../OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
@@ -150,10 +152,16 @@ function Make(DcbEventLogStorage) {
150
152
  ]).concat(stateViewSlices.map(V => [
151
153
  V.Spec.name,
152
154
  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 => [
155
+ ])).concat(automationSlices.flatMap(A => Stdlib_Array.filterMap(A.Automation.mappings, M => {
156
+ if (M.sourceName === Clock$Reventless.name) {
157
+ return;
158
+ } else {
159
+ return [
160
+ A.Spec.name,
161
+ M.sourceEventSchema
162
+ ];
163
+ }
164
+ }))).concat(outboundTranslationSlices.map(O => [
157
165
  O.Spec.name,
158
166
  O.Spec.consumedEventSchema
159
167
  ]));
@@ -352,6 +360,9 @@ function Make(DcbEventLogStorage) {
352
360
  automationSlices.forEach(A => {
353
361
  let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, A.queryDbName, undefined);
354
362
  Plugin_Helpers$ReventlessCore.queryFieldNamesRegistry[A.queryDbName] = qn;
363
+ Stdlib_Option.forEach(A.schedulesDbName, queryDbName => {
364
+ Plugin_Helpers$ReventlessCore.queryFieldNamesRegistry[queryDbName] = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, queryDbName, undefined);
365
+ });
355
366
  });
356
367
  outboundTranslationSlices.forEach(O => {
357
368
  let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, O.queryDbName, undefined);
@@ -612,6 +623,17 @@ function Make(DcbEventLogStorage) {
612
623
  connectionSpec: true
613
624
  };
614
625
  });
626
+ let scheduleEntries = Stdlib_Array.filterMap(automationSlices, A => Stdlib_Option.map(A.schedulesDbName, queryDbName => {
627
+ let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, queryDbName, undefined);
628
+ return {
629
+ singleFieldName: qn.singleFieldName,
630
+ listFieldName: qn.listFieldName,
631
+ returnTypeName: qn.returnTypeName,
632
+ stateSchema: AutomationSlice_Schedules$ReventlessCore.rowSchema,
633
+ authorization: undefined,
634
+ connectionSpec: true
635
+ };
636
+ }));
615
637
  let outboundEntries = outboundTranslationSlices.map(O => {
616
638
  let qn = Api_Naming$ReventlessCore.queryFieldNamesForSliceQueryDb(name, O.queryDbName, undefined);
617
639
  return {
@@ -655,7 +677,7 @@ function Make(DcbEventLogStorage) {
655
677
  dcbPublishJsons: publishJsons,
656
678
  dcbCommandTopicQueueUrl: dcbCommandTopicQueueUrl,
657
679
  mutationEntries: mutationEntriesFromSlices.concat(mutationEntriesFromInboundSlices),
658
- queryEntries: stateViewEntries.concat(automationEntries).concat(outboundEntries).concat(inboundEntries),
680
+ queryEntries: stateViewEntries.concat(automationEntries).concat(scheduleEntries).concat(outboundEntries).concat(inboundEntries),
659
681
  eventLogEntries: allProducedSchemas.length !== 0 ? [{
660
682
  busKey: name + "DcbEventLog",
661
683
  displayName: name,