@reventlessdev/reventless-core 3.0.0-alpha.253 → 3.0.0-alpha.255
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 +28 -0
- package/package.json +8 -8
- package/src/adapter/Messaging/Messaging_Log_Backend.res +98 -0
- package/src/adapter/Messaging/Messaging_Log_Backend.res.mjs +86 -0
- package/src/admin/Platform_ComponentDefinitionsApi.res +5 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res +55 -14
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +43 -12
- package/src/components/Api/SchemaType.res +1 -0
- package/src/components/Api/SchemaType.res.mjs +4 -0
- package/src/components/Api/SuryToJsonSchema.res +41 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +50 -17
- package/src/components/Dcb/Dcb_Builder.res +21 -4
- package/src/components/Dcb/Dcb_Builder.res.mjs +6 -3
- package/src/plugin/component/Plugin_Structure.res +13 -0
- package/src/plugin/component/Plugin_Structure.res.mjs +5 -1
- package/tests/admin/AdminApiSchemaDriftTest.res +1 -0
- package/tests/admin/AdminApiSchemaDriftTest.res.mjs +4 -1
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +2 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +4 -2
- package/tests/admin/Platform_PluginStructuresApiTest.res +32 -0
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +9 -0
- package/tests/api/GraphQL_FragmentGeneratorTest.res +47 -0
- package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +32 -0
- package/tests/api/SuryToJsonSchemaTest.res +157 -0
- package/tests/api/SuryToJsonSchemaTest.res.mjs +81 -0
- package/tests/plugin/PluginStructureTest.res +22 -0
- package/tests/plugin/PluginStructureTest.res.mjs +7 -0
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res +13 -3
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +7 -3
- package/tests/plugin/pluginDefinitionRequiredScalars.txt +6 -0
|
@@ -13,8 +13,11 @@ import * as Percent$Reventless from "@reventlessdev/reventless-spec/src/semantic
|
|
|
13
13
|
import * as Currency$Reventless from "@reventlessdev/reventless-spec/src/semantic/Currency.res.mjs";
|
|
14
14
|
import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
|
|
15
15
|
import * as GeoPoint$Reventless from "@reventlessdev/reventless-spec/src/semantic/GeoPoint.res.mjs";
|
|
16
|
+
import * as Semantic$Reventless from "@reventlessdev/reventless-spec/src/semantic/Semantic.res.mjs";
|
|
16
17
|
import * as DateRange$Reventless from "@reventlessdev/reventless-spec/src/semantic/DateRange.res.mjs";
|
|
18
|
+
import * as MemberRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/MemberRef.res.mjs";
|
|
17
19
|
import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
|
|
20
|
+
import * as Sensitive$Reventless from "@reventlessdev/reventless-spec/src/components/Sensitive.res.mjs";
|
|
18
21
|
import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
|
|
19
22
|
import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
|
|
20
23
|
import * as SuryToJsonSchema$ReventlessCore from "../../src/components/Api/SuryToJsonSchema.res.mjs";
|
|
@@ -130,6 +133,45 @@ globalThis.describe("SuryToJsonSchema:", () => {
|
|
|
130
133
|
globalThis.expect(ownerOf(optJson, "customerId")).toEqual(true);
|
|
131
134
|
});
|
|
132
135
|
});
|
|
136
|
+
globalThis.describe("deriveObjectSchema with a sensitive field:", () => {
|
|
137
|
+
let sensitiveOf = (json, name) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, "x-reventless-sensitive"));
|
|
138
|
+
let ownerOf = (json, name) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, "x-reventless-owner"));
|
|
139
|
+
let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
|
|
140
|
+
resetToken: s.m(Sensitive$Reventless.string),
|
|
141
|
+
note: s.m(Sury.string),
|
|
142
|
+
contact: s.m(Email$Reventless.schema)
|
|
143
|
+
})));
|
|
144
|
+
globalThis.test("the marked field carries x-reventless-sensitive", () => {
|
|
145
|
+
globalThis.expect(sensitiveOf(json, "resetToken")).toEqual(true);
|
|
146
|
+
});
|
|
147
|
+
globalThis.test("an unmarked string field carries nothing", () => {
|
|
148
|
+
globalThis.expect(sensitiveOf(json, "note")).toBe(undefined);
|
|
149
|
+
});
|
|
150
|
+
globalThis.test("an email field is sensitive with no annotation", () => {
|
|
151
|
+
globalThis.expect(sensitiveOf(json, "contact")).toEqual(true);
|
|
152
|
+
});
|
|
153
|
+
globalThis.test("the field is still a plain string on the wire", () => {
|
|
154
|
+
globalThis.expect(Stdlib_Option.flatMap(getPropertyOf(json, "resetToken"), s => getProperty(s, "type"))).toEqual("string");
|
|
155
|
+
});
|
|
156
|
+
globalThis.test("an optional sensitive field is still recognised", () => {
|
|
157
|
+
let optJson = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
|
|
158
|
+
resetToken: s.m(Sury.$option(Sensitive$Reventless.string))
|
|
159
|
+
})));
|
|
160
|
+
globalThis.expect(sensitiveOf(optJson, "resetToken")).toEqual(true);
|
|
161
|
+
});
|
|
162
|
+
globalThis.test("sensitivity composes with ownership on one field", () => {
|
|
163
|
+
let bothJson = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
|
|
164
|
+
customerId: s.m(Sensitive$Reventless.mark(Owner$Reventless.string))
|
|
165
|
+
})));
|
|
166
|
+
globalThis.expect([
|
|
167
|
+
sensitiveOf(bothJson, "customerId"),
|
|
168
|
+
ownerOf(bothJson, "customerId")
|
|
169
|
+
]).toEqual([
|
|
170
|
+
true,
|
|
171
|
+
true
|
|
172
|
+
]);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
133
175
|
globalThis.describe("deriveObjectSchema with stateAnnotations metadata:", () => {
|
|
134
176
|
globalThis.test("emits x-reventless-id on field listed in ids", () => {
|
|
135
177
|
let schema = Sury.$schema(s => ({
|
|
@@ -872,6 +914,45 @@ globalThis.describe("SuryToJsonSchema:", () => {
|
|
|
872
914
|
globalThis.expect(semanticOf(json, "imageUrl")).toEqual("storageRef");
|
|
873
915
|
});
|
|
874
916
|
});
|
|
917
|
+
globalThis.describe("a member-reference field:", () => {
|
|
918
|
+
let targetOf = (json, name) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, "x-reventless-semantic-target"));
|
|
919
|
+
globalThis.test("carries the collection, the view holding it, and what a member is", () => {
|
|
920
|
+
let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
|
|
921
|
+
productImage: s.m(MemberRef$Reventless.of_(undefined, "Products", Semantic$Reventless.Id.imageRef, "productImages"))
|
|
922
|
+
})));
|
|
923
|
+
globalThis.expect(Stdlib_Option.flatMap(getPropertyOf(json, "productImage"), s => getProperty(s, "x-reventless-semantic"))).toEqual("memberRef");
|
|
924
|
+
globalThis.expect(targetOf(json, "productImage")).toEqual(Object.fromEntries([
|
|
925
|
+
[
|
|
926
|
+
"field",
|
|
927
|
+
"productImages"
|
|
928
|
+
],
|
|
929
|
+
[
|
|
930
|
+
"view",
|
|
931
|
+
"Products"
|
|
932
|
+
],
|
|
933
|
+
[
|
|
934
|
+
"content",
|
|
935
|
+
"imageRef"
|
|
936
|
+
]
|
|
937
|
+
]));
|
|
938
|
+
});
|
|
939
|
+
globalThis.test("omits the view where the collection is on this very record", () => {
|
|
940
|
+
globalThis.expect(targetOf(SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
|
|
941
|
+
productImage: s.m(MemberRef$Reventless.of_(undefined, undefined, undefined, "productImages"))
|
|
942
|
+
}))), "productImage")).toEqual(Object.fromEntries([[
|
|
943
|
+
"field",
|
|
944
|
+
"productImages"
|
|
945
|
+
]]));
|
|
946
|
+
});
|
|
947
|
+
globalThis.test("declares no store, so nothing is provisioned for it", () => {
|
|
948
|
+
globalThis.expect(StorageRef$Reventless.getFieldStore(MemberRef$Reventless.of_(undefined, "Products", undefined, "productImages"))).toBe(undefined);
|
|
949
|
+
});
|
|
950
|
+
globalThis.test("stays a plain string on the wire", () => {
|
|
951
|
+
globalThis.expect(Stdlib_Option.flatMap(getPropertyOf(SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
|
|
952
|
+
productImage: s.m(MemberRef$Reventless.of_(undefined, undefined, undefined, "productImages"))
|
|
953
|
+
}))), "productImage"), s => getProperty(s, "type"))).toEqual("string");
|
|
954
|
+
});
|
|
955
|
+
});
|
|
875
956
|
globalThis.describe("branded scalars keep their underlying shape:", () => {
|
|
876
957
|
let keyOf = (json, name, key) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, key));
|
|
877
958
|
let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
|
|
@@ -1300,6 +1300,28 @@ describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
1300
1300
|
)->toBe(Some("currency"))
|
|
1301
1301
|
})
|
|
1302
1302
|
|
|
1303
|
+
testSync("@sensitive flows through the PPX to x-reventless-sensitive", () => {
|
|
1304
|
+
expect(
|
|
1305
|
+
annotatedSchema
|
|
1306
|
+
->getPropertyOf("contact")
|
|
1307
|
+
->Option.flatMap(s => getProperty(s, "x-reventless-sensitive"))
|
|
1308
|
+
->Option.flatMap(JSON.Decode.bool),
|
|
1309
|
+
)->toBe(Some(true))
|
|
1310
|
+
})
|
|
1311
|
+
|
|
1312
|
+
// The case the marker exists for. A renderer composes from an event payload,
|
|
1313
|
+
// and the annotation path `deriveObjectSchema` reads for state is
|
|
1314
|
+
// state-only — so a marker that lived there would be absent exactly where
|
|
1315
|
+
// the value is about to be interpolated into a message.
|
|
1316
|
+
testSync("@sensitive reaches an event variant's payload, not only state", () =>
|
|
1317
|
+
expect(
|
|
1318
|
+
Reventless.Sensitive.variantFieldNames(
|
|
1319
|
+
PsAnnotatedView.consumedEventSchema->S.castToUnknown,
|
|
1320
|
+
~variant="ItemRecorded",
|
|
1321
|
+
),
|
|
1322
|
+
)->toEqual(["contact"])
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1303
1325
|
testSync("@metric flows through the PPX to x-reventless-metric {aggregate,label}", () => {
|
|
1304
1326
|
let metricObj =
|
|
1305
1327
|
annotatedSchema
|
|
@@ -11,6 +11,7 @@ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/component
|
|
|
11
11
|
import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
|
|
12
12
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
13
13
|
import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
|
|
14
|
+
import * as Sensitive$Reventless from "@reventlessdev/reventless-spec/src/components/Sensitive.res.mjs";
|
|
14
15
|
import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
|
|
15
16
|
import * as Api_Naming$ReventlessCore from "../../src/components/Api/Api_Naming.res.mjs";
|
|
16
17
|
import * as PsShipOrder$ReventlessCore from "./StateChangeSlice/PsShipOrder.res.mjs";
|
|
@@ -1444,6 +1445,12 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
1444
1445
|
globalThis.test("@semantic(\"currency\") flows through the PPX to x-reventless-semantic", () => {
|
|
1445
1446
|
globalThis.expect(Stdlib_Option.flatMap(Stdlib_Option.flatMap(getPropertyOf(annotatedSchema, "total"), s => getProperty(s, "x-reventless-semantic")), Stdlib_JSON.Decode.string)).toBe("currency");
|
|
1446
1447
|
});
|
|
1448
|
+
globalThis.test("@sensitive flows through the PPX to x-reventless-sensitive", () => {
|
|
1449
|
+
globalThis.expect(Stdlib_Option.flatMap(Stdlib_Option.flatMap(getPropertyOf(annotatedSchema, "contact"), s => getProperty(s, "x-reventless-sensitive")), Stdlib_JSON.Decode.bool)).toBe(true);
|
|
1450
|
+
});
|
|
1451
|
+
globalThis.test("@sensitive reaches an event variant's payload, not only state", () => {
|
|
1452
|
+
globalThis.expect(Sensitive$Reventless.variantFieldNames(PsAnnotatedView$ReventlessCore.consumedEventSchema, "ItemRecorded")).toEqual(["contact"]);
|
|
1453
|
+
});
|
|
1447
1454
|
globalThis.test("@metric flows through the PPX to x-reventless-metric {aggregate,label}", () => {
|
|
1448
1455
|
let metricObj = Stdlib_Option.flatMap(getPropertyOf(annotatedSchema, "total"), s => getProperty(s, "x-reventless-metric"));
|
|
1449
1456
|
globalThis.expect([
|
|
@@ -7,7 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
@schema
|
|
9
9
|
type consumedEvent =
|
|
10
|
-
| ItemRecorded({
|
|
10
|
+
| ItemRecorded({
|
|
11
|
+
itemId: string,
|
|
12
|
+
ownerId: string,
|
|
13
|
+
version: string,
|
|
14
|
+
name: string,
|
|
15
|
+
total: float,
|
|
16
|
+
// On the event, not just the state: a renderer composes from an event
|
|
17
|
+
// payload, so this is the case the marker has to reach.
|
|
18
|
+
@sensitive contact: string,
|
|
19
|
+
})
|
|
11
20
|
|
|
12
21
|
@live(false)
|
|
13
22
|
@schema
|
|
@@ -17,10 +26,11 @@ type state = {
|
|
|
17
26
|
@index("byOwner") ownerId: string,
|
|
18
27
|
name: string,
|
|
19
28
|
@semantic("currency") @metric({aggregate: "sum", label: "Revenue"}) total: float,
|
|
29
|
+
@sensitive contact: string,
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
|
|
23
33
|
switch event {
|
|
24
|
-
| ItemRecorded({itemId, ownerId, version, name, total}) =>
|
|
25
|
-
[Set(itemId, {itemId, ownerId, version, name, total})]
|
|
34
|
+
| ItemRecorded({itemId, ownerId, version, name, total, contact}) =>
|
|
35
|
+
[Set(itemId, {itemId, ownerId, version, name, total, contact})]
|
|
26
36
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as Sury from "sury";
|
|
4
4
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
5
|
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
6
|
+
import * as Sensitive$Reventless from "@reventlessdev/reventless-spec/src/components/Sensitive.res.mjs";
|
|
6
7
|
import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
|
|
7
8
|
|
|
8
9
|
let consumedEventSchema = Sury.$schema(s => ({
|
|
@@ -11,7 +12,8 @@ let consumedEventSchema = Sury.$schema(s => ({
|
|
|
11
12
|
ownerId: s.m(DcbTag$Reventless.string),
|
|
12
13
|
version: s.m(Sury.string),
|
|
13
14
|
name: s.m(Sury.string),
|
|
14
|
-
total: s.m(Sury.float)
|
|
15
|
+
total: s.m(Sury.float),
|
|
16
|
+
contact: s.m(Sensitive$Reventless.string)
|
|
15
17
|
}));
|
|
16
18
|
|
|
17
19
|
let stateSchema = Sury.$schema(s => ({
|
|
@@ -19,7 +21,8 @@ let stateSchema = Sury.$schema(s => ({
|
|
|
19
21
|
version: s.m(Sury.string),
|
|
20
22
|
ownerId: s.m(Sury.string),
|
|
21
23
|
name: s.m(Sury.string),
|
|
22
|
-
total: s.m(Sury.float)
|
|
24
|
+
total: s.m(Sury.float),
|
|
25
|
+
contact: s.m(Sensitive$Reventless.string)
|
|
23
26
|
}));
|
|
24
27
|
|
|
25
28
|
function project(param) {
|
|
@@ -33,7 +36,8 @@ function project(param) {
|
|
|
33
36
|
version: event.version,
|
|
34
37
|
ownerId: event.ownerId,
|
|
35
38
|
name: event.name,
|
|
36
|
-
total: event.total
|
|
39
|
+
total: event.total,
|
|
40
|
+
contact: event.contact
|
|
37
41
|
}
|
|
38
42
|
}];
|
|
39
43
|
}
|
|
@@ -94,6 +94,12 @@
|
|
|
94
94
|
.structure.inboundTranslationSlices[].name: string
|
|
95
95
|
.structure.inboundTranslationSlices[].targetName: string
|
|
96
96
|
.structure.outboundTranslationSlices[].consumedEventTypes[]: string
|
|
97
|
+
# `consumedSources` is itself `js_nullable` and is NEW, so no persisted payload
|
|
98
|
+
# carries the array: a stored structure heals to null and this line is never
|
|
99
|
+
# reached — the same call `traitDeclarations` made below. Required inside it
|
|
100
|
+
# because a source name is a topic key the slice subscribes to, and a fabricated
|
|
101
|
+
# `""` would name a topic that does not exist rather than saying nothing.
|
|
102
|
+
.structure.outboundTranslationSlices[].consumedSources[]: string
|
|
97
103
|
.structure.outboundTranslationSlices[].inboundCommandTypes[]: string
|
|
98
104
|
.structure.outboundTranslationSlices[].name: string
|
|
99
105
|
.structure.readModels[].consumedEventTypes[]: string
|