@reventlessdev/reventless-aws 3.0.0-alpha.339 → 3.0.0-alpha.341
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 +19 -0
- package/package.json +9 -9
- package/src/Platform.res.mjs +4 -4
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +13 -0
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +7 -0
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +7 -0
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +2 -1
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +1 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +3 -1
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +39 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +31 -1
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +29 -3
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +12 -5
- package/src/plugin/runtime/PluginRuntime_Builder.res +7 -1
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +2 -1
- package/tests/AppSync_RetirementNarrowingTest.res.mjs +1 -1
- package/tests/EventCollectorReadyGateTest.res +89 -0
- package/tests/EventCollectorReadyGateTest.res.mjs +81 -0
- package/tests/PgQueryResolver_LambdaTest.res +3 -0
- package/tests/PgQueryResolver_LambdaTest.res.mjs +36 -31
- package/tests/ProjectionEntryPoint_DisplayNameTest.res +96 -0
- package/tests/ProjectionEntryPoint_DisplayNameTest.res.mjs +131 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res +67 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +102 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.341 (2026-09-08)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **aws:** the readiness gate waits for the collector's update, not its record ([c5a1d27](https://github.com/ReventlessDev/reventless-core/commit/c5a1d273be2c499bf1d1297a416d2cf6fce85ccf))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.340 (2026-09-08)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **aws:** a deployed state view slice composes its [@display](https://github.com/display)Name ([05ba7c5](https://github.com/ReventlessDev/reventless-core/commit/05ba7c531e605c5eb8d190a1fcb4f3d99888253d))
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* a slice folder is named for its kind, without the Slice ([a400555](https://github.com/ReventlessDev/reventless-core/commit/a400555121b023823983ebe846e99cda27063c1f))
|
|
21
|
+
* **core:** a reference names a row and shows its picture ([2b40d04](https://github.com/ReventlessDev/reventless-core/commit/2b40d0418bba2bf83463f446e94e372ea0c9898c))
|
|
22
|
+
* **core:** a state view records when it reached each state ([5216f71](https://github.com/ReventlessDev/reventless-core/commit/5216f71b8dcafcb2f0c991923ebcf2438b27e347))
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
# 3.0.0-alpha.339 (2026-09-07)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @reventlessdev/reventless-aws
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.341",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -15,18 +15,18 @@
|
|
|
15
15
|
"@aws-sdk/s3-request-presigner": "3.970.0",
|
|
16
16
|
"sury": "11.0.0-rc.2",
|
|
17
17
|
"uuid": "^13.0.0",
|
|
18
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
19
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
20
18
|
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.15",
|
|
19
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
21
20
|
"@reventlessdev/rescript-node": "2.0.0-alpha.9",
|
|
22
|
-
"@reventlessdev/rescript-
|
|
23
|
-
"@reventlessdev/rescript-
|
|
24
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.258",
|
|
21
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
22
|
+
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.9",
|
|
25
23
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
26
|
-
"@reventlessdev/
|
|
24
|
+
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
25
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.259",
|
|
26
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.160",
|
|
27
27
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.35",
|
|
28
|
-
"@reventlessdev/reventless-
|
|
29
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
28
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.123",
|
|
29
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.132"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"rescript": "12.3.0",
|
package/src/Platform.res.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import * as CapabilityNeed$Reventless from "@reventlessdev/reventless-spec/src/s
|
|
|
30
30
|
import * as PluginSpec$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginSpec.res.mjs";
|
|
31
31
|
import * as Util_Bundle$ReventlessAws from "./util/Util_Bundle.res.mjs";
|
|
32
32
|
import * as Plugin_Stack$ReventlessAws from "./plugin/stack/Plugin_Stack.res.mjs";
|
|
33
|
-
import * as UiFragments$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
33
|
+
import * as UiFragments$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs";
|
|
34
34
|
import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
|
|
35
35
|
import * as Platform_Casts$ReventlessAws from "./Platform_Casts.res.mjs";
|
|
36
36
|
import * as Platform_Stack$ReventlessAws from "./Platform_Stack.res.mjs";
|
|
@@ -61,7 +61,7 @@ import * as Platform_AdminApi$ReventlessCore from "@reventlessdev/reventless-cor
|
|
|
61
61
|
import * as PluginsProjection$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginsProjection.res.mjs";
|
|
62
62
|
import * as StateTopic_AppSync$ReventlessAws from "./adapter/StateTopic/StateTopic_AppSync.res.mjs";
|
|
63
63
|
import * as AppSync_SdlDecorate$ReventlessAws from "./components/Api/AppSync_SdlDecorate.res.mjs";
|
|
64
|
-
import * as UiFragmentRegistry$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
64
|
+
import * as UiFragmentRegistry$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry.res.mjs";
|
|
65
65
|
import * as Util_ResourceNaming$ReventlessAws from "./util/Util_ResourceNaming.res.mjs";
|
|
66
66
|
import * as Auth_ActiveRoleStore$ReventlessAws from "./adapter/Auth/Auth_ActiveRoleStore.res.mjs";
|
|
67
67
|
import * as ClonerRunner_Fargate$ReventlessAws from "./plugin/cloner/ClonerRunner_Fargate.res.mjs";
|
|
@@ -77,7 +77,7 @@ import * as EventTopicPublisher_SNS$ReventlessAws from "./adapter/EventTopic/Eve
|
|
|
77
77
|
import * as ExtensionPointMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionPointMapping.res.mjs";
|
|
78
78
|
import * as PgQueryResolver_Builder$ReventlessAws from "./adapter/QueryDb/PgQueryResolver_Builder.res.mjs";
|
|
79
79
|
import * as Platform_BakedManifest$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_BakedManifest.res.mjs";
|
|
80
|
-
import * as UiFragments_Projection$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
80
|
+
import * as UiFragments_Projection$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments_Projection.res.mjs";
|
|
81
81
|
import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
|
|
82
82
|
import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
|
|
83
83
|
import * as StateChangeSlice_Builder$ReventlessAws from "./components/StateChangeSlice_Builder.res.mjs";
|
|
@@ -89,7 +89,7 @@ import * as PluginExtensionPointSpec$ReventlessInfra from "@reventlessdev/revent
|
|
|
89
89
|
import * as Platform_UIFragments_Lambda$ReventlessAws from "./adapter/Api/Platform_UIFragments_Lambda.res.mjs";
|
|
90
90
|
import * as CommandTopicChannel_SQS_Sync$ReventlessAws from "./adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs";
|
|
91
91
|
import * as EventLogSubscription_AppSync$ReventlessAws from "./adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs";
|
|
92
|
-
import * as UiFragmentRegistry_Behavior$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
92
|
+
import * as UiFragmentRegistry_Behavior$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry_Behavior.res.mjs";
|
|
93
93
|
import * as CommandTopicChannel_SQS_Async$ReventlessAws from "./adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs";
|
|
94
94
|
import * as Geocoder_AwsLocation_Resolver$ReventlessAws from "./adapter/Geocoder/Geocoder_AwsLocation_Resolver.res.mjs";
|
|
95
95
|
import * as Plugin_ExtensionPoint_Builder$ReventlessAws from "./plugin/stack/Plugin_ExtensionPoint_Builder.res.mjs";
|
|
@@ -103,6 +103,10 @@ type binding = {
|
|
|
103
103
|
name — the `@namedWhenRetired` opt-in. Read only by the reference door;
|
|
104
104
|
every other door narrows as it always did. */
|
|
105
105
|
namedWhenRetired: bool,
|
|
106
|
+
/** The field a reference shows this row's picture from, derived from the same
|
|
107
|
+
schema as `capability` by `Reventless.RowImage`. `None` for a view that
|
|
108
|
+
declares no picture, which is most of them. */
|
|
109
|
+
imageSource: option<Reventless.RowImage.source>,
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
// -- arg helpers -------------------------------------------------------------
|
|
@@ -309,6 +313,15 @@ let dispatch = async (
|
|
|
309
313
|
->Option.getOr(id),
|
|
310
314
|
),
|
|
311
315
|
),
|
|
316
|
+
// The picture travels with the name, so a retired row that keeps
|
|
317
|
+
// one keeps the other.
|
|
318
|
+
(
|
|
319
|
+
"image",
|
|
320
|
+
switch (binding.imageSource, item->JSON.Decode.object) {
|
|
321
|
+
| (Some(source), Some(d)) => d->Reventless.RowImage.refFrom(source)
|
|
322
|
+
| _ => None
|
|
323
|
+
}->Option.mapOr(JSON.Encode.null, JSON.Encode.string),
|
|
324
|
+
),
|
|
312
325
|
("retired", JSON.Encode.bool(retired)),
|
|
313
326
|
("retiredState", state->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
314
327
|
])->JSON.Encode.object,
|
|
@@ -5,6 +5,7 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
6
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
7
7
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
8
|
+
import * as RowImage$Reventless from "@reventlessdev/reventless-spec/src/semantic/RowImage.res.mjs";
|
|
8
9
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
9
10
|
import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
|
|
10
11
|
import * as Api_Ids$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/Api_Ids.res.mjs";
|
|
@@ -218,6 +219,8 @@ async function dispatch(binding, lookupBindingOpt, payload) {
|
|
|
218
219
|
}
|
|
219
220
|
let get = key => Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(item), d => d[key]);
|
|
220
221
|
let id = Stdlib_Option.getOr(Stdlib_Option.flatMap(get("id"), Stdlib_JSON.Decode.string), "");
|
|
222
|
+
let match$1 = binding.imageSource;
|
|
223
|
+
let match$2 = Stdlib_JSON.Decode.object(item);
|
|
221
224
|
return Object.fromEntries([
|
|
222
225
|
[
|
|
223
226
|
"id",
|
|
@@ -227,6 +230,10 @@ async function dispatch(binding, lookupBindingOpt, payload) {
|
|
|
227
230
|
"label",
|
|
228
231
|
Stdlib_Option.getOr(Stdlib_Option.flatMap(get(binding.labelField), Stdlib_JSON.Decode.string), id)
|
|
229
232
|
],
|
|
233
|
+
[
|
|
234
|
+
"image",
|
|
235
|
+
Stdlib_Option.mapOr(match$1 !== undefined && match$2 !== undefined ? RowImage$Reventless.refFrom(match$2, match$1) : undefined, null, prim => prim)
|
|
236
|
+
],
|
|
230
237
|
[
|
|
231
238
|
"retired",
|
|
232
239
|
retired
|
|
@@ -548,6 +548,13 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
548
548
|
~retiredField,
|
|
549
549
|
~retiredValues,
|
|
550
550
|
~namedWhenRetired=retiredSpec->Option.mapOr(false, r => r.namedWhenRetired),
|
|
551
|
+
// Derived at deploy time from the same schema every other backend
|
|
552
|
+
// derives it from at runtime — this is the one resolver that is
|
|
553
|
+
// generated source rather than a function, so it takes the read as
|
|
554
|
+
// an expression instead of doing it.
|
|
555
|
+
~imageExpr=?stateSchemaOpt
|
|
556
|
+
->Option.flatMap(Reventless.RowImage.sourceFrom)
|
|
557
|
+
->Option.map(source => Reventless.RowImage.jsExpr(~row="row", source)),
|
|
551
558
|
~ownerField?,
|
|
552
559
|
~elevatedGroups,
|
|
553
560
|
),
|
|
@@ -4,6 +4,7 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
5
|
import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
|
|
6
6
|
import * as Owner$Reventless from "@reventlessdev/reventless-spec/src/components/Owner.res.mjs";
|
|
7
|
+
import * as RowImage$Reventless from "@reventlessdev/reventless-spec/src/semantic/RowImage.res.mjs";
|
|
7
8
|
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
8
9
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
9
10
|
import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
|
|
@@ -181,7 +182,7 @@ function make(name, api, apiRole, dataSourceName, indexes, subIdField, idResolve
|
|
|
181
182
|
if (includeIdParam && subIdField === undefined) {
|
|
182
183
|
let storage$1 = storageResource(undefined, name$1);
|
|
183
184
|
let refsField = fieldNameForAll + "Refs";
|
|
184
|
-
resolverRefs = makeQueryResolver(Stdlib_String.capitalize(refsField), refsField, generateCode(storage$1, AppSync_Resolver_Functions$PulumiAws.refsByIds(labelField, retiredField, retiredValues, Stdlib_Option.mapOr(retiredSpec, false, r => r.namedWhenRetired), ownerField, elevatedGroups)));
|
|
185
|
+
resolverRefs = makeQueryResolver(Stdlib_String.capitalize(refsField), refsField, generateCode(storage$1, AppSync_Resolver_Functions$PulumiAws.refsByIds(labelField, retiredField, retiredValues, Stdlib_Option.mapOr(retiredSpec, false, r => r.namedWhenRetired), Stdlib_Option.map(Stdlib_Option.flatMap(stateSchemaOpt, RowImage$Reventless.sourceFrom), source => RowImage$Reventless.jsExpr("row", source)), ownerField, elevatedGroups)));
|
|
185
186
|
} else {
|
|
186
187
|
resolverRefs = undefined;
|
|
187
188
|
}
|
|
@@ -169,6 +169,7 @@ let registerBinding = (
|
|
|
169
169
|
->Reventless.StateAnnotations.getSpec
|
|
170
170
|
->Option.flatMap(a => a.retired)
|
|
171
171
|
->Option.mapOr(false, r => r.namedWhenRetired),
|
|
172
|
+
imageSource: spec.stateSchema->Reventless.RowImage.sourceFrom,
|
|
172
173
|
},
|
|
173
174
|
)
|
|
174
175
|
logDebug("registered resolver binding for " ++ entry.readModelName, {comp: "PgQueryResolver"})
|
|
@@ -4,6 +4,7 @@ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
|
4
4
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
6
|
import * as Owner$Reventless from "@reventlessdev/reventless-spec/src/components/Owner.res.mjs";
|
|
7
|
+
import * as RowImage$Reventless from "@reventlessdev/reventless-spec/src/semantic/RowImage.res.mjs";
|
|
7
8
|
import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
|
|
8
9
|
import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
|
|
9
10
|
import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
|
|
@@ -78,7 +79,8 @@ function registerBinding(pushdowns, pgConnection, entry, spec) {
|
|
|
78
79
|
ownerField: Owner$Reventless.fieldNames(spec.stateSchema)[0],
|
|
79
80
|
retiredField: Stdlib_Option.map(Stdlib_Option.flatMap(StateAnnotations$Reventless.getSpec(spec.stateSchema), a => a.retired), r => r.field),
|
|
80
81
|
retiredValues: Stdlib_Option.flatMap(Stdlib_Option.flatMap(StateAnnotations$Reventless.getSpec(spec.stateSchema), a => a.retired), r => r.values),
|
|
81
|
-
namedWhenRetired: Stdlib_Option.mapOr(Stdlib_Option.flatMap(StateAnnotations$Reventless.getSpec(spec.stateSchema), a => a.retired), false, r => r.namedWhenRetired)
|
|
82
|
+
namedWhenRetired: Stdlib_Option.mapOr(Stdlib_Option.flatMap(StateAnnotations$Reventless.getSpec(spec.stateSchema), a => a.retired), false, r => r.namedWhenRetired),
|
|
83
|
+
imageSource: RowImage$Reventless.sourceFrom(spec.stateSchema)
|
|
82
84
|
});
|
|
83
85
|
HandlerFactoryHelpersMjs.log.debug("registered resolver binding for " + entry.readModelName, {
|
|
84
86
|
comp: "PgQueryResolver"
|
|
@@ -86,6 +86,45 @@ let withUnionMemberTypes = (
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
Composes the synthetic `displayName` column a `@displayName` state declares.
|
|
91
|
+
|
|
92
|
+
`Projection.rewriteAction` does this for the typed paths — the local platform
|
|
93
|
+
and the GWT harness rewrite the projection's actions before applying them — but
|
|
94
|
+
a deployed state-view slice assembles its own JSON-level pipeline here, so the
|
|
95
|
+
overlay never ran on AWS and every surface named the row by its id.
|
|
96
|
+
|
|
97
|
+
Applied at save rather than on the action: the state reaching the table is the
|
|
98
|
+
one an `Update`'s function already produced, so every action shape is covered
|
|
99
|
+
without enumerating them. `None` — a spec with no annotation, or one that
|
|
100
|
+
predates the metadata — leaves rows exactly as they were.
|
|
101
|
+
*/
|
|
102
|
+
let withDisplayName = (
|
|
103
|
+
base: ReventlessCore.QueryDb_Adapter.operations,
|
|
104
|
+
~stateSchema: option<S.t<unknown>>,
|
|
105
|
+
): ReventlessCore.QueryDb_Adapter.operations =>
|
|
106
|
+
switch stateSchema->Option.flatMap(Reventless.DisplayName.getSpec) {
|
|
107
|
+
| None => base
|
|
108
|
+
| Some(spec) =>
|
|
109
|
+
let compose = (state: JSON.t) => {
|
|
110
|
+
switch state->JSON.Decode.object {
|
|
111
|
+
| Some(stateDict) =>
|
|
112
|
+
stateDict->Dict.set(
|
|
113
|
+
"displayName",
|
|
114
|
+
JSON.Encode.string(Reventless.DisplayName.computeLabel(spec, stateDict)),
|
|
115
|
+
)
|
|
116
|
+
| None => ()
|
|
117
|
+
}
|
|
118
|
+
state
|
|
119
|
+
}
|
|
120
|
+
{
|
|
121
|
+
...base,
|
|
122
|
+
save: (id, state, saveMode, ttl) => base.save(id, compose(state), saveMode, ttl),
|
|
123
|
+
saveBatch: items =>
|
|
124
|
+
base.saveBatch(items->Array.map(((id, state, ttl)) => (id, compose(state), ttl))),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
89
128
|
let makeQueryDbOps = (
|
|
90
129
|
~queryDbTableName: string,
|
|
91
130
|
~pgConnection: option<PgConnection.connectionConfig>,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
3
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as DisplayName$Reventless from "@reventlessdev/reventless-spec/src/components/DisplayName.res.mjs";
|
|
4
6
|
import * as TaggedUnion$Reventless from "@reventlessdev/reventless-spec/src/components/TaggedUnion.res.mjs";
|
|
5
7
|
import * as StateTopicPublishMjs from "./StateTopicPublish.mjs";
|
|
6
8
|
import * as QueryDbEntryPoint_Ops$ReventlessAws from "./QueryDbEntryPoint_Ops.res.mjs";
|
|
@@ -41,6 +43,33 @@ function withUnionMemberTypes(base, stateSchema) {
|
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
function withDisplayName(base, stateSchema) {
|
|
47
|
+
let spec = Stdlib_Option.flatMap(stateSchema, DisplayName$Reventless.getSpec);
|
|
48
|
+
if (spec === undefined) {
|
|
49
|
+
return base;
|
|
50
|
+
}
|
|
51
|
+
let compose = state => {
|
|
52
|
+
let stateDict = Stdlib_JSON.Decode.object(state);
|
|
53
|
+
if (stateDict !== undefined) {
|
|
54
|
+
stateDict["displayName"] = DisplayName$Reventless.computeLabel(spec, stateDict);
|
|
55
|
+
}
|
|
56
|
+
return state;
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
load: base.load,
|
|
60
|
+
loadStream: base.loadStream,
|
|
61
|
+
save: (id, state, saveMode, ttl) => base.save(id, compose(state), saveMode, ttl),
|
|
62
|
+
saveBatch: items => base.saveBatch(items.map(param => [
|
|
63
|
+
param[0],
|
|
64
|
+
compose(param[1]),
|
|
65
|
+
param[2]
|
|
66
|
+
])),
|
|
67
|
+
count: base.count,
|
|
68
|
+
delete: base.delete,
|
|
69
|
+
deleteBatch: base.deleteBatch
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
44
73
|
function makeQueryDbOps(queryDbTableName, pgConnection, stateTopicName, indexes, subIdField) {
|
|
45
74
|
if (pgConnection !== undefined) {
|
|
46
75
|
return StateTopicPublishMjs.withLiveUpdates(QueryDbStorage_Postgres_Runtime$ReventlessAws.opsFor(pgConnection, queryDbTableName, indexes, subIdField), {
|
|
@@ -59,6 +88,7 @@ export {
|
|
|
59
88
|
subIdFieldOf,
|
|
60
89
|
withLiveUpdates,
|
|
61
90
|
withUnionMemberTypes,
|
|
91
|
+
withDisplayName,
|
|
62
92
|
makeQueryDbOps,
|
|
63
93
|
}
|
|
64
|
-
/*
|
|
94
|
+
/* DisplayName-Reventless Not a pure module */
|
|
@@ -97,6 +97,10 @@ type envelope<'event> = {
|
|
|
97
97
|
event: JSON.t,
|
|
98
98
|
meta: Reventless.Message.meta,
|
|
99
99
|
recordedAt: string,
|
|
100
|
+
// The producer time, read off the JSON rather than through `asMeta` — that
|
|
101
|
+
// cast admits `undefined`, so `meta.time` throws on an envelope without one.
|
|
102
|
+
// "" where the producer wrote none, which appends no trail entry.
|
|
103
|
+
producerTime: string,
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
let decodeEnvelope = (json: JSON.t): envelope<'event> => {
|
|
@@ -111,6 +115,12 @@ let decodeEnvelope = (json: JSON.t): envelope<'event> => {
|
|
|
111
115
|
->Option.flatMap(f => f->Dict.get("recordedAt"))
|
|
112
116
|
->Option.flatMap(JSON.Decode.string)
|
|
113
117
|
->Option.getOr(""),
|
|
118
|
+
producerTime: fields
|
|
119
|
+
->Option.flatMap(f => f->Dict.get("meta"))
|
|
120
|
+
->Option.flatMap(JSON.Decode.object)
|
|
121
|
+
->Option.flatMap(m => m->Dict.get("time"))
|
|
122
|
+
->Option.flatMap(JSON.Decode.string)
|
|
123
|
+
->Option.getOr(""),
|
|
114
124
|
}
|
|
115
125
|
}
|
|
116
126
|
|
|
@@ -129,14 +139,27 @@ let makeJsonEventsHandler = (
|
|
|
129
139
|
>,
|
|
130
140
|
~queryDbOps: ReventlessCore.QueryDb_Adapter.operations,
|
|
131
141
|
~subIdConfig: option<Reventless.ReadModel.subIdConfig<JSON.t>>,
|
|
142
|
+
~stateSchema: option<S.t<unknown>>,
|
|
132
143
|
): ReventlessCore.EventCollector.jsonEventsHandler =>
|
|
133
144
|
stream =>
|
|
134
145
|
stream
|
|
135
146
|
->Stream.mapEffect(json =>
|
|
136
147
|
Effect.sync(() =>
|
|
137
148
|
try {
|
|
138
|
-
let {event, meta, recordedAt} = decodeEnvelope(json)
|
|
139
|
-
project({
|
|
149
|
+
let {event, meta, recordedAt, producerTime} = decodeEnvelope(json)
|
|
150
|
+
let actions = project({
|
|
151
|
+
event: event->Reventless.Util_Sury.fromJson(eventSchema),
|
|
152
|
+
meta,
|
|
153
|
+
recordedAt,
|
|
154
|
+
})
|
|
155
|
+
// The deployed slice assembles JSON-level operations rather than going
|
|
156
|
+
// through the typed rewrite, so the trail is appended here — with this
|
|
157
|
+
// envelope's own time, which the apply below no longer has.
|
|
158
|
+
producerTime === ""
|
|
159
|
+
? actions
|
|
160
|
+
: actions->Array.map(
|
|
161
|
+
ReventlessCore.Projection.rewriteJsonAction(_, ~at=producerTime, ~stateSchema),
|
|
162
|
+
)
|
|
140
163
|
} catch {
|
|
141
164
|
| exn =>
|
|
142
165
|
StreamRoutedEntryPoint_Ops.logError(
|
|
@@ -190,7 +213,9 @@ let makeRegisteredHandler = (
|
|
|
190
213
|
~stateTopicName=entry.stateTopicName,
|
|
191
214
|
~indexes=ProjectionEntryPoint_Ops.indexesOf(modules.config),
|
|
192
215
|
~subIdField=ProjectionEntryPoint_Ops.subIdFieldOf(modules.subIdConfig),
|
|
193
|
-
)
|
|
216
|
+
)
|
|
217
|
+
->ProjectionEntryPoint_Ops.withUnionMemberTypes(~stateSchema=modules.stateSchema)
|
|
218
|
+
->ProjectionEntryPoint_Ops.withDisplayName(~stateSchema=modules.stateSchema)
|
|
194
219
|
{
|
|
195
220
|
handler: StreamRoutedEntryPoint_Ops.toStreamHandler(
|
|
196
221
|
makeJsonEventsHandler(
|
|
@@ -199,6 +224,7 @@ let makeRegisteredHandler = (
|
|
|
199
224
|
~project=modules.project,
|
|
200
225
|
~queryDbOps,
|
|
201
226
|
~subIdConfig=modules.subIdConfig,
|
|
227
|
+
~stateSchema=modules.stateSchema,
|
|
202
228
|
),
|
|
203
229
|
),
|
|
204
230
|
// Matches the `StateViewSlice(<name>)` shape the rest of the framework
|
|
@@ -59,19 +59,26 @@ function decodeEnvelope(json) {
|
|
|
59
59
|
return {
|
|
60
60
|
event: tmp,
|
|
61
61
|
meta: Stdlib_Option.flatMap(fields, f => f["meta"]),
|
|
62
|
-
recordedAt: Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(fields, f => f["recordedAt"]), Stdlib_JSON.Decode.string), "")
|
|
62
|
+
recordedAt: Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(fields, f => f["recordedAt"]), Stdlib_JSON.Decode.string), ""),
|
|
63
|
+
producerTime: Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(fields, f => f["meta"]), Stdlib_JSON.Decode.object), m => m["time"]), Stdlib_JSON.Decode.string), "")
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subIdConfig) {
|
|
67
|
+
function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subIdConfig, stateSchema) {
|
|
67
68
|
return stream => Stream.runForEach(Stream.flatMap(Stream.mapEffect(stream, json => Effect.sync(() => {
|
|
68
69
|
try {
|
|
69
70
|
let match = decodeEnvelope(json);
|
|
70
|
-
|
|
71
|
+
let producerTime = match.producerTime;
|
|
72
|
+
let actions = project({
|
|
71
73
|
event: Util_Sury$Reventless.fromJson(match.event, eventSchema),
|
|
72
74
|
meta: match.meta,
|
|
73
75
|
recordedAt: match.recordedAt
|
|
74
76
|
});
|
|
77
|
+
if (producerTime === "") {
|
|
78
|
+
return actions;
|
|
79
|
+
} else {
|
|
80
|
+
return actions.map(__x => Projection$ReventlessCore.rewriteJsonAction(__x, producerTime, stateSchema));
|
|
81
|
+
}
|
|
75
82
|
} catch (raw_exn) {
|
|
76
83
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
77
84
|
StreamRoutedEntryPoint_Ops$ReventlessAws.logError("failed to decode event", {
|
|
@@ -84,9 +91,9 @@ function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subI
|
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
function makeRegisteredHandler(entry, modules) {
|
|
87
|
-
let queryDbOps = ProjectionEntryPoint_Ops$ReventlessAws.withUnionMemberTypes(ProjectionEntryPoint_Ops$ReventlessAws.makeQueryDbOps(entry.queryDbTableName, entry.pgConnection, entry.stateTopicName, ProjectionEntryPoint_Ops$ReventlessAws.indexesOf(modules.config), ProjectionEntryPoint_Ops$ReventlessAws.subIdFieldOf(modules.subIdConfig)), modules.stateSchema);
|
|
94
|
+
let queryDbOps = ProjectionEntryPoint_Ops$ReventlessAws.withDisplayName(ProjectionEntryPoint_Ops$ReventlessAws.withUnionMemberTypes(ProjectionEntryPoint_Ops$ReventlessAws.makeQueryDbOps(entry.queryDbTableName, entry.pgConnection, entry.stateTopicName, ProjectionEntryPoint_Ops$ReventlessAws.indexesOf(modules.config), ProjectionEntryPoint_Ops$ReventlessAws.subIdFieldOf(modules.subIdConfig)), modules.stateSchema), modules.stateSchema);
|
|
88
95
|
return {
|
|
89
|
-
handler: StreamRoutedEntryPoint_Ops$ReventlessAws.toStreamHandler(makeJsonEventsHandler(modules.name, modules.consumedEventSchema, modules.project, queryDbOps, modules.subIdConfig)),
|
|
96
|
+
handler: StreamRoutedEntryPoint_Ops$ReventlessAws.toStreamHandler(makeJsonEventsHandler(modules.name, modules.consumedEventSchema, modules.project, queryDbOps, modules.subIdConfig, modules.stateSchema)),
|
|
90
97
|
comp: `StateViewSlice(` + modules.name + `)`
|
|
91
98
|
};
|
|
92
99
|
}
|
|
@@ -677,7 +677,13 @@ module Make = (
|
|
|
677
677
|
~opts,
|
|
678
678
|
)
|
|
679
679
|
|
|
680
|
-
|
|
680
|
+
// Through an output property, not the resource. `apply` receives the Function
|
|
681
|
+
// record as soon as the outer Output carries it — at construction, fields
|
|
682
|
+
// still unresolved — so discarding it reported ready mid-update, and the
|
|
683
|
+
// re-detect reached a Lambda still serving the previous bundle. `flatMap`
|
|
684
|
+
// returns the Output itself, which Pulumi awaits.
|
|
685
|
+
eventCollectorReadyRef :=
|
|
686
|
+
runtime.parts.lambda->Pulumi.Output.flatMap(fn => fn.arn)->Pulumi.Output.apply(_ => ())
|
|
681
687
|
|
|
682
688
|
// Admin-only cross-plugin SNS subscription permissions. The admin EC
|
|
683
689
|
// Lambda's manageSubscriptions hook (Phase 3 Step 1) creates SNS
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Aws from "@pulumi/aws";
|
|
4
4
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
+
import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
|
|
5
6
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
7
|
import * as Pulumi from "@pulumi/pulumi";
|
|
7
8
|
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
@@ -413,7 +414,7 @@ function Make(EventCollectorChannel) {
|
|
|
413
414
|
eventTopics: eventTopics,
|
|
414
415
|
resources: resources
|
|
415
416
|
}], runtime, opts);
|
|
416
|
-
eventCollectorReadyRef.contents = runtime.parts.lambda.apply(param => {});
|
|
417
|
+
eventCollectorReadyRef.contents = Output$Pulumi.flatMap(runtime.parts.lambda, fn => fn.arn).apply(param => {});
|
|
417
418
|
let isAdminEventCollector = Stdlib_Option.isNone(Plugin_Helpers$ReventlessCore.eventCollectorContextRef.contents[name]);
|
|
418
419
|
if (isAdminEventCollector) {
|
|
419
420
|
new (Aws.iam.RolePolicy)(name + `-snsManageSubs`, {
|
|
@@ -197,7 +197,7 @@ globalThis.describe("the unowned stub is callable where the door calls it", () =
|
|
|
197
197
|
]);
|
|
198
198
|
});
|
|
199
199
|
globalThis.test("declares a parameter on the reference door", () => {
|
|
200
|
-
let code = AppSync_Resolver_Functions$PulumiAws.refsByIds("name", undefined, undefined, false, undefined, undefined)("Products");
|
|
200
|
+
let code = AppSync_Resolver_Functions$PulumiAws.refsByIds("name", undefined, undefined, false, undefined, undefined, undefined)("Products");
|
|
201
201
|
globalThis.expect([
|
|
202
202
|
code.includes("const _owns = (row) => true;"),
|
|
203
203
|
code.includes("_owns(row)")
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// Guards what `eventCollectorReadyRef` waits for.
|
|
4
|
+
//
|
|
5
|
+
// The deploy publishes a synthetic re-detect once the EventCollector Lambda is
|
|
6
|
+
// ready, because that Lambda is what ANSWERS the handshake: reached while it is
|
|
7
|
+
// still serving the previous bundle, it replies with the previous definition,
|
|
8
|
+
// Connect sees a definition it already holds and emits nothing, and the
|
|
9
|
+
// registration keeps the old structure — with no second re-detect coming.
|
|
10
|
+
//
|
|
11
|
+
// The gate used to be `lambda->Output.apply(_ => ())`. `apply` receives the
|
|
12
|
+
// Function record as soon as the OUTER Output carries it, which is at
|
|
13
|
+
// construction, with every field inside still an unresolved Output — so the gate
|
|
14
|
+
// settled while the update was in flight and the whole barrier was inert. No
|
|
15
|
+
// deploy-shaped test catches it: the answer is only stale when a deploy changes a
|
|
16
|
+
// structure, and the race is won more often than it is lost.
|
|
17
|
+
//
|
|
18
|
+
// So this asserts the barrier directly — the gate must not settle until the
|
|
19
|
+
// resource's own outputs do.
|
|
20
|
+
|
|
21
|
+
// The engine's Output constructor, so a field can be left genuinely pending
|
|
22
|
+
// rather than faked. A resolved promise would settle on the next tick either
|
|
23
|
+
// way, which is the difference under test.
|
|
24
|
+
@module("@pulumi/pulumi") @new
|
|
25
|
+
external makeOutput: (
|
|
26
|
+
Set.t<unit>,
|
|
27
|
+
promise<'a>,
|
|
28
|
+
promise<bool>,
|
|
29
|
+
promise<bool>,
|
|
30
|
+
promise<Set.t<unit>>,
|
|
31
|
+
) => Pulumi.Output.t<'a> = "Output"
|
|
32
|
+
|
|
33
|
+
let pending = (value: 'a): (Pulumi.Output.t<'a>, unit => unit) => {
|
|
34
|
+
let settle = ref(() => ())
|
|
35
|
+
let p = Promise.make((resolve, _) => settle := () => resolve(value))
|
|
36
|
+
(
|
|
37
|
+
makeOutput(Set.make(), p, Promise.resolve(true), Promise.resolve(false), Promise.resolve(Set.make())),
|
|
38
|
+
settle.contents,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let ticks = async () => await Promise.make((resolve, _) => {
|
|
43
|
+
let _ = setTimeout(() => resolve(), 50)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// A Lambda mid-update: the resource record exists, its outputs do not yet.
|
|
47
|
+
let deploying = (): (Pulumi.Output.t<PulumiAws.Lambda.Function.t>, unit => unit) => {
|
|
48
|
+
let (arn, complete) = pending("arn:aws:lambda:eu-west-1:123456789012:function:CatalogPluginEventColl")
|
|
49
|
+
let fn: PulumiAws.Lambda.Function.t = {
|
|
50
|
+
arn,
|
|
51
|
+
id: "CatalogPluginEventColl"->Pulumi.Output.make,
|
|
52
|
+
name: "CatalogPluginEventColl"->Pulumi.Output.make,
|
|
53
|
+
invokeArn: "arn:aws:apigateway:invoke"->Pulumi.Output.make,
|
|
54
|
+
}
|
|
55
|
+
// `apply` preserves the nested Outputs, which is how the real resource arrives.
|
|
56
|
+
(()->Pulumi.Output.make->Pulumi.Output.apply(_ => fn), complete)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let observe = (gate: Pulumi.Output.t<unit>): ref<bool> => {
|
|
60
|
+
let settled = ref(false)
|
|
61
|
+
let _ = gate->Pulumi.Output.apply(_ => settled := true)
|
|
62
|
+
settled
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe("the EventCollector readiness gate", () => {
|
|
66
|
+
testAsync("does not settle while the function's outputs are still pending", async () => {
|
|
67
|
+
let (lambda, _complete) = deploying()
|
|
68
|
+
let settled = lambda->Pulumi.Output.flatMap(fn => fn.arn)->Pulumi.Output.apply(_ => ())->observe
|
|
69
|
+
await ticks()
|
|
70
|
+
expect(settled.contents)->toBe(false)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
testAsync("settles once they resolve", async () => {
|
|
74
|
+
let (lambda, complete) = deploying()
|
|
75
|
+
let settled = lambda->Pulumi.Output.flatMap(fn => fn.arn)->Pulumi.Output.apply(_ => ())->observe
|
|
76
|
+
complete()
|
|
77
|
+
await ticks()
|
|
78
|
+
expect(settled.contents)->toBe(true)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// The shape that shipped, kept as the reason the one above is written the way it
|
|
82
|
+
// is: applying to the resource settles against a Lambda still being updated.
|
|
83
|
+
testAsync("settles immediately when taken off the resource instead", async () => {
|
|
84
|
+
let (lambda, _complete) = deploying()
|
|
85
|
+
let settled = lambda->Pulumi.Output.apply(_ => ())->observe
|
|
86
|
+
await ticks()
|
|
87
|
+
expect(settled.contents)->toBe(true)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
|
|
4
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
5
|
+
|
|
6
|
+
function pending(value) {
|
|
7
|
+
let settle = {
|
|
8
|
+
contents: () => {}
|
|
9
|
+
};
|
|
10
|
+
let p = new Promise((resolve, param) => {
|
|
11
|
+
settle.contents = () => resolve(value);
|
|
12
|
+
});
|
|
13
|
+
return [
|
|
14
|
+
new Pulumi.Output(new Set(), p, Promise.resolve(true), Promise.resolve(false), Promise.resolve(new Set())),
|
|
15
|
+
settle.contents
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function ticks() {
|
|
20
|
+
return await new Promise((resolve, param) => {
|
|
21
|
+
setTimeout(() => resolve(), 50);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function deploying() {
|
|
26
|
+
let match = pending("arn:aws:lambda:eu-west-1:123456789012:function:CatalogPluginEventColl");
|
|
27
|
+
let fn_arn = match[0];
|
|
28
|
+
let fn_id = Pulumi.output("CatalogPluginEventColl");
|
|
29
|
+
let fn_name = Pulumi.output("CatalogPluginEventColl");
|
|
30
|
+
let fn_invokeArn = Pulumi.output("arn:aws:apigateway:invoke");
|
|
31
|
+
let fn = {
|
|
32
|
+
arn: fn_arn,
|
|
33
|
+
id: fn_id,
|
|
34
|
+
name: fn_name,
|
|
35
|
+
invokeArn: fn_invokeArn
|
|
36
|
+
};
|
|
37
|
+
return [
|
|
38
|
+
Pulumi.output().apply(() => fn),
|
|
39
|
+
match[1]
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function observe(gate) {
|
|
44
|
+
let settled = {
|
|
45
|
+
contents: false
|
|
46
|
+
};
|
|
47
|
+
gate.apply(() => {
|
|
48
|
+
settled.contents = true;
|
|
49
|
+
});
|
|
50
|
+
return settled;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
globalThis.describe("the EventCollector readiness gate", () => {
|
|
54
|
+
globalThis.test("does not settle while the function's outputs are still pending", async () => {
|
|
55
|
+
let match = deploying();
|
|
56
|
+
let settled = observe(Output$Pulumi.flatMap(match[0], fn => fn.arn).apply(param => {}));
|
|
57
|
+
await ticks();
|
|
58
|
+
globalThis.expect(settled.contents).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
globalThis.test("settles once they resolve", async () => {
|
|
61
|
+
let match = deploying();
|
|
62
|
+
let settled = observe(Output$Pulumi.flatMap(match[0], fn => fn.arn).apply(param => {}));
|
|
63
|
+
match[1]();
|
|
64
|
+
await ticks();
|
|
65
|
+
globalThis.expect(settled.contents).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
globalThis.test("settles immediately when taken off the resource instead", async () => {
|
|
68
|
+
let match = deploying();
|
|
69
|
+
let settled = observe(match[0].apply(param => {}));
|
|
70
|
+
await ticks();
|
|
71
|
+
globalThis.expect(settled.contents).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
pending,
|
|
77
|
+
ticks,
|
|
78
|
+
deploying,
|
|
79
|
+
observe,
|
|
80
|
+
}
|
|
81
|
+
/* Not a pure module */
|