@reventlessdev/reventless-aws 3.0.0-alpha.312 → 3.0.0-alpha.313
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 +7 -0
- package/package.json +6 -6
- package/tests/PgQueryDbUnion_IntegrationTest.res +101 -0
- package/tests/PgQueryDbUnion_IntegrationTest.res.mjs +139 -0
- package/tests/integration/AggIntegrationHarness.res +20 -0
- package/tests/integration/AggIntegrationHarness.res.mjs +30 -0
- package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +87 -0
- package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res.mjs +128 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.313 (2026-08-20)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **api:** emit a tagged-union state field as a GraphQL union ([3a380c0](https://github.com/ReventlessDev/reventless-core/commit/3a380c0ab055b87048d90a852ec1664f6aab6b00))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.312 (2026-08-19)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
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.313",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.1",
|
|
20
20
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
21
21
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
22
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
23
|
-
"@reventlessdev/reventless-
|
|
24
|
-
"@reventlessdev/reventless-
|
|
25
|
-
"@reventlessdev/reventless-
|
|
26
|
-
"@reventlessdev/reventless-
|
|
22
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.242",
|
|
23
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.106",
|
|
24
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.119",
|
|
25
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.32",
|
|
26
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.147"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"rescript": "12.3.0",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// A tagged-union state field, saved and read back through the real Postgres
|
|
2
|
+
// QueryDb path.
|
|
3
|
+
//
|
|
4
|
+
// Run via `pnpm run test:integration:pg` (boots a Postgres sidecar, runs the PG
|
|
5
|
+
// suites serially, tears down). Self-skips unless PG_URL is set.
|
|
6
|
+
//
|
|
7
|
+
// The Postgres resolver is the one backend that stamps a `__typename` of its own
|
|
8
|
+
// — the top-level row's ([PgQueryResolver_Lambda]) — and it stamps only that one.
|
|
9
|
+
// A union field sits one level in, which is precisely why the member stamp is
|
|
10
|
+
// written by the *write* path instead: what this suite proves is that the key
|
|
11
|
+
// survives the jsonb round trip, so the resolver has something to return.
|
|
12
|
+
|
|
13
|
+
open JestGlobals
|
|
14
|
+
|
|
15
|
+
@schema
|
|
16
|
+
type geolocation =
|
|
17
|
+
| Pending({requestedFor: string})
|
|
18
|
+
| Located({lat: float, lng: float})
|
|
19
|
+
| Unresolvable({reason: string})
|
|
20
|
+
|
|
21
|
+
let geolocationSchema = Reventless.TaggedUnion.named(~name="Geolocation", geolocationSchema)
|
|
22
|
+
|
|
23
|
+
module CustomersSpec = {
|
|
24
|
+
module Id = Reventless.Id.StringPure
|
|
25
|
+
let name = "PgUnionCustomers"
|
|
26
|
+
let moduleUrl = ""
|
|
27
|
+
|
|
28
|
+
@schema
|
|
29
|
+
type state = {customerId: string, geolocation: geolocation}
|
|
30
|
+
|
|
31
|
+
let config = Reventless.ReadModel.config()
|
|
32
|
+
let subIdConfig = None
|
|
33
|
+
let authorization: Reventless.Authorization.permission = AllowAuthenticated
|
|
34
|
+
let visibility: Reventless.Visibility.t = Public
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
switch NodeProcess.env->Dict.get("PG_URL") {
|
|
38
|
+
| None =>
|
|
39
|
+
testSync("Postgres union round trip (skipped — set PG_URL to run)", () =>
|
|
40
|
+
expect(true)->toBe(true)
|
|
41
|
+
)
|
|
42
|
+
| Some(url) =>
|
|
43
|
+
let pool = ReventlessPostgres.PgDriver.makePool({connectionString: url})
|
|
44
|
+
|
|
45
|
+
let jsonOps = ReventlessPostgres.QueryDbStorage_Postgres.makeOperations(
|
|
46
|
+
~pool,
|
|
47
|
+
~name=CustomersSpec.name,
|
|
48
|
+
~indexes=[],
|
|
49
|
+
~subIdField=None,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
module Ops = ReventlessCore.QueryDb_Operations.Make(
|
|
53
|
+
CustomersSpec,
|
|
54
|
+
{
|
|
55
|
+
let jsonOps = jsonOps
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
beforeAllAsync(async () => {
|
|
60
|
+
await ReventlessPostgres.PgSchema.ensureSchema(pool)
|
|
61
|
+
await ReventlessPostgres.PgSchema.truncateAll(pool)
|
|
62
|
+
let _ = await pool->ReventlessPostgres.PgDriver.query(
|
|
63
|
+
`DROP TABLE IF EXISTS qdb_${CustomersSpec.name}`,
|
|
64
|
+
[],
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
afterAll(() => {
|
|
68
|
+
let _ = pool->ReventlessPostgres.PgDriver.endPool
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe("QueryDb (Postgres) round-trips a union field", () => {
|
|
72
|
+
testPromise("a stored Located keeps its member type and its point", async () => {
|
|
73
|
+
let _ = await Ops.save(
|
|
74
|
+
"c-1"->CustomersSpec.Id.makeFromString,
|
|
75
|
+
{customerId: "c-1", geolocation: Located({lat: 48.2082, lng: 16.3738})},
|
|
76
|
+
ReventlessCore.QueryDb.Any,
|
|
77
|
+
None,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
switch await jsonOps.load("c-1") {
|
|
81
|
+
| Ok(rows) =>
|
|
82
|
+
let typename =
|
|
83
|
+
rows
|
|
84
|
+
->Array.get(0)
|
|
85
|
+
->Option.flatMap(JSON.Decode.object)
|
|
86
|
+
->Option.flatMap(o => o->Dict.get("geolocation"))
|
|
87
|
+
->Option.flatMap(JSON.Decode.object)
|
|
88
|
+
->Option.flatMap(o => o->Dict.get("__typename"))
|
|
89
|
+
->Option.flatMap(JSON.Decode.string)
|
|
90
|
+
expect(typename)->toEqual(Some("GeolocationLocated"))
|
|
91
|
+
| Error(_) => expect("load failed")->toBe("Ok")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
switch await Ops.load("c-1"->CustomersSpec.Id.makeFromString) {
|
|
95
|
+
| Ok([{geolocation: Located({lat, lng})}]) =>
|
|
96
|
+
expect((lat, lng))->toEqual((48.2082, 16.3738))
|
|
97
|
+
| _ => expect("round trip")->toBe("Located with its point intact")
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Sury from "sury";
|
|
4
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
8
|
+
import * as TaggedUnion$Reventless from "@reventlessdev/reventless-spec/src/components/TaggedUnion.res.mjs";
|
|
9
|
+
import * as PgDriver$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgDriver.res.mjs";
|
|
10
|
+
import * as PgSchema$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgSchema.res.mjs";
|
|
11
|
+
import * as QueryDb_Operations$ReventlessCore from "@reventlessdev/reventless-core/src/components/QueryDb/QueryDb_Operations.res.mjs";
|
|
12
|
+
import * as QueryDbStorage_Postgres$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/QueryDbStorage_Postgres.res.mjs";
|
|
13
|
+
|
|
14
|
+
let geolocationSchema = Sury.union([
|
|
15
|
+
Sury.$schema(s => ({
|
|
16
|
+
TAG: "Pending",
|
|
17
|
+
requestedFor: s.m(Sury.string)
|
|
18
|
+
})),
|
|
19
|
+
Sury.$schema(s => ({
|
|
20
|
+
TAG: "Located",
|
|
21
|
+
lat: s.m(Sury.float),
|
|
22
|
+
lng: s.m(Sury.float)
|
|
23
|
+
})),
|
|
24
|
+
Sury.$schema(s => ({
|
|
25
|
+
TAG: "Unresolvable",
|
|
26
|
+
reason: s.m(Sury.string)
|
|
27
|
+
}))
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
let geolocationSchema$1 = TaggedUnion$Reventless.named("Geolocation", geolocationSchema);
|
|
31
|
+
|
|
32
|
+
let name = "PgUnionCustomers";
|
|
33
|
+
|
|
34
|
+
let moduleUrl = "";
|
|
35
|
+
|
|
36
|
+
let stateSchema = Sury.$schema(s => ({
|
|
37
|
+
customerId: s.m(Sury.string),
|
|
38
|
+
geolocation: s.m(geolocationSchema$1)
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
let config = ReadModel$Reventless.config(undefined, undefined, undefined);
|
|
42
|
+
|
|
43
|
+
let CustomersSpec = {
|
|
44
|
+
Id: undefined,
|
|
45
|
+
name: name,
|
|
46
|
+
moduleUrl: moduleUrl,
|
|
47
|
+
stateSchema: stateSchema,
|
|
48
|
+
config: config,
|
|
49
|
+
subIdConfig: undefined,
|
|
50
|
+
authorization: "AllowAuthenticated",
|
|
51
|
+
visibility: "Public"
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let url = process.env["PG_URL"];
|
|
55
|
+
|
|
56
|
+
if (url !== undefined) {
|
|
57
|
+
let pool = PgDriver$ReventlessPostgres.makePool({
|
|
58
|
+
connectionString: url
|
|
59
|
+
});
|
|
60
|
+
let jsonOps = QueryDbStorage_Postgres$ReventlessPostgres.makeOperations(pool, name, [], undefined);
|
|
61
|
+
let Ops = QueryDb_Operations$ReventlessCore.Make({
|
|
62
|
+
Id: {
|
|
63
|
+
schema: Id$Reventless.StringPure.schema,
|
|
64
|
+
make: prim => prim,
|
|
65
|
+
makeFromString: prim => prim,
|
|
66
|
+
toString: prim => prim,
|
|
67
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
68
|
+
},
|
|
69
|
+
name: name,
|
|
70
|
+
moduleUrl: moduleUrl,
|
|
71
|
+
stateSchema: stateSchema,
|
|
72
|
+
config: config,
|
|
73
|
+
subIdConfig: undefined,
|
|
74
|
+
authorization: "AllowAuthenticated",
|
|
75
|
+
visibility: "Public"
|
|
76
|
+
})({
|
|
77
|
+
jsonOps: jsonOps
|
|
78
|
+
});
|
|
79
|
+
globalThis.beforeAll(async () => {
|
|
80
|
+
await PgSchema$ReventlessPostgres.ensureSchema(pool);
|
|
81
|
+
await PgSchema$ReventlessPostgres.truncateAll(pool);
|
|
82
|
+
await PgDriver$ReventlessPostgres.query(pool, `DROP TABLE IF EXISTS qdb_` + name, []);
|
|
83
|
+
});
|
|
84
|
+
globalThis.afterAll(() => {
|
|
85
|
+
PgDriver$ReventlessPostgres.endPool(pool);
|
|
86
|
+
});
|
|
87
|
+
globalThis.describe("QueryDb (Postgres) round-trips a union field", () => {
|
|
88
|
+
globalThis.test("a stored Located keeps its member type and its point", async () => {
|
|
89
|
+
await Ops.save("c-1", {
|
|
90
|
+
customerId: "c-1",
|
|
91
|
+
geolocation: {
|
|
92
|
+
TAG: "Located",
|
|
93
|
+
lat: 48.2082,
|
|
94
|
+
lng: 16.3738
|
|
95
|
+
}
|
|
96
|
+
}, "Any", undefined);
|
|
97
|
+
let rows = await jsonOps.load("c-1");
|
|
98
|
+
if (rows.TAG === "Ok") {
|
|
99
|
+
let typename = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(rows._0[0], Stdlib_JSON.Decode.object), o => o["geolocation"]), Stdlib_JSON.Decode.object), o => o["__typename"]), Stdlib_JSON.Decode.string);
|
|
100
|
+
globalThis.expect(typename).toEqual("GeolocationLocated");
|
|
101
|
+
} else {
|
|
102
|
+
globalThis.expect("load failed").toBe("Ok");
|
|
103
|
+
}
|
|
104
|
+
let match = await Ops.load("c-1");
|
|
105
|
+
if (match.TAG === "Ok") {
|
|
106
|
+
let match$1 = match._0;
|
|
107
|
+
if (match$1.length === 1) {
|
|
108
|
+
let match$2 = match$1[0];
|
|
109
|
+
let match$3 = match$2.geolocation;
|
|
110
|
+
switch (match$3.TAG) {
|
|
111
|
+
case "Located" :
|
|
112
|
+
globalThis.expect([
|
|
113
|
+
match$3.lat,
|
|
114
|
+
match$3.lng
|
|
115
|
+
]).toEqual([
|
|
116
|
+
48.2082,
|
|
117
|
+
16.3738
|
|
118
|
+
]);
|
|
119
|
+
return;
|
|
120
|
+
case "Pending" :
|
|
121
|
+
case "Unresolvable" :
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
globalThis.expect("round trip").toBe("Located with its point intact");
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
} else {
|
|
130
|
+
globalThis.test("Postgres union round trip (skipped — set PG_URL to run)", () => {
|
|
131
|
+
globalThis.expect(true).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
geolocationSchema$1 as geolocationSchema,
|
|
137
|
+
CustomersSpec,
|
|
138
|
+
}
|
|
139
|
+
/* geolocationSchema Not a pure module */
|
|
@@ -47,6 +47,26 @@ let createEventLogTable = async (tableName): Util_DynamoDb_Runtime.resolvedTable
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// A read-model table: `id` (HASH) alone. No range key — a projection with no
|
|
51
|
+
// `@subId` writes one row per id, which is the shape `QueryDbStorage_DynamoDb`
|
|
52
|
+
// provisions when `subIdField` is absent.
|
|
53
|
+
let createQueryDbTable = async (tableName): Util_DynamoDb_Runtime.resolvedTable => {
|
|
54
|
+
let input =
|
|
55
|
+
Dict.fromArray([
|
|
56
|
+
("TableName", s(tableName)),
|
|
57
|
+
("AttributeDefinitions", [attrDef("id")]->JSON.Encode.array),
|
|
58
|
+
("KeySchema", [keyEl("id", "HASH")]->JSON.Encode.array),
|
|
59
|
+
("BillingMode", s("PAY_PER_REQUEST")),
|
|
60
|
+
])->JSON.Encode.object
|
|
61
|
+
let _ = await send(createTableCommand(input))
|
|
62
|
+
{
|
|
63
|
+
Util_DynamoDb_Runtime.id: tableName,
|
|
64
|
+
name: tableName,
|
|
65
|
+
arn: `arn:aws:dynamodb:local:000000000000:table/${tableName}`,
|
|
66
|
+
hashKey: "id",
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
50
70
|
let deleteTable = async (table: Util_DynamoDb_Runtime.resolvedTable) => {
|
|
51
71
|
let input = Dict.fromArray([("TableName", s(table.name))])->JSON.Encode.object
|
|
52
72
|
let _ = await send(deleteTableCommand(input))
|
|
@@ -72,6 +72,35 @@ async function createEventLogTable(tableName) {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
async function createQueryDbTable(tableName) {
|
|
76
|
+
let input = Object.fromEntries([
|
|
77
|
+
[
|
|
78
|
+
"TableName",
|
|
79
|
+
tableName
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"AttributeDefinitions",
|
|
83
|
+
[attrDef("id")]
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
"KeySchema",
|
|
87
|
+
[keyEl("id", "HASH")]
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"BillingMode",
|
|
91
|
+
"PAY_PER_REQUEST"
|
|
92
|
+
]
|
|
93
|
+
]);
|
|
94
|
+
let cmd = new ClientDynamodb.CreateTableCommand(input);
|
|
95
|
+
await DynamoDb_DynamoDb$AwsSdk.client().send(cmd);
|
|
96
|
+
return {
|
|
97
|
+
id: tableName,
|
|
98
|
+
name: tableName,
|
|
99
|
+
arn: `arn:aws:dynamodb:local:000000000000:table/` + tableName,
|
|
100
|
+
hashKey: "id"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
75
104
|
async function deleteTable(table) {
|
|
76
105
|
let input = Object.fromEntries([[
|
|
77
106
|
"TableName",
|
|
@@ -87,6 +116,7 @@ export {
|
|
|
87
116
|
attrDef,
|
|
88
117
|
keyEl,
|
|
89
118
|
createEventLogTable,
|
|
119
|
+
createQueryDbTable,
|
|
90
120
|
deleteTable,
|
|
91
121
|
}
|
|
92
122
|
/* @aws-sdk/client-dynamodb Not a pure module */
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// A tagged-union state field, saved and read back through the real DynamoDB
|
|
2
|
+
// QueryDb path (DynamoDB Local — NOT run by the default unit suite; see
|
|
3
|
+
// `jest.integration.config.js` + `scripts/run-integration-tests.sh`).
|
|
4
|
+
//
|
|
5
|
+
// What a backend has to do here is carry a nested map with one extra key and
|
|
6
|
+
// hand it back unchanged. That is exactly what the AppSync resolvers rely on:
|
|
7
|
+
// they return the stored item as it is, and the `__typename` the write path
|
|
8
|
+
// stamped is the only thing that tells GraphQL which union member the value is.
|
|
9
|
+
// A backend that dropped or renamed the key would not produce a decode error —
|
|
10
|
+
// it would produce a member that resolves to null, and a null in a non-nullable
|
|
11
|
+
// field takes its parent row with it.
|
|
12
|
+
|
|
13
|
+
open JestGlobals
|
|
14
|
+
|
|
15
|
+
module H = AggIntegrationHarness
|
|
16
|
+
|
|
17
|
+
@schema
|
|
18
|
+
type geolocation =
|
|
19
|
+
| Pending({requestedFor: string})
|
|
20
|
+
| Located({lat: float, lng: float})
|
|
21
|
+
| Unresolvable({reason: string})
|
|
22
|
+
|
|
23
|
+
let geolocationSchema = Reventless.TaggedUnion.named(~name="Geolocation", geolocationSchema)
|
|
24
|
+
|
|
25
|
+
module CustomersSpec = {
|
|
26
|
+
module Id = Reventless.Id.StringPure
|
|
27
|
+
let name = "UnionRoundTripCustomers"
|
|
28
|
+
let moduleUrl = ""
|
|
29
|
+
|
|
30
|
+
@schema
|
|
31
|
+
type state = {customerId: string, geolocation: geolocation}
|
|
32
|
+
|
|
33
|
+
let config = Reventless.ReadModel.config()
|
|
34
|
+
let subIdConfig = None
|
|
35
|
+
let authorization: Reventless.Authorization.permission = AllowAuthenticated
|
|
36
|
+
let visibility: Reventless.Visibility.t = Public
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let tableName = "qdb-union-round-trip"
|
|
40
|
+
|
|
41
|
+
describe("QueryDb (DynamoDB) round-trips a union field", () => {
|
|
42
|
+
testPromise("a stored Located keeps its member type and its point", async () => {
|
|
43
|
+
let table = await H.createQueryDbTable(tableName)
|
|
44
|
+
let jsonOps = QueryDbEntryPoint_Ops.makeDynamoQueryDbOps(~tableName=table.name)
|
|
45
|
+
module Ops = ReventlessCore.QueryDb_Operations.Make(
|
|
46
|
+
CustomersSpec,
|
|
47
|
+
{
|
|
48
|
+
let jsonOps = jsonOps
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
let _ = await Ops.save(
|
|
53
|
+
"c-1",
|
|
54
|
+
{customerId: "c-1", geolocation: Located({lat: 48.2082, lng: 16.3738})},
|
|
55
|
+
ReventlessCore.QueryDb.Any,
|
|
56
|
+
None,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
// The item as a read door hands it back.
|
|
60
|
+
let stored =
|
|
61
|
+
await jsonOps.loadStream("c-1")
|
|
62
|
+
->Stream.runCollect
|
|
63
|
+
->Effect.catchAll(_ => Effect.succeed([]))
|
|
64
|
+
->Effect.runPromise
|
|
65
|
+
let typename =
|
|
66
|
+
stored
|
|
67
|
+
->Array.get(0)
|
|
68
|
+
->Option.flatMap(JSON.Decode.object)
|
|
69
|
+
->Option.flatMap(o => o->Dict.get("geolocation"))
|
|
70
|
+
->Option.flatMap(JSON.Decode.object)
|
|
71
|
+
->Option.flatMap(o => o->Dict.get("__typename"))
|
|
72
|
+
->Option.flatMap(JSON.Decode.string)
|
|
73
|
+
expect(typename)->toEqual(Some("GeolocationLocated"))
|
|
74
|
+
|
|
75
|
+
let items =
|
|
76
|
+
await Ops.loadStream("c-1"->CustomersSpec.Id.makeFromString)
|
|
77
|
+
->Stream.runCollect
|
|
78
|
+
->Effect.catchAll(_ => Effect.succeed([]))
|
|
79
|
+
->Effect.runPromise
|
|
80
|
+
switch items {
|
|
81
|
+
| [{geolocation: Located({lat, lng})}] => expect((lat, lng))->toEqual((48.2082, 16.3738))
|
|
82
|
+
| _ => expect("round trip")->toBe("Located with its point intact")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await H.deleteTable(table)
|
|
86
|
+
})
|
|
87
|
+
})
|
|
@@ -0,0 +1,128 @@
|
|
|
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 QueryDb_Operations$ReventlessCore from "@reventlessdev/reventless-core/src/components/QueryDb/QueryDb_Operations.res.mjs";
|
|
12
|
+
import * as AggIntegrationHarness$ReventlessAws from "./AggIntegrationHarness.res.mjs";
|
|
13
|
+
import * as QueryDbEntryPoint_Ops$ReventlessAws from "../../src/adapter/Runtime/QueryDbEntryPoint_Ops.res.mjs";
|
|
14
|
+
|
|
15
|
+
let geolocationSchema = Sury.union([
|
|
16
|
+
Sury.$schema(s => ({
|
|
17
|
+
TAG: "Pending",
|
|
18
|
+
requestedFor: s.m(Sury.string)
|
|
19
|
+
})),
|
|
20
|
+
Sury.$schema(s => ({
|
|
21
|
+
TAG: "Located",
|
|
22
|
+
lat: s.m(Sury.float),
|
|
23
|
+
lng: s.m(Sury.float)
|
|
24
|
+
})),
|
|
25
|
+
Sury.$schema(s => ({
|
|
26
|
+
TAG: "Unresolvable",
|
|
27
|
+
reason: s.m(Sury.string)
|
|
28
|
+
}))
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
let geolocationSchema$1 = TaggedUnion$Reventless.named("Geolocation", geolocationSchema);
|
|
32
|
+
|
|
33
|
+
let name = "UnionRoundTripCustomers";
|
|
34
|
+
|
|
35
|
+
let moduleUrl = "";
|
|
36
|
+
|
|
37
|
+
let stateSchema = Sury.$schema(s => ({
|
|
38
|
+
customerId: s.m(Sury.string),
|
|
39
|
+
geolocation: s.m(geolocationSchema$1)
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
let config = ReadModel$Reventless.config(undefined, undefined, undefined);
|
|
43
|
+
|
|
44
|
+
let CustomersSpec = {
|
|
45
|
+
Id: undefined,
|
|
46
|
+
name: name,
|
|
47
|
+
moduleUrl: moduleUrl,
|
|
48
|
+
stateSchema: stateSchema,
|
|
49
|
+
config: config,
|
|
50
|
+
subIdConfig: undefined,
|
|
51
|
+
authorization: "AllowAuthenticated",
|
|
52
|
+
visibility: "Public"
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
let tableName = "qdb-union-round-trip";
|
|
56
|
+
|
|
57
|
+
globalThis.describe("QueryDb (DynamoDB) round-trips a union field", () => {
|
|
58
|
+
globalThis.test("a stored Located keeps its member type and its point", async () => {
|
|
59
|
+
let table = await AggIntegrationHarness$ReventlessAws.createQueryDbTable(tableName);
|
|
60
|
+
let jsonOps = QueryDbEntryPoint_Ops$ReventlessAws.makeDynamoQueryDbOps(table.name);
|
|
61
|
+
let Ops = QueryDb_Operations$ReventlessCore.Make({
|
|
62
|
+
Id: {
|
|
63
|
+
schema: Id$Reventless.StringPure.schema,
|
|
64
|
+
make: prim => prim,
|
|
65
|
+
makeFromString: prim => prim,
|
|
66
|
+
toString: prim => prim,
|
|
67
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
68
|
+
},
|
|
69
|
+
name: name,
|
|
70
|
+
moduleUrl: moduleUrl,
|
|
71
|
+
stateSchema: stateSchema,
|
|
72
|
+
config: config,
|
|
73
|
+
subIdConfig: undefined,
|
|
74
|
+
authorization: "AllowAuthenticated",
|
|
75
|
+
visibility: "Public"
|
|
76
|
+
})({
|
|
77
|
+
jsonOps: jsonOps
|
|
78
|
+
});
|
|
79
|
+
await Ops.save("c-1", {
|
|
80
|
+
customerId: "c-1",
|
|
81
|
+
geolocation: {
|
|
82
|
+
TAG: "Located",
|
|
83
|
+
lat: 48.2082,
|
|
84
|
+
lng: 16.3738
|
|
85
|
+
}
|
|
86
|
+
}, "Any", undefined);
|
|
87
|
+
let stored = await Effect.runPromise(Effect.catchAll(Stream.runCollect(jsonOps.loadStream("c-1")), param => Effect.succeed([])));
|
|
88
|
+
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);
|
|
89
|
+
globalThis.expect(typename).toEqual("GeolocationLocated");
|
|
90
|
+
let items = await Effect.runPromise(Effect.catchAll(Stream.runCollect(Ops.loadStream("c-1")), param => Effect.succeed([])));
|
|
91
|
+
let exit = 0;
|
|
92
|
+
if (items.length !== 1) {
|
|
93
|
+
exit = 1;
|
|
94
|
+
} else {
|
|
95
|
+
let match = items[0];
|
|
96
|
+
let match$1 = match.geolocation;
|
|
97
|
+
switch (match$1.TAG) {
|
|
98
|
+
case "Located" :
|
|
99
|
+
globalThis.expect([
|
|
100
|
+
match$1.lat,
|
|
101
|
+
match$1.lng
|
|
102
|
+
]).toEqual([
|
|
103
|
+
48.2082,
|
|
104
|
+
16.3738
|
|
105
|
+
]);
|
|
106
|
+
break;
|
|
107
|
+
case "Pending" :
|
|
108
|
+
case "Unresolvable" :
|
|
109
|
+
exit = 1;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (exit === 1) {
|
|
114
|
+
globalThis.expect("round trip").toBe("Located with its point intact");
|
|
115
|
+
}
|
|
116
|
+
return await AggIntegrationHarness$ReventlessAws.deleteTable(table);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
let H;
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
H,
|
|
124
|
+
geolocationSchema$1 as geolocationSchema,
|
|
125
|
+
CustomersSpec,
|
|
126
|
+
tableName,
|
|
127
|
+
}
|
|
128
|
+
/* geolocationSchema Not a pure module */
|