@reventlessdev/reventless-local 3.0.0-alpha.226 → 3.0.0-alpha.228
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/package.json +14 -14
- package/src/LocalPlatformRegistry.res +2 -2
- package/src/LocalPlatformRegistry.res.mjs +14 -13
- package/src/Platform.res +14 -15
- package/src/Platform.res.mjs +20 -21
- package/src/adapter/Auth/LocalAuth.res +3 -3
- package/src/adapter/Auth/LocalAuth.res.mjs +3 -2
- package/src/adapter/Auth/UserStore.res +1 -1
- package/src/adapter/Auth/UserStore.res.mjs +7 -6
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +1 -1
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs +1 -1
- package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +1 -1
- package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res.mjs +3 -3
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +2 -2
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +4 -4
- package/src/adapter/DomainGraphQL_Server.res +2 -2
- package/src/adapter/DomainGraphQL_Server.res.mjs +3 -3
- package/src/adapter/DomainMCP_Server.res +1 -1
- package/src/adapter/MCP_ServerInstance.res +1 -1
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +223 -22
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +176 -18
- package/tests/BakedManifestFilesTest.res +0 -1
- package/tests/PluginEventDecodeTest.res +3 -3
- package/tests/PluginEventDecodeTest.res.mjs +4 -4
- package/tests/ShellConfigTest.res +0 -1
- package/tests/UiHintsTest.res +33 -6
- package/tests/UiHintsTest.res.mjs +15 -2
- package/tests/adapter/CommandAuthorizationTest.res.mjs +7 -7
- package/tests/adapter/GraphQL_SchemaInspectorTest.res +9 -7
- package/tests/adapter/GraphQL_SchemaInspectorTest.res.mjs +64 -64
- package/tests/adapter/LocalAuthHttpTest.res +1 -1
- package/tests/adapter/LocalAuthHttpTest.res.mjs +2 -2
- package/tests/adapter/QueryDbListResolverTest.res +82 -2
- package/tests/adapter/QueryDbListResolverTest.res.mjs +111 -16
- package/tests/components/aggregate/AggregateFixtures.res.mjs +6 -6
- package/tests/components/automationslice/AutomationSliceFixtures.res +4 -4
- package/tests/components/automationslice/AutomationSliceFixtures.res.mjs +20 -19
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +2 -2
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +23 -22
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +4 -4
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res.mjs +21 -20
- package/tests/components/commandgenerator/CommandGeneratorFixtures.res +0 -1
- package/tests/components/commandgenerator/CommandGeneratorFixtures.res.mjs +6 -8
- package/tests/components/commandgenerator/CommandGeneratorTest.res +1 -1
- package/tests/components/commandgenerator/CommandGeneratorTest.res.mjs +2 -2
- package/tests/components/commandtopic/CommandTopicFixtures.res.mjs +9 -9
- package/tests/components/commandtopic/CommandTopicStreamFixtures.res.mjs +9 -9
- package/tests/components/dcb/DcbCrossPartitionFixtures.res +3 -3
- package/tests/components/dcb/DcbCrossPartitionFixtures.res.mjs +22 -21
- package/tests/components/dcb/DcbEventLogAppendStreamTest.res +1 -1
- package/tests/components/dcb/DcbEventLogAppendStreamTest.res.mjs +2 -2
- package/tests/components/dcb/DcbFixtures.res +2 -2
- package/tests/components/dcb/DcbFixtures.res.mjs +12 -11
- package/tests/components/eventlog/EventLogFixtures.res.mjs +6 -6
- package/tests/components/eventlog/EventLogProvisioningSeamTest.res.mjs +3 -3
- package/tests/components/eventtopic/EventTopicFixtures.res.mjs +6 -6
- package/tests/components/eventtopic/EventTopicStreamFixtures.res.mjs +6 -6
- package/tests/components/extensionpoint/ExtensionPointFixtures.res +0 -1
- package/tests/components/extensionpoint/ExtensionPointFixtures.res.mjs +11 -13
- package/tests/components/extensionpoint/ExtensionPointTest.res +3 -3
- package/tests/components/extensionpoint/ExtensionPointTest.res.mjs +4 -4
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +10 -10
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs +9 -9
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res.mjs +23 -21
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +2 -2
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res.mjs +17 -14
- package/tests/components/querydb/QueryDbFixtures.res.mjs +4 -4
- package/tests/components/querydb/QueryDbSubIdFixtures.res.mjs +6 -6
- package/tests/components/readmodel/DcbReadModelE2EFixtures.res +3 -3
- package/tests/components/readmodel/DcbReadModelE2EFixtures.res.mjs +17 -16
- package/tests/components/readmodel/DcbReadModelTypoFailFastTest.res.mjs +7 -7
- package/tests/components/readmodel/MixedSourceReadModelFixtures.res +5 -5
- package/tests/components/readmodel/MixedSourceReadModelFixtures.res.mjs +20 -19
- package/tests/components/readmodel/ReadModelFixtures.res +3 -3
- package/tests/components/readmodel/ReadModelFixtures.res.mjs +9 -8
- package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +2 -3
- package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +7 -8
- package/tests/components/stateviewslice/StateViewSliceFixtures.res +2 -2
- package/tests/components/stateviewslice/StateViewSliceFixtures.res.mjs +22 -21
- package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +2 -2
- package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res.mjs +27 -26
- package/tests/components/task/TaskFixtures.res +0 -1
- package/tests/components/task/TaskFixtures.res.mjs +0 -3
|
@@ -102,7 +102,7 @@ let pageInfoString = (response: JSON.t, key: string): option<string> =>
|
|
|
102
102
|
// Per-test fixture — fresh Bus + storage + registry + resolver
|
|
103
103
|
// ─────────────────────────────────────────────────────────────
|
|
104
104
|
|
|
105
|
-
let buildFixture = async (~name: string) => {
|
|
105
|
+
let buildFixture = async (~name: string, ~indexes: array<Reventless.ReadModel.indexConfig>=[]) => {
|
|
106
106
|
module Bus = LocalBus.Make()
|
|
107
107
|
module Storage = LocalQueryDbStorage.Make(Bus)
|
|
108
108
|
module Resolvers = QueryDbResolvers_GraphQL.Make(Bus)
|
|
@@ -151,7 +151,7 @@ let buildFixture = async (~name: string) => {
|
|
|
151
151
|
~api=(),
|
|
152
152
|
~apiRole=(),
|
|
153
153
|
~dataSourceName=""->Pulumi.Output.make,
|
|
154
|
-
~indexes
|
|
154
|
+
~indexes,
|
|
155
155
|
~subIdField=None,
|
|
156
156
|
~idResolverConfigs=[],
|
|
157
157
|
~idsResolverConfigs=[],
|
|
@@ -440,3 +440,83 @@ describe("QueryDb list resolver — SQLite push-down path", () => {
|
|
|
440
440
|
})
|
|
441
441
|
)
|
|
442
442
|
})
|
|
443
|
+
|
|
444
|
+
// The by-index door answers a Relay connection, keyed on the index's column.
|
|
445
|
+
// Both halves used to be wrong here and in the opposite direction on AppSync:
|
|
446
|
+
// this backend promised `[String]` and returned whole rows, so every call failed
|
|
447
|
+
// to serialise before a caller could reach the rows at all.
|
|
448
|
+
describe("QueryDb by-index resolver", () => {
|
|
449
|
+
let statusIndex: array<Reventless.ReadModel.indexConfig> = [
|
|
450
|
+
{index: "status", type_: "S", projectionType: ALL},
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
let indexResolver = async (~name) => {
|
|
454
|
+
let _ = await buildFixture(~name, ~indexes=statusIndex)
|
|
455
|
+
switch DomainGraphQL_Server.getQueryResolver(name ++ "ByStatus") {
|
|
456
|
+
| Some(r) => r
|
|
457
|
+
| None => JsError.throwWithMessage("index resolver not registered: " ++ name ++ "ByStatus")
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
beforeEach(() => {
|
|
462
|
+
DomainGraphQL_Server.reset()
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
testPromise("answers the rows carrying the index value, as edges", async () => {
|
|
466
|
+
let resolver = await indexResolver(~name="IdxA")
|
|
467
|
+
let response = await resolver(JSON.Encode.null, argsOf([("status", strJson("active"))]), emptyCtx)
|
|
468
|
+
let edges = getEdges(response)
|
|
469
|
+
expect(edges->Array.length)->toBe(3)
|
|
470
|
+
expect(edgeNodeField(edges->Array.getUnsafe(0), "productId"))->toBe("p-1")
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
testPromise("pages forward on first/after like every other connection", async () => {
|
|
474
|
+
let resolver = await indexResolver(~name="IdxB")
|
|
475
|
+
let page1 =
|
|
476
|
+
await resolver(
|
|
477
|
+
JSON.Encode.null,
|
|
478
|
+
argsOf([("status", strJson("active")), ("first", numJson(2))]),
|
|
479
|
+
emptyCtx,
|
|
480
|
+
)
|
|
481
|
+
expect(getEdges(page1)->Array.length)->toBe(2)
|
|
482
|
+
expect(pageInfoBool(page1, "hasNextPage"))->toBe(true)
|
|
483
|
+
|
|
484
|
+
let page2 =
|
|
485
|
+
await resolver(
|
|
486
|
+
JSON.Encode.null,
|
|
487
|
+
argsOf([
|
|
488
|
+
("status", strJson("active")),
|
|
489
|
+
("first", numJson(2)),
|
|
490
|
+
("after", strJson(pageInfoString(page1, "endCursor")->Option.getOr(""))),
|
|
491
|
+
]),
|
|
492
|
+
emptyCtx,
|
|
493
|
+
)
|
|
494
|
+
let edges2 = getEdges(page2)
|
|
495
|
+
expect(edges2->Array.length)->toBe(1)
|
|
496
|
+
expect(edgeNodeField(edges2->Array.getUnsafe(0), "productId"))->toBe("p-4")
|
|
497
|
+
expect(pageInfoBool(page2, "hasNextPage"))->toBe(false)
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
// Refused rather than ignored, and refused here because AppSync cannot do it:
|
|
501
|
+
// a door that paged backward on one backend and quietly returned the forward
|
|
502
|
+
// page on the other is the divergence this whole field was rebuilt to remove.
|
|
503
|
+
testPromise("refuses backward paging instead of answering forward", async () => {
|
|
504
|
+
let resolver = await indexResolver(~name="IdxC")
|
|
505
|
+
let outcome = try {
|
|
506
|
+
let _ =
|
|
507
|
+
await resolver(
|
|
508
|
+
JSON.Encode.null,
|
|
509
|
+
argsOf([("status", strJson("active")), ("last", numJson(2))]),
|
|
510
|
+
emptyCtx,
|
|
511
|
+
)
|
|
512
|
+
Ok()
|
|
513
|
+
} catch {
|
|
514
|
+
| e => Error(e->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr(""))
|
|
515
|
+
}
|
|
516
|
+
switch outcome {
|
|
517
|
+
| Ok() => expect("no error")->toBe("a BAD_USER_INPUT refusal")
|
|
518
|
+
| Error(message) =>
|
|
519
|
+
expect(message->String.includes("Backward pagination (last/before) is not supported"))->toBe(true)
|
|
520
|
+
}
|
|
521
|
+
})
|
|
522
|
+
})
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Sury from "sury";
|
|
4
4
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
5
6
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
6
7
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
8
|
import * as Pulumi from "@pulumi/pulumi";
|
|
8
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
9
10
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
10
11
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
12
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
11
13
|
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
12
14
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
13
15
|
import * as LocalBus$ReventlessLocal from "../../src/adapter/LocalBus.res.mjs";
|
|
@@ -24,13 +26,13 @@ import * as QueryDbResolvers_GraphQL$ReventlessLocal from "../../src/adapter/Que
|
|
|
24
26
|
|
|
25
27
|
TestRunner$ReventlessLocal.setup();
|
|
26
28
|
|
|
27
|
-
let rowStateSchema =
|
|
29
|
+
let rowStateSchema = Sury.$schema(s => ({
|
|
28
30
|
productId: s.m(DcbTag$Reventless.string),
|
|
29
|
-
status: s.m(
|
|
30
|
-
name: s.m(
|
|
31
|
+
status: s.m(Sury.string),
|
|
32
|
+
name: s.m(Sury.string)
|
|
31
33
|
}));
|
|
32
34
|
|
|
33
|
-
let rowStateSchemaWithAnnotations =
|
|
35
|
+
let rowStateSchemaWithAnnotations = Sury.$Metadata_set(rowStateSchema, StateAnnotations$Reventless.stateAnnotationsId, {
|
|
34
36
|
ids: ["productId"],
|
|
35
37
|
compositeIds: [],
|
|
36
38
|
subIds: [],
|
|
@@ -112,7 +114,8 @@ function pageInfoString(response, key) {
|
|
|
112
114
|
return getString(pageInfo(response), key);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
async function buildFixture(name) {
|
|
117
|
+
async function buildFixture(name, indexesOpt) {
|
|
118
|
+
let indexes = indexesOpt !== undefined ? indexesOpt : [];
|
|
116
119
|
let Bus = LocalBus$ReventlessLocal.Make({});
|
|
117
120
|
let Storage = LocalQueryDbStorage$ReventlessLocal.Make(Bus);
|
|
118
121
|
let Resolvers = QueryDbResolvers_GraphQL$ReventlessLocal.Make(Bus);
|
|
@@ -151,7 +154,7 @@ async function buildFixture(name) {
|
|
|
151
154
|
Plugin_Helpers$ReventlessCore.stateSchemaRegistry[name] = rowStateSchemaWithAnnotations;
|
|
152
155
|
let queryDb = Maker.make(undefined, undefined, undefined, undefined, undefined);
|
|
153
156
|
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(queryDb));
|
|
154
|
-
Resolvers.make(name, undefined, undefined, Pulumi.output(""),
|
|
157
|
+
Resolvers.make(name, undefined, undefined, Pulumi.output(""), indexes, undefined, [], [], "AllowAuthenticated", {});
|
|
155
158
|
let r = DomainGraphQL_Server$ReventlessLocal.getQueryResolver(listFieldName);
|
|
156
159
|
let resolver = r !== undefined ? r : Stdlib_JsError.throwWithMessage("resolver not registered: " + listFieldName);
|
|
157
160
|
let rows = [
|
|
@@ -199,7 +202,7 @@ async function buildFixture(name) {
|
|
|
199
202
|
globalThis.describe("QueryDb list resolver — keyset pagination", () => {
|
|
200
203
|
globalThis.beforeEach(() => DomainGraphQL_Server$ReventlessLocal.reset());
|
|
201
204
|
globalThis.test("first: 2 over a 5-row fixture returns 2 edges and hasNextPage = true", async () => {
|
|
202
|
-
let match = await buildFixture("PageA");
|
|
205
|
+
let match = await buildFixture("PageA", undefined);
|
|
203
206
|
let response = await match[0](null, Object.fromEntries([[
|
|
204
207
|
"first",
|
|
205
208
|
2
|
|
@@ -212,7 +215,7 @@ globalThis.describe("QueryDb list resolver — keyset pagination", () => {
|
|
|
212
215
|
globalThis.expect(edgeNodeField(edges[1], "productId")).toBe("p-2");
|
|
213
216
|
});
|
|
214
217
|
globalThis.test("after: <endCursor> walks forward without overlap", async () => {
|
|
215
|
-
let match = await buildFixture("PageB");
|
|
218
|
+
let match = await buildFixture("PageB", undefined);
|
|
216
219
|
let resolver = match[0];
|
|
217
220
|
let page1 = await resolver(null, Object.fromEntries([[
|
|
218
221
|
"first",
|
|
@@ -251,7 +254,7 @@ globalThis.describe("QueryDb list resolver — keyset pagination", () => {
|
|
|
251
254
|
globalThis.expect(pageInfoBool(page3, "hasNextPage")).toBe(false);
|
|
252
255
|
});
|
|
253
256
|
globalThis.test("last + before walks backward and flips hasPreviousPage", async () => {
|
|
254
|
-
let match = await buildFixture("PageC");
|
|
257
|
+
let match = await buildFixture("PageC", undefined);
|
|
255
258
|
let resolver = match[0];
|
|
256
259
|
let page1 = await resolver(null, Object.fromEntries([[
|
|
257
260
|
"first",
|
|
@@ -314,7 +317,7 @@ globalThis.describe("QueryDb list resolver — keyset pagination", () => {
|
|
|
314
317
|
globalThis.expect(pageInfoBool(firstPage, "hasPreviousPage")).toBe(false);
|
|
315
318
|
});
|
|
316
319
|
globalThis.test("filter.<field>Eq + first / after paginates only the narrowed subset", async () => {
|
|
317
|
-
let match = await buildFixture("PageD");
|
|
320
|
+
let match = await buildFixture("PageD", undefined);
|
|
318
321
|
let resolver = match[0];
|
|
319
322
|
let activeFilter = Object.fromEntries([[
|
|
320
323
|
"statusEq",
|
|
@@ -356,7 +359,7 @@ globalThis.describe("QueryDb list resolver — keyset pagination", () => {
|
|
|
356
359
|
globalThis.expect(pageInfoBool(page2, "hasNextPage")).toBe(false);
|
|
357
360
|
});
|
|
358
361
|
globalThis.test("orderBy DESC + first / after paginates the reverse-sorted view", async () => {
|
|
359
|
-
let match = await buildFixture("PageE");
|
|
362
|
+
let match = await buildFixture("PageE", undefined);
|
|
360
363
|
let resolver = match[0];
|
|
361
364
|
let orderBy = Object.fromEntries([
|
|
362
365
|
[
|
|
@@ -404,7 +407,7 @@ globalThis.describe("QueryDb list resolver — keyset pagination", () => {
|
|
|
404
407
|
globalThis.expect(edgeNodeField(edges2[1], "name")).toBe("Bravo");
|
|
405
408
|
});
|
|
406
409
|
globalThis.test("bare request (no first / after) returns a single bounded page", async () => {
|
|
407
|
-
let match = await buildFixture("PageF");
|
|
410
|
+
let match = await buildFixture("PageF", undefined);
|
|
408
411
|
let response = await match[0](null, Object.fromEntries([]), emptyCtx);
|
|
409
412
|
let edges = getEdges(response);
|
|
410
413
|
globalThis.expect(edges.length).toBe(5);
|
|
@@ -424,7 +427,7 @@ globalThis.describe("QueryDb list resolver — SQLite push-down path", () => {
|
|
|
424
427
|
return r;
|
|
425
428
|
};
|
|
426
429
|
globalThis.test("first:2 then after walks forward without overlap", () => withSqlite(async () => {
|
|
427
|
-
let match = await buildFixture("SqlPageA");
|
|
430
|
+
let match = await buildFixture("SqlPageA", undefined);
|
|
428
431
|
let resolver = match[0];
|
|
429
432
|
let page1 = await resolver(null, Object.fromEntries([[
|
|
430
433
|
"first",
|
|
@@ -452,7 +455,7 @@ globalThis.describe("QueryDb list resolver — SQLite push-down path", () => {
|
|
|
452
455
|
globalThis.expect(edgeNodeField(edges2[1], "productId")).toBe("p-4");
|
|
453
456
|
}));
|
|
454
457
|
globalThis.test("orderBy name DESC + first:2 returns the reverse-sorted head", () => withSqlite(async () => {
|
|
455
|
-
let match = await buildFixture("SqlPageB");
|
|
458
|
+
let match = await buildFixture("SqlPageB", undefined);
|
|
456
459
|
let orderBy = Object.fromEntries([
|
|
457
460
|
[
|
|
458
461
|
"field",
|
|
@@ -479,7 +482,7 @@ globalThis.describe("QueryDb list resolver — SQLite push-down path", () => {
|
|
|
479
482
|
globalThis.expect(edgeNodeField(edges1[1], "name")).toBe("Delta");
|
|
480
483
|
}));
|
|
481
484
|
globalThis.test("statusEq active narrows the pushed page", () => withSqlite(async () => {
|
|
482
|
-
let match = await buildFixture("SqlPageC");
|
|
485
|
+
let match = await buildFixture("SqlPageC", undefined);
|
|
483
486
|
let activeFilter = Object.fromEntries([[
|
|
484
487
|
"statusEq",
|
|
485
488
|
"active"
|
|
@@ -495,6 +498,98 @@ globalThis.describe("QueryDb list resolver — SQLite push-down path", () => {
|
|
|
495
498
|
}));
|
|
496
499
|
});
|
|
497
500
|
|
|
501
|
+
globalThis.describe("QueryDb by-index resolver", () => {
|
|
502
|
+
let statusIndex = [{
|
|
503
|
+
index: "status",
|
|
504
|
+
type_: "S",
|
|
505
|
+
projectionType: "ALL"
|
|
506
|
+
}];
|
|
507
|
+
let indexResolver = async name => {
|
|
508
|
+
await buildFixture(name, statusIndex);
|
|
509
|
+
let r = DomainGraphQL_Server$ReventlessLocal.getQueryResolver(name + "ByStatus");
|
|
510
|
+
if (r !== undefined) {
|
|
511
|
+
return r;
|
|
512
|
+
} else {
|
|
513
|
+
return Stdlib_JsError.throwWithMessage("index resolver not registered: " + name + "ByStatus");
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
globalThis.beforeEach(() => DomainGraphQL_Server$ReventlessLocal.reset());
|
|
517
|
+
globalThis.test("answers the rows carrying the index value, as edges", async () => {
|
|
518
|
+
let resolver = await indexResolver("IdxA");
|
|
519
|
+
let response = await resolver(null, Object.fromEntries([[
|
|
520
|
+
"status",
|
|
521
|
+
"active"
|
|
522
|
+
]]), emptyCtx);
|
|
523
|
+
let edges = getEdges(response);
|
|
524
|
+
globalThis.expect(edges.length).toBe(3);
|
|
525
|
+
globalThis.expect(edgeNodeField(edges[0], "productId")).toBe("p-1");
|
|
526
|
+
});
|
|
527
|
+
globalThis.test("pages forward on first/after like every other connection", async () => {
|
|
528
|
+
let resolver = await indexResolver("IdxB");
|
|
529
|
+
let page1 = await resolver(null, Object.fromEntries([
|
|
530
|
+
[
|
|
531
|
+
"status",
|
|
532
|
+
"active"
|
|
533
|
+
],
|
|
534
|
+
[
|
|
535
|
+
"first",
|
|
536
|
+
2
|
|
537
|
+
]
|
|
538
|
+
]), emptyCtx);
|
|
539
|
+
globalThis.expect(getEdges(page1).length).toBe(2);
|
|
540
|
+
globalThis.expect(pageInfoBool(page1, "hasNextPage")).toBe(true);
|
|
541
|
+
let page2 = await resolver(null, Object.fromEntries([
|
|
542
|
+
[
|
|
543
|
+
"status",
|
|
544
|
+
"active"
|
|
545
|
+
],
|
|
546
|
+
[
|
|
547
|
+
"first",
|
|
548
|
+
2
|
|
549
|
+
],
|
|
550
|
+
[
|
|
551
|
+
"after",
|
|
552
|
+
Stdlib_Option.getOr(getString(pageInfo(page1), "endCursor"), "")
|
|
553
|
+
]
|
|
554
|
+
]), emptyCtx);
|
|
555
|
+
let edges2 = getEdges(page2);
|
|
556
|
+
globalThis.expect(edges2.length).toBe(1);
|
|
557
|
+
globalThis.expect(edgeNodeField(edges2[0], "productId")).toBe("p-4");
|
|
558
|
+
globalThis.expect(pageInfoBool(page2, "hasNextPage")).toBe(false);
|
|
559
|
+
});
|
|
560
|
+
globalThis.test("refuses backward paging instead of answering forward", async () => {
|
|
561
|
+
let resolver = await indexResolver("IdxC");
|
|
562
|
+
let outcome;
|
|
563
|
+
try {
|
|
564
|
+
await resolver(null, Object.fromEntries([
|
|
565
|
+
[
|
|
566
|
+
"status",
|
|
567
|
+
"active"
|
|
568
|
+
],
|
|
569
|
+
[
|
|
570
|
+
"last",
|
|
571
|
+
2
|
|
572
|
+
]
|
|
573
|
+
]), emptyCtx);
|
|
574
|
+
outcome = {
|
|
575
|
+
TAG: "Ok",
|
|
576
|
+
_0: undefined
|
|
577
|
+
};
|
|
578
|
+
} catch (raw_e) {
|
|
579
|
+
let e = Primitive_exceptions.internalToException(raw_e);
|
|
580
|
+
outcome = {
|
|
581
|
+
TAG: "Error",
|
|
582
|
+
_0: Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(e), Stdlib_JsExn.message), "")
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
if (outcome.TAG === "Ok") {
|
|
586
|
+
globalThis.expect("no error").toBe("a BAD_USER_INPUT refusal");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
globalThis.expect(outcome._0.includes("Backward pagination (last/before) is not supported")).toBe(true);
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
|
|
498
593
|
export {
|
|
499
594
|
rowStateSchema,
|
|
500
595
|
rowStateSchemaWithAnnotations,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Sury from "sury";
|
|
4
4
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
5
|
import * as LocalBus$ReventlessLocal from "../../../src/adapter/LocalBus.res.mjs";
|
|
6
6
|
import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
|
|
@@ -9,17 +9,17 @@ import * as Aggregate_Builder$ReventlessLocal from "../../../src/components/Aggr
|
|
|
9
9
|
|
|
10
10
|
let name = "TestItem";
|
|
11
11
|
|
|
12
|
-
let commandSchema =
|
|
12
|
+
let commandSchema = Sury.$schema(s => ({
|
|
13
13
|
TAG: "CreateItem",
|
|
14
|
-
name: s.m(
|
|
14
|
+
name: s.m(Sury.string)
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
|
-
let eventSchema =
|
|
17
|
+
let eventSchema = Sury.$schema(s => ({
|
|
18
18
|
TAG: "ItemCreated",
|
|
19
|
-
name: s.m(
|
|
19
|
+
name: s.m(Sury.string)
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
-
let errorSchema =
|
|
22
|
+
let errorSchema = Sury.literal("AlreadyExists");
|
|
23
23
|
|
|
24
24
|
let moduleUrl = import.meta.url;
|
|
25
25
|
|
|
@@ -138,7 +138,7 @@ let testContext: Reventless.AutomationSlice.context = {
|
|
|
138
138
|
// the event payload with a `meta.service` matching the source name so the
|
|
139
139
|
// callback's per-source dispatch picks the right mapping.
|
|
140
140
|
let encodeShipOrderEvent = (event: ShipOrderSource.event): JSON.t => {
|
|
141
|
-
let payloadJson = event->
|
|
141
|
+
let payloadJson = event->Reventless.Util_Sury.toJson(ShipOrderSource.eventSchema)
|
|
142
142
|
let dict = switch payloadJson->JSON.Decode.object {
|
|
143
143
|
| Some(d) => d->Dict.copy
|
|
144
144
|
| None =>
|
|
@@ -158,13 +158,13 @@ let encodeShipOrderEvent = (event: ShipOrderSource.event): JSON.t => {
|
|
|
158
158
|
msgId: "msg-1",
|
|
159
159
|
correlationId: "",
|
|
160
160
|
}
|
|
161
|
-
dict->Dict.set("meta", meta->
|
|
161
|
+
dict->Dict.set("meta", meta->Reventless.Util_Sury.toJson(Reventless.Message.metaSchema))
|
|
162
162
|
dict->Dict.set("id", JSON.Encode.string("env-id"))
|
|
163
163
|
JSON.Encode.object(dict)
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
let encodeSkipProcessEvent = (event: SkipProcessSource.event): JSON.t => {
|
|
167
|
-
let payloadJson = event->
|
|
167
|
+
let payloadJson = event->Reventless.Util_Sury.toJson(SkipProcessSource.eventSchema)
|
|
168
168
|
let dict = switch payloadJson->JSON.Decode.object {
|
|
169
169
|
| Some(d) => d->Dict.copy
|
|
170
170
|
| None =>
|
|
@@ -183,7 +183,7 @@ let encodeSkipProcessEvent = (event: SkipProcessSource.event): JSON.t => {
|
|
|
183
183
|
msgId: "msg-1",
|
|
184
184
|
correlationId: "",
|
|
185
185
|
}
|
|
186
|
-
dict->Dict.set("meta", meta->
|
|
186
|
+
dict->Dict.set("meta", meta->Reventless.Util_Sury.toJson(Reventless.Message.metaSchema))
|
|
187
187
|
dict->Dict.set("id", JSON.Encode.string("env-id"))
|
|
188
188
|
JSON.Encode.object(dict)
|
|
189
189
|
}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Sury from "sury";
|
|
4
4
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
5
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
6
6
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
7
7
|
import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
|
|
8
|
+
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
8
9
|
import * as AutomationSlice$Reventless from "@reventlessdev/reventless-spec/src/components/AutomationSlice.res.mjs";
|
|
9
10
|
|
|
10
11
|
let name = "ShipOrderDcbEventLog";
|
|
11
12
|
|
|
12
|
-
let eventSchema =
|
|
13
|
-
|
|
13
|
+
let eventSchema = Sury.union([
|
|
14
|
+
Sury.$schema(s => ({
|
|
14
15
|
TAG: "OrderPlaced",
|
|
15
16
|
orderId: s.m(DcbTag$Reventless.string),
|
|
16
|
-
address: s.m(
|
|
17
|
+
address: s.m(Sury.string)
|
|
17
18
|
})),
|
|
18
|
-
|
|
19
|
+
Sury.$schema(s => ({
|
|
19
20
|
TAG: "ShipmentCreated",
|
|
20
21
|
orderId: s.m(DcbTag$Reventless.string)
|
|
21
22
|
}))
|
|
@@ -29,12 +30,12 @@ let ShipOrderSource = {
|
|
|
29
30
|
|
|
30
31
|
let name$1 = "SkipProcessDcbEventLog";
|
|
31
32
|
|
|
32
|
-
let eventSchema$1 =
|
|
33
|
-
|
|
33
|
+
let eventSchema$1 = Sury.union([
|
|
34
|
+
Sury.$schema(s => ({
|
|
34
35
|
TAG: "OrderPlaced",
|
|
35
36
|
orderId: s.m(DcbTag$Reventless.string)
|
|
36
37
|
})),
|
|
37
|
-
|
|
38
|
+
Sury.$schema(s => ({
|
|
38
39
|
TAG: "ShipmentCreated",
|
|
39
40
|
orderId: s.m(DcbTag$Reventless.string)
|
|
40
41
|
}))
|
|
@@ -48,12 +49,12 @@ let SkipProcessSource = {
|
|
|
48
49
|
|
|
49
50
|
let moduleUrl = import.meta.url;
|
|
50
51
|
|
|
51
|
-
let todoItemSchema =
|
|
52
|
-
orderId: s.m(
|
|
53
|
-
address: s.m(
|
|
52
|
+
let todoItemSchema = Sury.$schema(s => ({
|
|
53
|
+
orderId: s.m(Sury.string),
|
|
54
|
+
address: s.m(Sury.string)
|
|
54
55
|
}));
|
|
55
56
|
|
|
56
|
-
let commandSchema =
|
|
57
|
+
let commandSchema = Sury.$schema(s => ({
|
|
57
58
|
TAG: "CreateShipment",
|
|
58
59
|
orderId: s.m(DcbTag$Reventless.string)
|
|
59
60
|
}));
|
|
@@ -75,11 +76,11 @@ let ShipOrderSpec = {
|
|
|
75
76
|
|
|
76
77
|
let moduleUrl$1 = import.meta.url;
|
|
77
78
|
|
|
78
|
-
let todoItemSchema$1 =
|
|
79
|
-
orderId: s.m(
|
|
79
|
+
let todoItemSchema$1 = Sury.$schema(s => ({
|
|
80
|
+
orderId: s.m(Sury.string)
|
|
80
81
|
}));
|
|
81
82
|
|
|
82
|
-
let commandSchema$1 =
|
|
83
|
+
let commandSchema$1 = Sury.literal("Noop");
|
|
83
84
|
|
|
84
85
|
function commandAuthorization$1(param) {
|
|
85
86
|
return "AllowAuthenticated";
|
|
@@ -196,7 +197,7 @@ let SkipProcessAutomation = {
|
|
|
196
197
|
};
|
|
197
198
|
|
|
198
199
|
function encodeShipOrderEvent(event) {
|
|
199
|
-
let payloadJson =
|
|
200
|
+
let payloadJson = Util_Sury$Reventless.toJson(event, eventSchema);
|
|
200
201
|
let d = Stdlib_JSON.Decode.object(payloadJson);
|
|
201
202
|
let dict;
|
|
202
203
|
if (d !== undefined) {
|
|
@@ -219,13 +220,13 @@ function encodeShipOrderEvent(event) {
|
|
|
219
220
|
msgId: "msg-1",
|
|
220
221
|
correlationId: ""
|
|
221
222
|
};
|
|
222
|
-
dict["meta"] =
|
|
223
|
+
dict["meta"] = Util_Sury$Reventless.toJson(meta, Message$Reventless.metaSchema);
|
|
223
224
|
dict["id"] = "env-id";
|
|
224
225
|
return dict;
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
function encodeSkipProcessEvent(event) {
|
|
228
|
-
let payloadJson =
|
|
229
|
+
let payloadJson = Util_Sury$Reventless.toJson(event, eventSchema$1);
|
|
229
230
|
let d = Stdlib_JSON.Decode.object(payloadJson);
|
|
230
231
|
let dict;
|
|
231
232
|
if (d !== undefined) {
|
|
@@ -248,7 +249,7 @@ function encodeSkipProcessEvent(event) {
|
|
|
248
249
|
msgId: "msg-1",
|
|
249
250
|
correlationId: ""
|
|
250
251
|
};
|
|
251
|
-
dict["meta"] =
|
|
252
|
+
dict["meta"] = Util_Sury$Reventless.toJson(meta, Message$Reventless.metaSchema);
|
|
252
253
|
dict["id"] = "env-id";
|
|
253
254
|
return dict;
|
|
254
255
|
}
|
|
@@ -207,7 +207,7 @@ let publishJsons: ReventlessInfra.CommandTopic.publishJsons = async cmdJsons =>
|
|
|
207
207
|
let fullBody = JSON.Encode.object(
|
|
208
208
|
Dict.fromArray([
|
|
209
209
|
("id", JSON.Encode.string(cmdJson.id)),
|
|
210
|
-
("meta", cmdJson.meta->
|
|
210
|
+
("meta", cmdJson.meta->Reventless.Util_Sury.toJson(Reventless.Message.metaSchema)),
|
|
211
211
|
("command", cmdJson.commandJson),
|
|
212
212
|
]),
|
|
213
213
|
)
|
|
@@ -266,7 +266,7 @@ let placeCmdJson = (orderId: string): Reventless.Message.commandJson => {
|
|
|
266
266
|
id: orderId,
|
|
267
267
|
meta: testMeta,
|
|
268
268
|
commandJson: PlaceSpec.Place({orderId: orderId})
|
|
269
|
-
->
|
|
269
|
+
->Reventless.Util_Sury.toJson(PlaceSpec.commandSchema),
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
// Promise-based timeout used to fail fast if the chain self-deadlocks.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Sury from "sury";
|
|
4
4
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
@@ -8,6 +8,7 @@ import * as Stream from "effect/Stream";
|
|
|
8
8
|
import * as Pulumi from "@pulumi/pulumi";
|
|
9
9
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
10
10
|
import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
|
|
11
|
+
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
11
12
|
import * as LocalBus$ReventlessLocal from "../../../src/adapter/LocalBus.res.mjs";
|
|
12
13
|
import * as Component$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Component.res.mjs";
|
|
13
14
|
import * as AutomationSlice$Reventless from "@reventlessdev/reventless-spec/src/components/AutomationSlice.res.mjs";
|
|
@@ -22,19 +23,19 @@ let name = "Place";
|
|
|
22
23
|
|
|
23
24
|
let moduleUrl = import.meta.url;
|
|
24
25
|
|
|
25
|
-
let eventSchema =
|
|
26
|
+
let eventSchema = Sury.$schema(s => ({
|
|
26
27
|
TAG: "Placed",
|
|
27
28
|
orderId: s.m(DcbTag$Reventless.string)
|
|
28
29
|
}));
|
|
29
30
|
|
|
30
|
-
let consumedEventSchema =
|
|
31
|
+
let consumedEventSchema = Sury.literal("Placed");
|
|
31
32
|
|
|
32
|
-
let commandSchema =
|
|
33
|
+
let commandSchema = Sury.$schema(s => ({
|
|
33
34
|
TAG: "Place",
|
|
34
35
|
orderId: s.m(DcbTag$Reventless.string)
|
|
35
36
|
}));
|
|
36
37
|
|
|
37
|
-
let errorSchema =
|
|
38
|
+
let errorSchema = Sury.literal("AlreadyPlaced");
|
|
38
39
|
|
|
39
40
|
function commandAuthorization(param) {
|
|
40
41
|
return "AllowAuthenticated";
|
|
@@ -87,25 +88,25 @@ let name$1 = "Ship";
|
|
|
87
88
|
|
|
88
89
|
let moduleUrl$2 = import.meta.url;
|
|
89
90
|
|
|
90
|
-
let eventSchema$1 =
|
|
91
|
+
let eventSchema$1 = Sury.$schema(s => ({
|
|
91
92
|
TAG: "Shipped",
|
|
92
93
|
orderId: s.m(DcbTag$Reventless.string)
|
|
93
94
|
}));
|
|
94
95
|
|
|
95
|
-
let consumedEventSchema$1 =
|
|
96
|
-
|
|
96
|
+
let consumedEventSchema$1 = Sury.union([
|
|
97
|
+
Sury.$schema(s => ({
|
|
97
98
|
TAG: "Placed",
|
|
98
|
-
orderId: s.m(
|
|
99
|
+
orderId: s.m(Sury.string)
|
|
99
100
|
})),
|
|
100
|
-
|
|
101
|
+
Sury.literal("Shipped")
|
|
101
102
|
]);
|
|
102
103
|
|
|
103
|
-
let commandSchema$1 =
|
|
104
|
+
let commandSchema$1 = Sury.$schema(s => ({
|
|
104
105
|
TAG: "Ship",
|
|
105
106
|
orderId: s.m(DcbTag$Reventless.string)
|
|
106
107
|
}));
|
|
107
108
|
|
|
108
|
-
let errorSchema$1 =
|
|
109
|
+
let errorSchema$1 = Sury.literal("NotPlaced");
|
|
109
110
|
|
|
110
111
|
function commandAuthorization$1(param) {
|
|
111
112
|
return "AllowAuthenticated";
|
|
@@ -178,14 +179,14 @@ let ShipBehavior = {
|
|
|
178
179
|
|
|
179
180
|
let name$2 = "TestLogDcbEventLog";
|
|
180
181
|
|
|
181
|
-
let eventSchema$2 =
|
|
182
|
-
|
|
182
|
+
let eventSchema$2 = Sury.union([
|
|
183
|
+
Sury.$schema(s => ({
|
|
183
184
|
TAG: "Placed",
|
|
184
|
-
orderId: s.m(
|
|
185
|
+
orderId: s.m(Sury.string)
|
|
185
186
|
})),
|
|
186
|
-
|
|
187
|
+
Sury.$schema(s => ({
|
|
187
188
|
TAG: "Shipped",
|
|
188
|
-
orderId: s.m(
|
|
189
|
+
orderId: s.m(Sury.string)
|
|
189
190
|
}))
|
|
190
191
|
]);
|
|
191
192
|
|
|
@@ -199,11 +200,11 @@ let name$3 = "AutoShip";
|
|
|
199
200
|
|
|
200
201
|
let moduleUrl$4 = import.meta.url;
|
|
201
202
|
|
|
202
|
-
let todoItemSchema =
|
|
203
|
-
orderId: s.m(
|
|
203
|
+
let todoItemSchema = Sury.$schema(s => ({
|
|
204
|
+
orderId: s.m(Sury.string)
|
|
204
205
|
}));
|
|
205
206
|
|
|
206
|
-
let commandSchema$2 =
|
|
207
|
+
let commandSchema$2 = Sury.$schema(s => ({
|
|
207
208
|
TAG: "Ship",
|
|
208
209
|
orderId: s.m(DcbTag$Reventless.string)
|
|
209
210
|
}));
|
|
@@ -304,7 +305,7 @@ async function publishJsons(cmdJsons) {
|
|
|
304
305
|
],
|
|
305
306
|
[
|
|
306
307
|
"meta",
|
|
307
|
-
|
|
308
|
+
Util_Sury$Reventless.toJson(cmdJson.meta, Message$Reventless.metaSchema)
|
|
308
309
|
],
|
|
309
310
|
[
|
|
310
311
|
"command",
|
|
@@ -387,7 +388,7 @@ function placeCmdJson(orderId) {
|
|
|
387
388
|
return {
|
|
388
389
|
id: orderId,
|
|
389
390
|
meta: TestFixtures$ReventlessLocal.testMeta,
|
|
390
|
-
commandJson:
|
|
391
|
+
commandJson: Util_Sury$Reventless.toJson({
|
|
391
392
|
TAG: "Place",
|
|
392
393
|
orderId: orderId
|
|
393
394
|
}, commandSchema)
|
|
@@ -193,8 +193,8 @@ let publishAggregateEvent = async (id, event: OrderAggregateSource.event) => {
|
|
|
193
193
|
let eventJson = JSON.Encode.object(
|
|
194
194
|
Dict.fromArray([
|
|
195
195
|
("id", id->JSON.Encode.string),
|
|
196
|
-
("meta", meta->
|
|
197
|
-
("event", event->
|
|
196
|
+
("meta", meta->Reventless.Util_Sury.toJson(Message.metaSchema)),
|
|
197
|
+
("event", event->Reventless.Util_Sury.toJson(OrderAggregateSource.eventSchema)),
|
|
198
198
|
]),
|
|
199
199
|
)
|
|
200
200
|
await Bus.publishEvent(OrderAggregateSource.name, "test", meta, eventJson)
|
|
@@ -205,8 +205,8 @@ let publishDcbEvent = async (id, event: InventoryDcbSource.event) => {
|
|
|
205
205
|
let eventJson = JSON.Encode.object(
|
|
206
206
|
Dict.fromArray([
|
|
207
207
|
("id", id->JSON.Encode.string),
|
|
208
|
-
("meta", meta->
|
|
209
|
-
("event", event->
|
|
208
|
+
("meta", meta->Reventless.Util_Sury.toJson(Message.metaSchema)),
|
|
209
|
+
("event", event->Reventless.Util_Sury.toJson(InventoryDcbSource.eventSchema)),
|
|
210
210
|
]),
|
|
211
211
|
)
|
|
212
212
|
await Bus.publishEvent(InventoryDcbSource.name, "test", meta, eventJson)
|