@reventlessdev/reventless-local 3.0.0-alpha.229 → 3.0.0-alpha.231
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
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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.231 (2026-08-20)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @reventlessdev/reventless-local
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 3.0.0-alpha.230 (2026-08-20)
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* **api:** emit a tagged-union state field as a GraphQL union ([3a380c0](https://github.com/ReventlessDev/reventless-core/commit/3a380c0ab055b87048d90a852ec1664f6aab6b00))
|
|
19
|
+
|
|
20
|
+
|
|
6
21
|
# 3.0.0-alpha.229 (2026-08-19)
|
|
7
22
|
|
|
8
23
|
**Note:** Version bump only for package @reventlessdev/reventless-local
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-local",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.231",
|
|
4
4
|
"description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -35,23 +35,23 @@
|
|
|
35
35
|
"sury": "11.0.0-rc.1",
|
|
36
36
|
"ws": "^8.18.0",
|
|
37
37
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
38
|
-
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.27",
|
|
39
38
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
40
|
-
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
|
|
41
39
|
"@reventlessdev/rescript-node": "2.0.0-alpha.8",
|
|
40
|
+
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.27",
|
|
42
41
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
43
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
44
|
-
"@reventlessdev/
|
|
45
|
-
"@reventlessdev/reventless-
|
|
46
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
47
|
-
"@reventlessdev/reventless-
|
|
42
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.243",
|
|
43
|
+
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
|
|
44
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.91",
|
|
45
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.148",
|
|
46
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.190",
|
|
48
47
|
"@reventlessdev/reventless-seed": "1.0.0-alpha.16",
|
|
49
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
48
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.120",
|
|
49
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.107"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"rescript": "12.3.0",
|
|
53
53
|
"sury-ppx": "11.0.0-rc.1",
|
|
54
|
-
"@reventlessdev/reventless-ppx": "1.0.0-alpha.
|
|
54
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.72"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"rescript": "12.3.0"
|
|
@@ -12,11 +12,22 @@ let _ = TestRunner.setup()
|
|
|
12
12
|
// Test state spec — five-row fixture exercising filter / sort / paginate
|
|
13
13
|
// ─────────────────────────────────────────────────────────────
|
|
14
14
|
|
|
15
|
+
// A union field on an *indexed* view: the by-index door is one of two in the
|
|
16
|
+
// mechanism plan's table that no fixture carried a union through. Optional so the
|
|
17
|
+
// existing five-row literals below are untouched.
|
|
18
|
+
@schema
|
|
19
|
+
type geolocation =
|
|
20
|
+
| Pending({requestedFor: string})
|
|
21
|
+
| Located({lat: float, lng: float})
|
|
22
|
+
|
|
23
|
+
let geolocationSchema = Reventless.TaggedUnion.named(~name="Geolocation", geolocationSchema)
|
|
24
|
+
|
|
15
25
|
@schema
|
|
16
26
|
type rowState = {
|
|
17
27
|
productId: @s.matches(Reventless.DcbTag.string) string,
|
|
18
28
|
status: string,
|
|
19
29
|
name: string,
|
|
30
|
+
geolocation?: geolocation,
|
|
20
31
|
}
|
|
21
32
|
|
|
22
33
|
let rowStateSchemaWithAnnotations =
|
|
@@ -85,6 +96,13 @@ let edgeNodeField = (edge: JSON.t, field: string): string =>
|
|
|
85
96
|
->Option.flatMap(n => n->getString(field))
|
|
86
97
|
->Option.getOr("")
|
|
87
98
|
|
|
99
|
+
let edges2Find = (edges: array<JSON.t>, pid: string): JSON.t =>
|
|
100
|
+
edges->Array.find(e => e->getField("node")->Option.flatMap(n => n->getString("productId")) === Some(pid))
|
|
101
|
+
->Option.getOr(JSON.Encode.null)
|
|
102
|
+
|
|
103
|
+
let edgeNodeJson = (edge: JSON.t, field: string): option<JSON.t> =>
|
|
104
|
+
edge->getField("node")->Option.flatMap(n => n->getField(field))
|
|
105
|
+
|
|
88
106
|
let pageInfo = (response: JSON.t): JSON.t =>
|
|
89
107
|
response->getField("pageInfo")->Option.getOr(JSON.Encode.null)
|
|
90
108
|
|
|
@@ -175,7 +193,13 @@ let buildFixture = async (~name: string, ~indexes: array<Reventless.ReadModel.in
|
|
|
175
193
|
]
|
|
176
194
|
for i in 0 to rows->Array.length - 1 {
|
|
177
195
|
let (pid, status, n) = rows->Array.getUnsafe(i)
|
|
178
|
-
|
|
196
|
+
// p-1 carries the union; the rest leave it absent, which also proves an
|
|
197
|
+
// absent optional union is not stamped into something.
|
|
198
|
+
let state: rowState =
|
|
199
|
+
pid === "p-1"
|
|
200
|
+
? {productId: pid, status, name: n, geolocation: Located({lat: 48.2082, lng: 16.3738})}
|
|
201
|
+
: {productId: pid, status, name: n}
|
|
202
|
+
let _ = await ops.save(pid, state, Init, None)
|
|
179
203
|
}
|
|
180
204
|
|
|
181
205
|
(resolver, listFieldName)
|
|
@@ -497,6 +521,36 @@ describe("QueryDb by-index resolver", () => {
|
|
|
497
521
|
expect(pageInfoBool(page2, "hasNextPage"))->toBe(false)
|
|
498
522
|
})
|
|
499
523
|
|
|
524
|
+
// The gap this fixture exists to close: a union field reaching a caller through
|
|
525
|
+
// the by-index door, carrying the `__typename` the write-time stamp put on it.
|
|
526
|
+
// A member without one resolves to null and takes its non-nullable parent with
|
|
527
|
+
// it, so the whole row would vanish rather than error.
|
|
528
|
+
testPromise("carries a union field's member type through", async () => {
|
|
529
|
+
let resolver = await indexResolver(~name="IdxU")
|
|
530
|
+
let response = await resolver(JSON.Encode.null, argsOf([("status", strJson("active"))]), emptyCtx)
|
|
531
|
+
let edges = getEdges(response)
|
|
532
|
+
let seeded =
|
|
533
|
+
edges->Array.find(e => edgeNodeField(e, "productId") === "p-1")->Option.getOr(JSON.Encode.null)
|
|
534
|
+
let geo = edgeNodeJson(seeded, "geolocation")->Option.flatMap(JSON.Decode.object)
|
|
535
|
+
expect(geo->Option.flatMap(o => o->Dict.get("__typename"))->Option.flatMap(JSON.Decode.string))
|
|
536
|
+
->toEqual(Some("GeolocationLocated"))
|
|
537
|
+
expect(
|
|
538
|
+
geo
|
|
539
|
+
->Option.flatMap(o => o->Dict.get("lat"))
|
|
540
|
+
->Option.flatMap(JSON.Decode.float),
|
|
541
|
+
)->toEqual(Some(48.2082))
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
// An absent optional union is left absent rather than stamped into an arm.
|
|
545
|
+
testPromise("leaves an absent union absent", async () => {
|
|
546
|
+
let resolver = await indexResolver(~name="IdxV")
|
|
547
|
+
let response = await resolver(JSON.Encode.null, argsOf([("status", strJson("active"))]), emptyCtx)
|
|
548
|
+
let other = edges2Find(getEdges(response), "p-2")
|
|
549
|
+
// Assert the row is real first, so an absent field cannot pass as an absent row.
|
|
550
|
+
expect(edgeNodeField(other, "name"))->toBe("Alpha")
|
|
551
|
+
expect(edgeNodeJson(other, "geolocation")->Option.isNone)->toBe(true)
|
|
552
|
+
})
|
|
553
|
+
|
|
500
554
|
// Refused rather than ignored, and refused here because AppSync cannot do it:
|
|
501
555
|
// a door that paged backward on one backend and quietly returned the forward
|
|
502
556
|
// page on the other is the divergence this whole field was rebuilt to remove.
|
|
@@ -11,6 +11,7 @@ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/component
|
|
|
11
11
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
12
12
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
13
13
|
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
14
|
+
import * as TaggedUnion$Reventless from "@reventlessdev/reventless-spec/src/components/TaggedUnion.res.mjs";
|
|
14
15
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
15
16
|
import * as LocalBus$ReventlessLocal from "../../src/adapter/LocalBus.res.mjs";
|
|
16
17
|
import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
|
|
@@ -26,10 +27,25 @@ import * as QueryDbResolvers_GraphQL$ReventlessLocal from "../../src/adapter/Que
|
|
|
26
27
|
|
|
27
28
|
TestRunner$ReventlessLocal.setup();
|
|
28
29
|
|
|
30
|
+
let geolocationSchema = Sury.union([
|
|
31
|
+
Sury.$schema(s => ({
|
|
32
|
+
TAG: "Pending",
|
|
33
|
+
requestedFor: s.m(Sury.string)
|
|
34
|
+
})),
|
|
35
|
+
Sury.$schema(s => ({
|
|
36
|
+
TAG: "Located",
|
|
37
|
+
lat: s.m(Sury.float),
|
|
38
|
+
lng: s.m(Sury.float)
|
|
39
|
+
}))
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
let geolocationSchema$1 = TaggedUnion$Reventless.named("Geolocation", geolocationSchema);
|
|
43
|
+
|
|
29
44
|
let rowStateSchema = Sury.$schema(s => ({
|
|
30
45
|
productId: s.m(DcbTag$Reventless.string),
|
|
31
46
|
status: s.m(Sury.string),
|
|
32
|
-
name: s.m(Sury.string)
|
|
47
|
+
name: s.m(Sury.string),
|
|
48
|
+
geolocation: s.m(Sury.$option(geolocationSchema$1))
|
|
33
49
|
}));
|
|
34
50
|
|
|
35
51
|
let rowStateSchemaWithAnnotations = Sury.$Metadata_set(rowStateSchema, StateAnnotations$Reventless.stateAnnotationsId, {
|
|
@@ -102,6 +118,14 @@ function edgeNodeField(edge, field) {
|
|
|
102
118
|
return Stdlib_Option.getOr(Stdlib_Option.flatMap(getField(edge, "node"), n => getString(n, field)), "");
|
|
103
119
|
}
|
|
104
120
|
|
|
121
|
+
function edges2Find(edges, pid) {
|
|
122
|
+
return Stdlib_Option.getOr(edges.find(e => Stdlib_Option.flatMap(getField(e, "node"), n => getString(n, "productId")) === pid), null);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function edgeNodeJson(edge, field) {
|
|
126
|
+
return Stdlib_Option.flatMap(getField(edge, "node"), n => getField(n, field));
|
|
127
|
+
}
|
|
128
|
+
|
|
105
129
|
function pageInfo(response) {
|
|
106
130
|
return Stdlib_Option.getOr(getField(response, "pageInfo"), null);
|
|
107
131
|
}
|
|
@@ -186,12 +210,24 @@ async function buildFixture(name, indexesOpt) {
|
|
|
186
210
|
];
|
|
187
211
|
for (let i = 0, i_finish = rows.length; i < i_finish; ++i) {
|
|
188
212
|
let match = rows[i];
|
|
213
|
+
let n = match[2];
|
|
214
|
+
let status = match[1];
|
|
189
215
|
let pid = match[0];
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
216
|
+
let state = pid === "p-1" ? ({
|
|
217
|
+
productId: pid,
|
|
218
|
+
status: status,
|
|
219
|
+
name: n,
|
|
220
|
+
geolocation: {
|
|
221
|
+
TAG: "Located",
|
|
222
|
+
lat: 48.2082,
|
|
223
|
+
lng: 16.3738
|
|
224
|
+
}
|
|
225
|
+
}) : ({
|
|
226
|
+
productId: pid,
|
|
227
|
+
status: status,
|
|
228
|
+
name: n
|
|
229
|
+
});
|
|
230
|
+
await ops.save(pid, state, "Init", undefined);
|
|
195
231
|
}
|
|
196
232
|
return [
|
|
197
233
|
resolver,
|
|
@@ -557,6 +593,28 @@ globalThis.describe("QueryDb by-index resolver", () => {
|
|
|
557
593
|
globalThis.expect(edgeNodeField(edges2[0], "productId")).toBe("p-4");
|
|
558
594
|
globalThis.expect(pageInfoBool(page2, "hasNextPage")).toBe(false);
|
|
559
595
|
});
|
|
596
|
+
globalThis.test("carries a union field's member type through", async () => {
|
|
597
|
+
let resolver = await indexResolver("IdxU");
|
|
598
|
+
let response = await resolver(null, Object.fromEntries([[
|
|
599
|
+
"status",
|
|
600
|
+
"active"
|
|
601
|
+
]]), emptyCtx);
|
|
602
|
+
let edges = getEdges(response);
|
|
603
|
+
let seeded = Stdlib_Option.getOr(edges.find(e => edgeNodeField(e, "productId") === "p-1"), null);
|
|
604
|
+
let geo = Stdlib_Option.flatMap(edgeNodeJson(seeded, "geolocation"), Stdlib_JSON.Decode.object);
|
|
605
|
+
globalThis.expect(Stdlib_Option.flatMap(Stdlib_Option.flatMap(geo, o => o["__typename"]), Stdlib_JSON.Decode.string)).toEqual("GeolocationLocated");
|
|
606
|
+
globalThis.expect(Stdlib_Option.flatMap(Stdlib_Option.flatMap(geo, o => o["lat"]), Stdlib_JSON.Decode.float)).toEqual(48.2082);
|
|
607
|
+
});
|
|
608
|
+
globalThis.test("leaves an absent union absent", async () => {
|
|
609
|
+
let resolver = await indexResolver("IdxV");
|
|
610
|
+
let response = await resolver(null, Object.fromEntries([[
|
|
611
|
+
"status",
|
|
612
|
+
"active"
|
|
613
|
+
]]), emptyCtx);
|
|
614
|
+
let other = edges2Find(getEdges(response), "p-2");
|
|
615
|
+
globalThis.expect(edgeNodeField(other, "name")).toBe("Alpha");
|
|
616
|
+
globalThis.expect(Stdlib_Option.isNone(edgeNodeJson(other, "geolocation"))).toBe(true);
|
|
617
|
+
});
|
|
560
618
|
globalThis.test("refuses backward paging instead of answering forward", async () => {
|
|
561
619
|
let resolver = await indexResolver("IdxC");
|
|
562
620
|
let outcome;
|
|
@@ -591,6 +649,7 @@ globalThis.describe("QueryDb by-index resolver", () => {
|
|
|
591
649
|
});
|
|
592
650
|
|
|
593
651
|
export {
|
|
652
|
+
geolocationSchema$1 as geolocationSchema,
|
|
594
653
|
rowStateSchema,
|
|
595
654
|
rowStateSchemaWithAnnotations,
|
|
596
655
|
emptyCtx,
|
|
@@ -602,6 +661,8 @@ export {
|
|
|
602
661
|
getEdges,
|
|
603
662
|
edgeCursor,
|
|
604
663
|
edgeNodeField,
|
|
664
|
+
edges2Find,
|
|
665
|
+
edgeNodeJson,
|
|
605
666
|
pageInfo,
|
|
606
667
|
pageInfoBool,
|
|
607
668
|
pageInfoString,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// A tagged-union state field, saved and read back under both local backends.
|
|
2
|
+
//
|
|
3
|
+
// The stamp that puts `__typename` beside `TAG` runs in QueryDb_Operations, so
|
|
4
|
+
// what a backend has to do is carry a nested object with one extra key and hand
|
|
5
|
+
// it back. Cheap to assume and cheap to check — and the failure it guards
|
|
6
|
+
// against is not a decode error but a union member that resolves to null, which
|
|
7
|
+
// takes its non-nullable parent with it and reads as data loss.
|
|
8
|
+
//
|
|
9
|
+
// The spec is declared here rather than shared with reventless-core's fixture:
|
|
10
|
+
// a package's `type: dev` sources are not visible to another package, and the
|
|
11
|
+
// union is four lines.
|
|
12
|
+
|
|
13
|
+
open JestGlobals
|
|
14
|
+
|
|
15
|
+
let _ = TestRunner.setup()
|
|
16
|
+
let opts: Pulumi.CustomResourceOptions.t = {}
|
|
17
|
+
|
|
18
|
+
@schema
|
|
19
|
+
type geolocation =
|
|
20
|
+
| Pending({requestedFor: string})
|
|
21
|
+
| Located({lat: float, lng: float})
|
|
22
|
+
| Unresolvable({reason: string})
|
|
23
|
+
|
|
24
|
+
let geolocationSchema = Reventless.TaggedUnion.named(~name="Geolocation", geolocationSchema)
|
|
25
|
+
|
|
26
|
+
module CustomersSpec = {
|
|
27
|
+
module Id = Reventless.Id.StringPure
|
|
28
|
+
let name = "UnionRoundTripCustomers"
|
|
29
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
30
|
+
|
|
31
|
+
@schema
|
|
32
|
+
type state = {customerId: string, geolocation: geolocation}
|
|
33
|
+
|
|
34
|
+
let config = Reventless.ReadModel.config()
|
|
35
|
+
let subIdConfig = None
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let runUnderMemory = async fn => {
|
|
39
|
+
BackendState.setMemory()
|
|
40
|
+
await fn()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let runUnderSqlite = async fn => {
|
|
44
|
+
let db = SqliteDriver.openDb(~path=":memory:")
|
|
45
|
+
BackendState.setSqlite(~db, ~path=":memory:")
|
|
46
|
+
await fn()
|
|
47
|
+
BackendState.setMemory()
|
|
48
|
+
db->SqliteDriver.close
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe("QueryDb round-trips a union field", () => {
|
|
52
|
+
let scenario = async () => {
|
|
53
|
+
module TestBus = LocalBus.Make()
|
|
54
|
+
module Storage = LocalQueryDbStorage.Make(TestBus)
|
|
55
|
+
let s = Storage.make(
|
|
56
|
+
~name="union-round-trip",
|
|
57
|
+
~indexes=[],
|
|
58
|
+
~api=(),
|
|
59
|
+
~apiRole=(),
|
|
60
|
+
~owner=None,
|
|
61
|
+
~opts,
|
|
62
|
+
)
|
|
63
|
+
let jsonOps = await s.operations->TestRunner.resolve
|
|
64
|
+
module Ops = ReventlessCore.QueryDb_Operations.Make(
|
|
65
|
+
CustomersSpec,
|
|
66
|
+
{
|
|
67
|
+
let jsonOps = jsonOps
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
let _ = await Ops.save(
|
|
72
|
+
"c-1",
|
|
73
|
+
{customerId: "c-1", geolocation: Located({lat: 48.2082, lng: 16.3738})},
|
|
74
|
+
ReventlessCore.QueryDb.Any,
|
|
75
|
+
None,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
// The stored row, as any read door would hand it back.
|
|
79
|
+
let stored =
|
|
80
|
+
await jsonOps.loadStream("c-1")
|
|
81
|
+
->Stream.runCollect
|
|
82
|
+
->Effect.catchAll(_ => Effect.succeed([]))
|
|
83
|
+
->Effect.runPromise
|
|
84
|
+
let typename =
|
|
85
|
+
stored
|
|
86
|
+
->Array.get(0)
|
|
87
|
+
->Option.flatMap(JSON.Decode.object)
|
|
88
|
+
->Option.flatMap(o => o->Dict.get("geolocation"))
|
|
89
|
+
->Option.flatMap(JSON.Decode.object)
|
|
90
|
+
->Option.flatMap(o => o->Dict.get("__typename"))
|
|
91
|
+
->Option.flatMap(JSON.Decode.string)
|
|
92
|
+
expect(typename)->toEqual(Some("GeolocationLocated"))
|
|
93
|
+
|
|
94
|
+
let items =
|
|
95
|
+
await Ops.loadStream("c-1"->CustomersSpec.Id.makeFromString)
|
|
96
|
+
->Stream.runCollect
|
|
97
|
+
->Effect.catchAll(_ => Effect.succeed([]))
|
|
98
|
+
->Effect.runPromise
|
|
99
|
+
switch items {
|
|
100
|
+
| [{geolocation: Located({lat, lng})}] => expect((lat, lng))->toEqual((48.2082, 16.3738))
|
|
101
|
+
| _ => expect("round trip")->toBe("Located with its point intact")
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
testPromise("in-memory", () => runUnderMemory(scenario))
|
|
106
|
+
testPromise("sqlite", () => runUnderSqlite(scenario))
|
|
107
|
+
})
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Sury from "sury";
|
|
4
|
+
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
5
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
6
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
7
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
10
|
+
import * as TaggedUnion$Reventless from "@reventlessdev/reventless-spec/src/components/TaggedUnion.res.mjs";
|
|
11
|
+
import * as LocalBus$ReventlessLocal from "../../src/adapter/LocalBus.res.mjs";
|
|
12
|
+
import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
|
|
13
|
+
import * as BackendState$ReventlessLocal from "../../src/adapter/BackendState.res.mjs";
|
|
14
|
+
import * as SqliteDriver$ReventlessLocal from "../../src/adapter/SqliteDriver.res.mjs";
|
|
15
|
+
import * as QueryDb_Operations$ReventlessCore from "@reventlessdev/reventless-core/src/components/QueryDb/QueryDb_Operations.res.mjs";
|
|
16
|
+
import * as LocalQueryDbStorage$ReventlessLocal from "../../src/adapter/QueryDb/LocalQueryDbStorage.res.mjs";
|
|
17
|
+
|
|
18
|
+
TestRunner$ReventlessLocal.setup();
|
|
19
|
+
|
|
20
|
+
let opts = {};
|
|
21
|
+
|
|
22
|
+
let geolocationSchema = Sury.union([
|
|
23
|
+
Sury.$schema(s => ({
|
|
24
|
+
TAG: "Pending",
|
|
25
|
+
requestedFor: s.m(Sury.string)
|
|
26
|
+
})),
|
|
27
|
+
Sury.$schema(s => ({
|
|
28
|
+
TAG: "Located",
|
|
29
|
+
lat: s.m(Sury.float),
|
|
30
|
+
lng: s.m(Sury.float)
|
|
31
|
+
})),
|
|
32
|
+
Sury.$schema(s => ({
|
|
33
|
+
TAG: "Unresolvable",
|
|
34
|
+
reason: s.m(Sury.string)
|
|
35
|
+
}))
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
let geolocationSchema$1 = TaggedUnion$Reventless.named("Geolocation", geolocationSchema);
|
|
39
|
+
|
|
40
|
+
let name = "UnionRoundTripCustomers";
|
|
41
|
+
|
|
42
|
+
let moduleUrl = import.meta.url;
|
|
43
|
+
|
|
44
|
+
let stateSchema = Sury.$schema(s => ({
|
|
45
|
+
customerId: s.m(Sury.string),
|
|
46
|
+
geolocation: s.m(geolocationSchema$1)
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
let config = ReadModel$Reventless.config(undefined, undefined, undefined);
|
|
50
|
+
|
|
51
|
+
let CustomersSpec = {
|
|
52
|
+
Id: undefined,
|
|
53
|
+
name: name,
|
|
54
|
+
moduleUrl: moduleUrl,
|
|
55
|
+
stateSchema: stateSchema,
|
|
56
|
+
config: config,
|
|
57
|
+
subIdConfig: undefined,
|
|
58
|
+
authorization: "AllowAuthenticated",
|
|
59
|
+
visibility: "Public"
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
async function runUnderMemory(fn) {
|
|
63
|
+
BackendState$ReventlessLocal.setMemory();
|
|
64
|
+
return await fn();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function runUnderSqlite(fn) {
|
|
68
|
+
let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
|
|
69
|
+
BackendState$ReventlessLocal.setSqlite(db, ":memory:");
|
|
70
|
+
await fn();
|
|
71
|
+
BackendState$ReventlessLocal.setMemory();
|
|
72
|
+
return SqliteDriver$ReventlessLocal.close(db);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
globalThis.describe("QueryDb round-trips a union field", () => {
|
|
76
|
+
let scenario = async () => {
|
|
77
|
+
let TestBus = LocalBus$ReventlessLocal.Make({});
|
|
78
|
+
let Storage = LocalQueryDbStorage$ReventlessLocal.Make(TestBus);
|
|
79
|
+
let s = Storage.make("union-round-trip", [], undefined, undefined, undefined, undefined, undefined, opts);
|
|
80
|
+
let jsonOps = await TestRunner$ReventlessLocal.resolve(s.operations);
|
|
81
|
+
let Ops = QueryDb_Operations$ReventlessCore.Make({
|
|
82
|
+
Id: {
|
|
83
|
+
schema: Id$Reventless.StringPure.schema,
|
|
84
|
+
make: prim => prim,
|
|
85
|
+
makeFromString: prim => prim,
|
|
86
|
+
toString: prim => prim,
|
|
87
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
88
|
+
},
|
|
89
|
+
name: name,
|
|
90
|
+
moduleUrl: moduleUrl,
|
|
91
|
+
stateSchema: stateSchema,
|
|
92
|
+
config: config,
|
|
93
|
+
subIdConfig: undefined,
|
|
94
|
+
authorization: "AllowAuthenticated",
|
|
95
|
+
visibility: "Public"
|
|
96
|
+
})({
|
|
97
|
+
jsonOps: jsonOps
|
|
98
|
+
});
|
|
99
|
+
await Ops.save("c-1", {
|
|
100
|
+
customerId: "c-1",
|
|
101
|
+
geolocation: {
|
|
102
|
+
TAG: "Located",
|
|
103
|
+
lat: 48.2082,
|
|
104
|
+
lng: 16.3738
|
|
105
|
+
}
|
|
106
|
+
}, "Any", undefined);
|
|
107
|
+
let stored = await Effect.runPromise(Effect.catchAll(Stream.runCollect(jsonOps.loadStream("c-1")), param => Effect.succeed([])));
|
|
108
|
+
let typename = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(stored[0], Stdlib_JSON.Decode.object), o => o["geolocation"]), Stdlib_JSON.Decode.object), o => o["__typename"]), Stdlib_JSON.Decode.string);
|
|
109
|
+
globalThis.expect(typename).toEqual("GeolocationLocated");
|
|
110
|
+
let items = await Effect.runPromise(Effect.catchAll(Stream.runCollect(Ops.loadStream("c-1")), param => Effect.succeed([])));
|
|
111
|
+
if (items.length === 1) {
|
|
112
|
+
let match = items[0];
|
|
113
|
+
let match$1 = match.geolocation;
|
|
114
|
+
switch (match$1.TAG) {
|
|
115
|
+
case "Located" :
|
|
116
|
+
globalThis.expect([
|
|
117
|
+
match$1.lat,
|
|
118
|
+
match$1.lng
|
|
119
|
+
]).toEqual([
|
|
120
|
+
48.2082,
|
|
121
|
+
16.3738
|
|
122
|
+
]);
|
|
123
|
+
return;
|
|
124
|
+
case "Pending" :
|
|
125
|
+
case "Unresolvable" :
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
globalThis.expect("round trip").toBe("Located with its point intact");
|
|
130
|
+
};
|
|
131
|
+
globalThis.test("in-memory", () => runUnderMemory(scenario));
|
|
132
|
+
globalThis.test("sqlite", () => runUnderSqlite(scenario));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
opts,
|
|
137
|
+
geolocationSchema$1 as geolocationSchema,
|
|
138
|
+
CustomersSpec,
|
|
139
|
+
runUnderMemory,
|
|
140
|
+
runUnderSqlite,
|
|
141
|
+
}
|
|
142
|
+
/* Not a pure module */
|