@reventlessdev/reventless-aws 3.0.0-alpha.397 → 3.0.0-alpha.399
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 +41 -0
- package/package.json +9 -9
- package/scripts/BakeManifest.res +15 -15
- package/scripts/DeployApp.res +8 -9
- package/scripts/DeployApp.res.mjs +4 -13
- package/scripts/DeployManifest.res +4 -5
- package/scripts/DeployManifest.res.mjs +0 -1
- package/scripts/ProvisionAccounts.res +41 -33
- package/scripts/ProvisionAccounts.res.mjs +6 -9
- package/scripts/ProvisionAdmin.res +10 -10
- package/scripts/ProvisionIdentity.res +2 -2
- package/scripts/ResolveEnvironment.res +3 -3
- package/src/Platform.res +6 -4
- package/src/Platform.res.mjs +6 -4
- package/src/Platform_ProvidedGroups.res +4 -4
- package/src/Platform_Stack.res +2 -3
- package/src/Platform_Stack.res.mjs +1 -2
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +2 -2
- package/src/adapter/Api/AppSync_EventsApi.res +2 -2
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +6 -6
- package/src/adapter/DcbEventLog/DcbBackend.res +3 -3
- package/src/adapter/DcbEventLog/DcbBackend.res.mjs +1 -3
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +11 -9
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs +4 -6
- package/src/adapter/EventLog/EventLogBackend.res +3 -3
- package/src/adapter/EventLog/EventLogBackend.res.mjs +1 -3
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Backend.res +1 -1
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +5 -5
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +2 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +2 -2
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +32 -32
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +4 -3
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +6 -6
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -5
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +3 -3
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +5 -5
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +24 -18
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +16 -14
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +2 -2
- package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +4 -4
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +4 -4
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +9 -9
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +11 -9
- package/src/components/Api/AppSync_Adapter.res +4 -4
- package/src/components/InboundTranslationSlice_Builder.res +19 -0
- package/src/components/InboundTranslationSlice_Builder.res.mjs +12 -2
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +4 -7
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -4
- package/src/plugin/runtime/PluginRuntime_Builder.res +17 -26
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +7 -22
- package/tests/AppSyncEventsSigner_OpsTest.res +1 -1
- package/tests/AppSync_AdapterTest.res +4 -4
- package/tests/AppSync_AdapterTest.res.mjs +4 -8
- package/tests/Auth_ActiveRoleStoreTest.res +1 -1
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res +1 -1
- package/tests/AutomationSliceEntryPoint_OpsTest.res +0 -2
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +3 -6
- package/tests/BakeConvergenceTest.res +1 -8
- package/tests/BakeConvergenceTest.res.mjs +3 -23
- package/tests/BakeManifestTest.res +5 -10
- package/tests/BakeManifestTest.res.mjs +1 -6
- package/tests/CommandLineTest.res +3 -3
- package/tests/DeployAppTest.res +3 -4
- package/tests/DeployAppTest.res.mjs +6 -15
- package/tests/EpInboundTestSlice.res +1 -0
- package/tests/EpInboundTestSlice.res.mjs +3 -0
- package/tests/EventCollectorPluginDefinitionAssetTest.res +1 -2
- package/tests/EventCollectorPluginDefinitionAssetTest.res.mjs +11 -21
- package/tests/IdentityProvider_Cognito_BackendTest.res +0 -3
- package/tests/IdentityProvider_Cognito_BackendTest.res.mjs +0 -3
- package/tests/PgQueryResolver_LambdaTest.res +6 -10
- package/tests/PgQueryResolver_LambdaTest.res.mjs +4 -34
- package/tests/Platform_ProvidedGroupsTest.res +2 -2
- package/tests/Platform_ProvidedGroupsTest.res.mjs +1 -2
- package/tests/ProvisionAccountsTest.res +5 -10
- package/tests/ProvisionAccountsTest.res.mjs +0 -9
- package/tests/ProvisionAdminTest.res +4 -9
- package/tests/ProvisionAdminTest.res.mjs +0 -7
- package/tests/ProvisionIdentityTest.res +2 -4
- package/tests/ProvisionIdentityTest.res.mjs +0 -2
- package/tests/SideEffectEntryPoint_OpsTest.res +2 -2
- package/tests/Util_ShellConfigTest.res +3 -3
- package/tests/Util_ShellConfigTest.res.mjs +1 -2
package/src/Platform.res.mjs
CHANGED
|
@@ -334,8 +334,7 @@ function MakeWithConfig(Config) {
|
|
|
334
334
|
};
|
|
335
335
|
domainEventsApiOpt = {
|
|
336
336
|
name: "DomainEventsApi",
|
|
337
|
-
api: api$2
|
|
338
|
-
defaultNamespace: undefined
|
|
337
|
+
api: api$2
|
|
339
338
|
};
|
|
340
339
|
} else {
|
|
341
340
|
let cognitoPool = Platform_Stack$ReventlessAws.resolveCognitoUserPool();
|
|
@@ -1505,6 +1504,8 @@ function MakeWithConfig(Config) {
|
|
|
1505
1504
|
return `\n Messaging is \`Capability_Messaging.make\`, passed to the platform as \`~messagingSender\`. The sender itself is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or \`platform:messagingEmailProvider: log\` to log every message and send none.`;
|
|
1506
1505
|
case "IdentityProvider" :
|
|
1507
1506
|
return `\n IdentityProvider is the platform's Cognito pool, exported as \`identityProviderId\`. This platform stack exports none — deploy it first.`;
|
|
1507
|
+
case "Stash" :
|
|
1508
|
+
return `\n Stash has no deployed backend yet; the local platform keeps one in memory. Host a plugin that needs it on the local platform.`;
|
|
1508
1509
|
}
|
|
1509
1510
|
}))).join(""));
|
|
1510
1511
|
}
|
|
@@ -1768,8 +1769,7 @@ function Make($star) {
|
|
|
1768
1769
|
};
|
|
1769
1770
|
domainEventsApiOpt = {
|
|
1770
1771
|
name: "DomainEventsApi",
|
|
1771
|
-
api: api$2
|
|
1772
|
-
defaultNamespace: undefined
|
|
1772
|
+
api: api$2
|
|
1773
1773
|
};
|
|
1774
1774
|
} else {
|
|
1775
1775
|
let cognitoPool = Platform_Stack$ReventlessAws.resolveCognitoUserPool();
|
|
@@ -2914,6 +2914,8 @@ function Make($star) {
|
|
|
2914
2914
|
return `\n Messaging is \`Capability_Messaging.make\`, passed to the platform as \`~messagingSender\`. The sender itself is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or \`platform:messagingEmailProvider: log\` to log every message and send none.`;
|
|
2915
2915
|
case "IdentityProvider" :
|
|
2916
2916
|
return `\n IdentityProvider is the platform's Cognito pool, exported as \`identityProviderId\`. This platform stack exports none — deploy it first.`;
|
|
2917
|
+
case "Stash" :
|
|
2918
|
+
return `\n Stash has no deployed backend yet; the local platform keeps one in memory. Host a plugin that needs it on the local platform.`;
|
|
2917
2919
|
}
|
|
2918
2920
|
}))).join(""));
|
|
2919
2921
|
}
|
|
@@ -8,7 +8,7 @@ read outside any `Output.apply`.
|
|
|
8
8
|
|
|
9
9
|
module Cognito = AwsSdk.CognitoIdentityServiceProvider
|
|
10
10
|
|
|
11
|
-
type pool = {id: string, region
|
|
11
|
+
type pool = {id: string, region?: string}
|
|
12
12
|
|
|
13
13
|
type send = Cognito.ListGroupsCommand.input => promise<Cognito.ListGroupsCommand.output>
|
|
14
14
|
|
|
@@ -39,7 +39,7 @@ let poolFromExports = (~get: string => option<JSON.t>): option<pool> => {
|
|
|
39
39
|
->Option.orElse(read("cognitoUserPoolId"))
|
|
40
40
|
->Option.map(id => {
|
|
41
41
|
id,
|
|
42
|
-
region: read("identityProviderRegion")->Option.orElse(read("cognitoRegion")),
|
|
42
|
+
region: ?(read("identityProviderRegion")->Option.orElse(read("cognitoRegion"))),
|
|
43
43
|
})
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -72,7 +72,7 @@ let resolvePool = async (~stackRef: option<Pulumi.StackReference.t>): option<poo
|
|
|
72
72
|
~cfg=Pulumi.Config.make(Some("platform")),
|
|
73
73
|
)->Option.map(id => {
|
|
74
74
|
id,
|
|
75
|
-
region: Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region"),
|
|
75
|
+
region: ?(Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")),
|
|
76
76
|
})
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -86,7 +86,7 @@ let sendTo = (~region: option<string>): send => {
|
|
|
86
86
|
let load = async (~stackRef: option<Pulumi.StackReference.t>): unit =>
|
|
87
87
|
switch await resolvePool(~stackRef) {
|
|
88
88
|
| None => ()
|
|
89
|
-
| Some({id, region}) =>
|
|
89
|
+
| Some({id, ?region}) =>
|
|
90
90
|
let groups = try await listAll(~send=sendTo(~region), ~userPoolId=id) catch {
|
|
91
91
|
| exn if exn->Util_AwsError.hasCode(~code="AccessDeniedException") =>
|
|
92
92
|
JsError.throwWithMessage(
|
package/src/Platform_Stack.res
CHANGED
|
@@ -18,7 +18,7 @@ type cognitoUserPool = {
|
|
|
18
18
|
/** The sign-in attribute this stack created the pool with. `None` in BYO mode:
|
|
19
19
|
the pool's own choice was made elsewhere and no lookup here reads it back,
|
|
20
20
|
so reporting one would be a guess. */
|
|
21
|
-
loginIdentifier
|
|
21
|
+
loginIdentifier?: Auth_LoginIdentifier.t,
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -250,7 +250,6 @@ let _resolveUncached = (): cognitoUserPool => {
|
|
|
250
250
|
managed: false,
|
|
251
251
|
activeRoleTable,
|
|
252
252
|
identityTable,
|
|
253
|
-
loginIdentifier: None,
|
|
254
253
|
}
|
|
255
254
|
|
|
256
255
|
| None =>
|
|
@@ -346,7 +345,7 @@ let _resolveUncached = (): cognitoUserPool => {
|
|
|
346
345
|
managed: true,
|
|
347
346
|
activeRoleTable,
|
|
348
347
|
identityTable,
|
|
349
|
-
loginIdentifier
|
|
348
|
+
loginIdentifier,
|
|
350
349
|
}
|
|
351
350
|
}
|
|
352
351
|
|
|
@@ -107,8 +107,7 @@ function _resolveUncached() {
|
|
|
107
107
|
poolArn: lookup.apply(r => r.arn),
|
|
108
108
|
managed: false,
|
|
109
109
|
activeRoleTable: activeRoleTable,
|
|
110
|
-
identityTable: identityTable
|
|
111
|
-
loginIdentifier: undefined
|
|
110
|
+
identityTable: identityTable
|
|
112
111
|
};
|
|
113
112
|
} else {
|
|
114
113
|
let loginIdentifier = _loginIdentifier(cfg);
|
|
@@ -23,13 +23,13 @@ let hmacFromBuf = (key: NodeCrypto.buffer, data: string): NodeCrypto.buffer =>
|
|
|
23
23
|
|
|
24
24
|
// ── SigV4 ───────────────────────────────────────────────────────────────────
|
|
25
25
|
|
|
26
|
-
type creds = {accessKeyId: string, secretAccessKey: string, sessionToken
|
|
26
|
+
type creds = {accessKeyId: string, secretAccessKey: string, sessionToken?: string}
|
|
27
27
|
|
|
28
28
|
// Ambient Lambda execution-role credentials (the managed runtime injects these).
|
|
29
29
|
let envCreds = (): creds => {
|
|
30
30
|
accessKeyId: NodeProcess.env->Dict.get("AWS_ACCESS_KEY_ID")->Option.getOr(""),
|
|
31
31
|
secretAccessKey: NodeProcess.env->Dict.get("AWS_SECRET_ACCESS_KEY")->Option.getOr(""),
|
|
32
|
-
sessionToken: NodeProcess.env->Dict.get("AWS_SESSION_TOKEN"),
|
|
32
|
+
sessionToken: ?(NodeProcess.env->Dict.get("AWS_SESSION_TOKEN")),
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
let region = (): string => NodeProcess.env->Dict.get("AWS_REGION")->Option.getOr("eu-west-1")
|
|
@@ -80,7 +80,7 @@ type t = {
|
|
|
80
80
|
/** Full AppSync Events API output — carry this through the stack. */
|
|
81
81
|
api: Api.t,
|
|
82
82
|
/** The `default` channel namespace created on the API. */
|
|
83
|
-
defaultNamespace
|
|
83
|
+
defaultNamespace?: ChannelNamespace.t,
|
|
84
84
|
/** The client-publishable `client` namespace. Absent on plugin-stack
|
|
85
85
|
phantoms (namespaces live on the platform stack's API). */
|
|
86
86
|
clientNamespace?: ChannelNamespace.t,
|
|
@@ -202,7 +202,7 @@ let make = (
|
|
|
202
202
|
~opts=Some({...opts, parent: api->Pulumi.Resource.makeFromJs}),
|
|
203
203
|
)
|
|
204
204
|
|
|
205
|
-
{name, api, defaultNamespace
|
|
205
|
+
{name, api, defaultNamespace, clientNamespace}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
/** HTTP endpoint URL for use as Lambda `APPSYNC_ENDPOINT` env var.
|
|
@@ -334,9 +334,9 @@ let bakeSelection = (json: JSON.t): option<ReventlessCore.Platform_BakedManifest
|
|
|
334
334
|
->Option.flatMap(JSON.Decode.string)
|
|
335
335
|
->Option.map(plugin => {
|
|
336
336
|
ReventlessCore.Platform_BakedManifest.plugin,
|
|
337
|
-
views: strings(o, "views"),
|
|
338
|
-
commands: strings(o, "commands"),
|
|
339
|
-
derived: strings(o, "derived"),
|
|
337
|
+
views: ?strings(o, "views"),
|
|
338
|
+
commands: ?strings(o, "commands"),
|
|
339
|
+
derived: ?strings(o, "derived"),
|
|
340
340
|
})
|
|
341
341
|
)
|
|
342
342
|
}
|
|
@@ -502,8 +502,8 @@ type registrationState =
|
|
|
502
502
|
type registration = {
|
|
503
503
|
plugin: string,
|
|
504
504
|
expected: string,
|
|
505
|
-
found
|
|
506
|
-
writtenAt
|
|
505
|
+
found?: string,
|
|
506
|
+
writtenAt?: string,
|
|
507
507
|
state: registrationState,
|
|
508
508
|
}
|
|
509
509
|
|
|
@@ -550,7 +550,7 @@ let registrations = (
|
|
|
550
550
|
let row = current->Dict.get(plugin)
|
|
551
551
|
let found = row->Option.map(((key, _)) => key)
|
|
552
552
|
let writtenAt = row->Option.flatMap(((_, at)) => at)
|
|
553
|
-
{plugin, expected, found, writtenAt, state: classify(~expected, ~found, ~writtenAt, ~since)}
|
|
553
|
+
{plugin, expected, ?found, ?writtenAt, state: classify(~expected, ~found, ~writtenAt, ~since)}
|
|
554
554
|
})
|
|
555
555
|
}
|
|
556
556
|
|
|
@@ -53,8 +53,8 @@ let isPostgres = (): bool => selectionRef.contents->Option.isSome
|
|
|
53
53
|
type relayLogEntry = {
|
|
54
54
|
logName: string,
|
|
55
55
|
partitionTag: Reventless.DcbTag.derivedPartitionTag,
|
|
56
|
-
mutable collectorQueueUrl
|
|
57
|
-
mutable collectorQueueArn
|
|
56
|
+
mutable collectorQueueUrl?: Pulumi.Output.t<string>,
|
|
57
|
+
mutable collectorQueueArn?: Pulumi.Output.t<string>,
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
let relayLogs: array<relayLogEntry> = []
|
|
@@ -62,7 +62,7 @@ let relayLogs: array<relayLogEntry> = []
|
|
|
62
62
|
/** Step 1: record a Postgres DCB log and its partition tag (from the storage maker). */
|
|
63
63
|
let registerRelayLog = (~logName: string, ~partitionTag: Reventless.DcbTag.derivedPartitionTag) =>
|
|
64
64
|
relayLogs
|
|
65
|
-
->Array.push({logName, partitionTag
|
|
65
|
+
->Array.push({logName, partitionTag})
|
|
66
66
|
->ignore
|
|
67
67
|
|
|
68
68
|
/** Step 2: attach a plugin's EventCollector SQS queue to its DCB log's relay entry. */
|
|
@@ -265,9 +265,9 @@ let queryByCompositeTags = async (
|
|
|
265
265
|
|
|
266
266
|
// Filter clause shared by both scan paths.
|
|
267
267
|
type scanFilter = {
|
|
268
|
-
filterExpression
|
|
269
|
-
expressionAttributeNames
|
|
270
|
-
expressionAttributeValues
|
|
268
|
+
filterExpression?: string,
|
|
269
|
+
expressionAttributeNames?: Dict.t<string>,
|
|
270
|
+
expressionAttributeValues?: Dict.t<JSON.t>,
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
// Builds the scan FilterExpression. Always asserts `attribute_exists(event)` so
|
|
@@ -294,9 +294,9 @@ let buildScanFilter = (~eventTypes: option<array<string>>=?): scanFilter => {
|
|
|
294
294
|
|
|
295
295
|
let hasAttributeValues = expressionAttributeValues->Dict.keysToArray->Array.length > 0
|
|
296
296
|
{
|
|
297
|
-
filterExpression:
|
|
298
|
-
expressionAttributeNames
|
|
299
|
-
expressionAttributeValues: hasAttributeValues ? Some(expressionAttributeValues) : None,
|
|
297
|
+
filterExpression: filterParts->Array.join(" AND "),
|
|
298
|
+
expressionAttributeNames,
|
|
299
|
+
expressionAttributeValues: ?(hasAttributeValues ? Some(expressionAttributeValues) : None),
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
|
@@ -523,7 +523,7 @@ let deduplicateByPosition = (
|
|
|
523
523
|
|
|
524
524
|
type mergeSlot = {
|
|
525
525
|
queue: Queue.t<option<ReventlessCore.DcbEventLog_Adapter.rawSequencedEvent>>,
|
|
526
|
-
head
|
|
526
|
+
head?: ReventlessCore.DcbEventLog_Adapter.rawSequencedEvent,
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
// Wraps a sub-stream in a Queue(1) producer fiber and pulls the first element.
|
|
@@ -539,7 +539,9 @@ let initSlot = (
|
|
|
539
539
|
// unhandled-fiber-failure warning).
|
|
540
540
|
->Effect.ensuring(Queue.offer(queue, None)->Effect.map(_ => ()))
|
|
541
541
|
->Effect.catchAll(_ => Effect.succeed())
|
|
542
|
-
Effect.fork(producer)->Effect.flatMap(_ =>
|
|
542
|
+
Effect.fork(producer)->Effect.flatMap(_ =>
|
|
543
|
+
Queue.take(queue)->Effect.map(head => {queue, ?head})
|
|
544
|
+
)
|
|
543
545
|
})
|
|
544
546
|
|
|
545
547
|
// Returns the index of the slot with the smallest position, or None if all exhausted.
|
|
@@ -596,7 +598,7 @@ let mergeSortedEvents = (
|
|
|
596
598
|
let newSlots = slots->Array.mapWithIndex(
|
|
597
599
|
(s, i) =>
|
|
598
600
|
if i == idx {
|
|
599
|
-
{...s, head: nextHead}
|
|
601
|
+
{...s, head: ?nextHead}
|
|
600
602
|
} else {
|
|
601
603
|
s
|
|
602
604
|
},
|
|
@@ -468,14 +468,12 @@ function mergeSortedEvents(streams) {
|
|
|
468
468
|
let emitEvent = Stdlib_Option.getOrThrow(slot.head, undefined);
|
|
469
469
|
return Effect$1.map(Queue.take(slot.queue), nextHead => {
|
|
470
470
|
let newSlots = slots.map((s, i) => {
|
|
471
|
-
if (i
|
|
472
|
-
return {
|
|
473
|
-
queue: s.queue,
|
|
474
|
-
head: nextHead
|
|
475
|
-
};
|
|
476
|
-
} else {
|
|
471
|
+
if (i !== idx) {
|
|
477
472
|
return s;
|
|
478
473
|
}
|
|
474
|
+
let newrecord = {...s};
|
|
475
|
+
newrecord.head = nextHead;
|
|
476
|
+
return newrecord;
|
|
479
477
|
});
|
|
480
478
|
let hasActive = newSlots.some(s => Stdlib_Option.isSome(s.head));
|
|
481
479
|
return [
|
|
@@ -53,8 +53,8 @@ let isPostgres = (): bool => selectionRef.contents->Option.isSome
|
|
|
53
53
|
type relayLogEntry = {
|
|
54
54
|
logName: string,
|
|
55
55
|
aggregateName: string,
|
|
56
|
-
mutable collectorQueueUrl
|
|
57
|
-
mutable collectorQueueArn
|
|
56
|
+
mutable collectorQueueUrl?: Pulumi.Output.t<string>,
|
|
57
|
+
mutable collectorQueueArn?: Pulumi.Output.t<string>,
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
let relayLogs: array<relayLogEntry> = []
|
|
@@ -62,7 +62,7 @@ let relayLogs: array<relayLogEntry> = []
|
|
|
62
62
|
/** Step 1: record a Postgres classic log (from the storage maker). */
|
|
63
63
|
let registerRelayLog = (~logName: string, ~aggregateName: string) =>
|
|
64
64
|
relayLogs
|
|
65
|
-
->Array.push({logName, aggregateName
|
|
65
|
+
->Array.push({logName, aggregateName})
|
|
66
66
|
->ignore
|
|
67
67
|
|
|
68
68
|
/** Step 2: attach a plugin's EventCollector SQS queue to one aggregate's relay entry. */
|
|
@@ -23,9 +23,7 @@ let relayLogs = [];
|
|
|
23
23
|
function registerRelayLog(logName, aggregateName) {
|
|
24
24
|
relayLogs.push({
|
|
25
25
|
logName: logName,
|
|
26
|
-
aggregateName: aggregateName
|
|
27
|
-
collectorQueueUrl: undefined,
|
|
28
|
-
collectorQueueArn: undefined
|
|
26
|
+
aggregateName: aggregateName
|
|
29
27
|
});
|
|
30
28
|
}
|
|
31
29
|
|
|
@@ -57,7 +57,7 @@ let search = async (~indexName: string, ~text: string, ~maxResults: int=5): resu
|
|
|
57
57
|
Some({
|
|
58
58
|
Reventless.Geocoding.label: place.label->Option.getOr(""),
|
|
59
59
|
point,
|
|
60
|
-
relevance: r.relevance,
|
|
60
|
+
relevance: ?r.relevance,
|
|
61
61
|
})
|
|
62
62
|
// A coordinate that fails the range check is dropped rather than
|
|
63
63
|
// propagated: it cannot be stored, and one bad row should not lose
|
|
@@ -89,7 +89,7 @@ type binding = {
|
|
|
89
89
|
ops: ReventlessCore.QueryDb_Adapter.operations,
|
|
90
90
|
pushdowns: pushdowns,
|
|
91
91
|
indexes: array<Reventless.ReadModel.indexConfig>,
|
|
92
|
-
subIdField
|
|
92
|
+
subIdField?: string,
|
|
93
93
|
capability: ReventlessCore.GraphQL_FragmentGenerator.serverCapability,
|
|
94
94
|
labelField: string,
|
|
95
95
|
includeIdParam: bool,
|
|
@@ -97,9 +97,9 @@ type binding = {
|
|
|
97
97
|
/** The state's `@owner` field, when it declares one. Derived at Lambda init
|
|
98
98
|
from the same schema `capability` comes from, so the two cannot disagree
|
|
99
99
|
about which fields this read model has. */
|
|
100
|
-
ownerField
|
|
101
|
-
retiredField
|
|
102
|
-
retiredValues
|
|
100
|
+
ownerField?: string,
|
|
101
|
+
retiredField?: string,
|
|
102
|
+
retiredValues?: array<string>,
|
|
103
103
|
/** Whether a reference to a retired row of this read model still resolves its
|
|
104
104
|
name — the `@namedWhenRetired` opt-in. Read only by the reference door;
|
|
105
105
|
every other door narrows as it always did. */
|
|
@@ -107,7 +107,7 @@ type binding = {
|
|
|
107
107
|
/** The field a reference shows this row's picture from, derived from the same
|
|
108
108
|
schema as `capability` by `Reventless.RowImage`. `None` for a view that
|
|
109
109
|
declares no picture, which is most of them. */
|
|
110
|
-
imageSource
|
|
110
|
+
imageSource?: Reventless.RowImage.source,
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// -- arg helpers -------------------------------------------------------------
|
|
@@ -11,7 +11,7 @@ type aggregateInfo = {
|
|
|
11
11
|
specModulePath: string,
|
|
12
12
|
decisionModulePath: string,
|
|
13
13
|
eventLogTableName: Pulumi.Output.t<string>,
|
|
14
|
-
mappingsModulePath
|
|
14
|
+
mappingsModulePath?: string,
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
let aggregateInfos: dict<aggregateInfo> = Dict.make()
|
|
@@ -25,7 +25,7 @@ let registerAggregate = (
|
|
|
25
25
|
) =>
|
|
26
26
|
aggregateInfos->Dict.set(
|
|
27
27
|
aggregateName,
|
|
28
|
-
{specModulePath, decisionModulePath, eventLogTableName, mappingsModulePath},
|
|
28
|
+
{specModulePath, decisionModulePath, eventLogTableName, ?mappingsModulePath},
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
type storedSpec = {
|
|
@@ -15,7 +15,7 @@ type aggregateInfo = {
|
|
|
15
15
|
specModulePath: string,
|
|
16
16
|
decisionModulePath: string,
|
|
17
17
|
eventLogTableName: Pulumi.Output.t<string>,
|
|
18
|
-
mappingsModulePath
|
|
18
|
+
mappingsModulePath?: string,
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
let aggregateInfos: dict<aggregateInfo> = Dict.make()
|
|
@@ -29,7 +29,7 @@ let registerAggregate = (
|
|
|
29
29
|
) =>
|
|
30
30
|
aggregateInfos->Dict.set(
|
|
31
31
|
aggregateName,
|
|
32
|
-
{specModulePath, decisionModulePath, eventLogTableName, mappingsModulePath},
|
|
32
|
+
{specModulePath, decisionModulePath, eventLogTableName, ?mappingsModulePath},
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
type storedSpec = {
|
|
@@ -29,11 +29,11 @@ type handlerEntry = {
|
|
|
29
29
|
/** Whether `dcbQueueUrl` is a FIFO queue. Absent in a config written before
|
|
30
30
|
the flavor travelled with the URL, where `false` — a standard queue — is
|
|
31
31
|
what these command topics actually are. */
|
|
32
|
-
commandQueueIsFifo
|
|
32
|
+
commandQueueIsFifo?: bool,
|
|
33
33
|
sourceUrn: string,
|
|
34
|
-
context
|
|
34
|
+
context?: Reventless.AutomationSlice.context,
|
|
35
35
|
/** The slice's schedules store; absent for a slice that maps no `Clock`. */
|
|
36
|
-
schedulesTableName
|
|
36
|
+
schedulesTableName?: string,
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
|
|
@@ -58,10 +58,10 @@ let decodeEntry = (json: JSON.t): option<handlerEntry> =>
|
|
|
58
58
|
callbackType: h->strOf("callbackType")->Option.getOr("automation"),
|
|
59
59
|
queryDbTableName: h->strOf("queryDbTableName")->Option.getOr(""),
|
|
60
60
|
dcbQueueUrl: h->strOf("dcbQueueUrl")->Option.getOr(""),
|
|
61
|
-
commandQueueIsFifo: h->Dict.get("commandQueueIsFifo")->Option.flatMap(JSON.Decode.bool),
|
|
61
|
+
commandQueueIsFifo: ?(h->Dict.get("commandQueueIsFifo")->Option.flatMap(JSON.Decode.bool)),
|
|
62
62
|
sourceUrn: h->strOf("sourceUrn")->Option.getOr(""),
|
|
63
|
-
context: h->Dict.get("context")->Option.flatMap(decodeContext),
|
|
64
|
-
schedulesTableName: h->strOf("schedulesTableName"),
|
|
63
|
+
context: ?(h->Dict.get("context")->Option.flatMap(decodeContext)),
|
|
64
|
+
schedulesTableName: ?(h->strOf("schedulesTableName")),
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
// A compact entry names its slice and carries only what the deploy resolved;
|
|
@@ -91,15 +91,15 @@ let decodeCompactEntry = (
|
|
|
91
91
|
callbackType: m->strOf("callbackType")->Option.getOr("automation"),
|
|
92
92
|
queryDbTableName: h->strOf("q")->Option.getOr(""),
|
|
93
93
|
dcbQueueUrl: h->strOf("k")->Option.getOr(sharedQueueUrl),
|
|
94
|
-
commandQueueIsFifo: switch h->Dict.get("f")->Option.flatMap(JSON.Decode.bool) {
|
|
94
|
+
commandQueueIsFifo: ?switch h->Dict.get("f")->Option.flatMap(JSON.Decode.bool) {
|
|
95
95
|
| Some(f) => Some(f)
|
|
96
96
|
| None => sharedIsFifo
|
|
97
97
|
},
|
|
98
98
|
// Only the first, which is all the shell registers today. The remaining
|
|
99
99
|
// urns stay in the config for the multi-source registration to use.
|
|
100
100
|
sourceUrn: urns->Array.get(0)->Option.getOr(""),
|
|
101
|
-
context: m->Dict.get("context")->Option.flatMap(decodeContext),
|
|
102
|
-
schedulesTableName: h->strOf("s"),
|
|
101
|
+
context: ?(m->Dict.get("context")->Option.flatMap(decodeContext)),
|
|
102
|
+
schedulesTableName: ?(h->strOf("s")),
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -428,6 +428,9 @@ let capabilities = (): Reventless.Capabilities.t => {
|
|
|
428
428
|
~identityStore=NodeProcess.env->Dict.get("IDENTITY_STORE_TABLE")->Option.getOr(""),
|
|
429
429
|
),
|
|
430
430
|
secrets: ReventlessCore.Secrets_Node_Backend.provider,
|
|
431
|
+
// None deployed yet: a slice declaring the need is refused by the deploy gate,
|
|
432
|
+
// and this answers `Unavailable` should one run regardless.
|
|
433
|
+
stash: Reventless.Capabilities.none.stash,
|
|
431
434
|
}
|
|
432
435
|
|
|
433
436
|
// ── Phase-1/phase-2 pipelines ───────────────────────────────────────────────
|
|
@@ -576,7 +579,7 @@ type builtSlice = {
|
|
|
576
579
|
tick: option<(~key: string, ~at: float) => promise<unit>>,
|
|
577
580
|
/** Brings the slice's schedules in line with its spec and its rows, at a deploy;
|
|
578
581
|
`None` for an outbound slice. */
|
|
579
|
-
reconcile
|
|
582
|
+
reconcile?: unit => promise<unit>,
|
|
580
583
|
}
|
|
581
584
|
|
|
582
585
|
let makeAutomationRegisteredHandler = (
|
|
@@ -651,27 +654,25 @@ let makeAutomationRegisteredHandler = (
|
|
|
651
654
|
await syncTodoItems()
|
|
652
655
|
},
|
|
653
656
|
),
|
|
654
|
-
reconcile:
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
},
|
|
674
|
-
),
|
|
657
|
+
reconcile: async () =>
|
|
658
|
+
switch schedules() {
|
|
659
|
+
| Some({load, sync, platform}) =>
|
|
660
|
+
await load()
|
|
661
|
+
await ReventlessCore.AutomationSlice_Schedules.reconcile(
|
|
662
|
+
callback.schedules,
|
|
663
|
+
platform,
|
|
664
|
+
~declaredTime=schedule,
|
|
665
|
+
~now=Date.now(),
|
|
666
|
+
)
|
|
667
|
+
await sync()
|
|
668
|
+
// No Clock mapping, so no schedule of the slice may stay on the platform.
|
|
669
|
+
| None =>
|
|
670
|
+
let platform = platformSchedulesOf(~slice=sliceName)
|
|
671
|
+
let names = await platform.names()
|
|
672
|
+
for i in 0 to names->Array.length - 1 {
|
|
673
|
+
await platform.removeName(names->Array.getUnsafe(i))
|
|
674
|
+
}
|
|
675
|
+
},
|
|
675
676
|
}
|
|
676
677
|
}
|
|
677
678
|
|
|
@@ -718,7 +719,6 @@ let makeOutboundRegisteredHandler = (
|
|
|
718
719
|
comp,
|
|
719
720
|
name: sliceName,
|
|
720
721
|
tick: None,
|
|
721
|
-
reconcile: None,
|
|
722
722
|
}
|
|
723
723
|
}
|
|
724
724
|
|
|
@@ -13,6 +13,7 @@ import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/compon
|
|
|
13
13
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
14
14
|
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
15
15
|
import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
16
|
+
import * as Capabilities$Reventless from "@reventlessdev/reventless-spec/src/semantic/Capabilities.res.mjs";
|
|
16
17
|
import * as EffectLogger$ReventlessCore from "@reventlessdev/reventless-core/src/util/EffectLogger.res.mjs";
|
|
17
18
|
import * as DynamoDb_Error$ReventlessAws from "../../errors/DynamoDb_Error.res.mjs";
|
|
18
19
|
import * as Messaging_Ses_Backend$ReventlessAws from "../Messaging/Messaging_Ses_Backend.res.mjs";
|
|
@@ -276,7 +277,8 @@ function capabilities() {
|
|
|
276
277
|
geocode: text => Geocoder_AwsLocation_Backend$ReventlessAws.search(Stdlib_Option.getOr(process.env["PLACE_INDEX_NAME"], ""), text, undefined),
|
|
277
278
|
messaging: messagingEmailProvider(),
|
|
278
279
|
identityProvider: IdentityProvider_Cognito_Backend$ReventlessAws.provider(Stdlib_Option.getOr(process.env["IDENTITY_PROVIDER_ID"], ""), Stdlib_Option.getOr(process.env["IDENTITY_STORE_TABLE"], "")),
|
|
279
|
-
secrets: Secrets_Node_Backend$ReventlessCore.provider
|
|
280
|
+
secrets: Secrets_Node_Backend$ReventlessCore.provider,
|
|
281
|
+
stash: Capabilities$Reventless.none.stash
|
|
280
282
|
};
|
|
281
283
|
}
|
|
282
284
|
|
|
@@ -422,8 +424,7 @@ function makeOutboundRegisteredHandler(entry, sliceName, consumedEventSchema, ca
|
|
|
422
424
|
},
|
|
423
425
|
comp: comp,
|
|
424
426
|
name: sliceName,
|
|
425
|
-
tick: undefined
|
|
426
|
-
reconcile: undefined
|
|
427
|
+
tick: undefined
|
|
427
428
|
};
|
|
428
429
|
}
|
|
429
430
|
|
|
@@ -23,7 +23,7 @@ type sliceInfo = {
|
|
|
23
23
|
queryDbResources: array<ReventlessInfra.Adapter.resource>,
|
|
24
24
|
// Threaded to phase 1 collect/resolve on the automation path (mirrors the
|
|
25
25
|
// in-process builder's `~context`); outbound slices take no context.
|
|
26
|
-
context
|
|
26
|
+
context?: Reventless.AutomationSlice.context,
|
|
27
27
|
// The non-DCB topics this slice subscribes to — an Aggregate's EventTopic when
|
|
28
28
|
// it names one by `Spec.name`. Empty for the common DCB-only slice.
|
|
29
29
|
//
|
|
@@ -47,10 +47,10 @@ type sliceInfo = {
|
|
|
47
47
|
// Resolved in the finalizer rather than here: an Aggregate's CommandTopic is
|
|
48
48
|
// created *after* the slices that target it, so a lookup at registration time
|
|
49
49
|
// finds nothing. Only the name is knowable this early.
|
|
50
|
-
commandTargetName
|
|
50
|
+
commandTargetName?: string,
|
|
51
51
|
// The automation's declared time; what the deploy's reconcile puts on the
|
|
52
52
|
// platform, and what re-runs it when it changes.
|
|
53
|
-
schedule
|
|
53
|
+
schedule?: Reventless.Schedule.time,
|
|
54
54
|
// The schedules store's table, empty for a slice that maps no `Clock`. Granted to
|
|
55
55
|
// the Lambda with the to-do tables, and named in the slice's handler config.
|
|
56
56
|
schedulesResources: array<ReventlessInfra.Adapter.resource>,
|
|
@@ -116,11 +116,11 @@ let registerAutomationSlice = (
|
|
|
116
116
|
callbackType,
|
|
117
117
|
queryDbTableName,
|
|
118
118
|
queryDbResources,
|
|
119
|
-
context,
|
|
119
|
+
?context,
|
|
120
120
|
sourceTopics,
|
|
121
121
|
consumesDcbLog,
|
|
122
|
-
commandTargetName,
|
|
123
|
-
schedule,
|
|
122
|
+
?commandTargetName,
|
|
123
|
+
?schedule,
|
|
124
124
|
schedulesResources,
|
|
125
125
|
capabilityNeeds,
|
|
126
126
|
},
|