@reventlessdev/reventless-aws 3.0.0-alpha.231 → 3.0.0-alpha.233
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 +7 -7
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
- package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -3
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +11 -6
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
- package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
- package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
- package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
- package/src/components/AutomationSlice_Builder.res +2 -0
- package/src/components/AutomationSlice_Builder.res.mjs +1 -1
- package/src/components/OutboundTranslationSlice_Builder.res +1 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
- package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
- package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
- package/tests/CounterEntryPoint_OpsTest.res +115 -0
- package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
- package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
- package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
- package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
- package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
- package/tests/Platform_AdminScan_OpsTest.res +80 -0
- package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
- package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
- package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
- package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
- package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
- package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
+
import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
|
|
6
|
+
import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
7
|
+
|
|
8
|
+
function setRequestId(prim) {
|
|
9
|
+
HandlerFactoryHelpersMjs.setRequestId(prim);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function runEffect(prim0, prim1) {
|
|
13
|
+
return HandlerFactoryHelpersMjs.runEffect(prim0, prim1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function extractMetaField(prim0, prim1) {
|
|
17
|
+
return HandlerFactoryHelpersMjs.extractMetaField(prim0, prim1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function extractSentTimestamp(prim) {
|
|
21
|
+
return HandlerFactoryHelpersMjs.extractSentTimestamp(prim);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function extractRetryCount(prim) {
|
|
25
|
+
return HandlerFactoryHelpersMjs.extractRetryCount(prim);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function logDebug(prim0, prim1) {
|
|
29
|
+
HandlerFactoryHelpersMjs.log.debug(prim0, prim1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseHandlerConfig(raw) {
|
|
33
|
+
return JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function makeCallbackSpec(config) {
|
|
37
|
+
return {
|
|
38
|
+
publishToAggregates: Object.fromEntries(Object.entries(Stdlib_Option.getOr(config.publishToAggregates, {})).map(param => {
|
|
39
|
+
let queueUrl = Stdlib_Option.getOr(process.env[param[1]], "");
|
|
40
|
+
let queue = {
|
|
41
|
+
id: queueUrl,
|
|
42
|
+
name: queueUrl,
|
|
43
|
+
arn: ""
|
|
44
|
+
};
|
|
45
|
+
return [
|
|
46
|
+
param[0],
|
|
47
|
+
CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO")
|
|
48
|
+
];
|
|
49
|
+
})),
|
|
50
|
+
commandTopicResources: [],
|
|
51
|
+
scheduler: {
|
|
52
|
+
createSchedule: (param, param$1) => Stdlib_JsError.throwWithMessage("Scheduler not available in bundled ExtensionPoint handler"),
|
|
53
|
+
deleteSchedule: (param, param$1) => Stdlib_JsError.throwWithMessage("Scheduler not available in bundled ExtensionPoint handler")
|
|
54
|
+
},
|
|
55
|
+
queryEngine: {
|
|
56
|
+
scan: (param, param$1, param$2) => Stdlib_JsError.throwWithMessage("QueryEngine not available in bundled ExtensionPoint handler"),
|
|
57
|
+
query: (param, param$1, param$2, param$3, param$4, param$5, param$6) => Stdlib_JsError.throwWithMessage("QueryEngine not available in bundled ExtensionPoint handler")
|
|
58
|
+
},
|
|
59
|
+
resourceNaming: {
|
|
60
|
+
validateName: n => n,
|
|
61
|
+
urnName: n => n
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function makeBuilt(config, specName, handleJsonCommands) {
|
|
67
|
+
let queueUrl = Stdlib_Option.getOr(config.queueUrl, "");
|
|
68
|
+
let queue = {
|
|
69
|
+
id: queueUrl,
|
|
70
|
+
name: queueUrl,
|
|
71
|
+
arn: ""
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
comp: `ExtensionPoint(` + specName + `)`,
|
|
75
|
+
sqsHandler: CommandTopicChannel_SQS_Runtime$ReventlessAws.handleQueueEvent(queue, handleJsonCommands)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function makeHandler(buildPromise) {
|
|
80
|
+
return async (event, context) => {
|
|
81
|
+
HandlerFactoryHelpersMjs.setRequestId(context.awsRequestId);
|
|
82
|
+
let match = await buildPromise;
|
|
83
|
+
let records = event.Records;
|
|
84
|
+
let prim0 = `processing ` + records.length.toString() + ` record(s)`;
|
|
85
|
+
HandlerFactoryHelpersMjs.log.debug(prim0, {
|
|
86
|
+
comp: "ExtensionPointRuntime"
|
|
87
|
+
});
|
|
88
|
+
await HandlerFactoryHelpersMjs.runEffect(match.sqsHandler(event, context), {
|
|
89
|
+
correlationId: HandlerFactoryHelpersMjs.extractMetaField(records, "correlationId"),
|
|
90
|
+
causationId: HandlerFactoryHelpersMjs.extractMetaField(records, "causationId"),
|
|
91
|
+
comp: match.comp,
|
|
92
|
+
timestamp: HandlerFactoryHelpersMjs.extractSentTimestamp(records),
|
|
93
|
+
retryCount: HandlerFactoryHelpersMjs.extractRetryCount(records)
|
|
94
|
+
});
|
|
95
|
+
return "";
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
setRequestId,
|
|
101
|
+
runEffect,
|
|
102
|
+
extractMetaField,
|
|
103
|
+
extractSentTimestamp,
|
|
104
|
+
extractRetryCount,
|
|
105
|
+
logDebug,
|
|
106
|
+
parseHandlerConfig,
|
|
107
|
+
makeCallbackSpec,
|
|
108
|
+
makeBuilt,
|
|
109
|
+
makeHandler,
|
|
110
|
+
}
|
|
111
|
+
/* ./HandlerFactoryHelpers.mjs Not a pure module */
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// PgChangeFeedRelay Lambda entry point (scheduled poll — B2.3) — compiled,
|
|
2
|
+
// type-checked ReScript (replaces the hand-written PgChangeFeedRelayEntryPoint.mjs
|
|
3
|
+
// shell; no dynamic user-module import, so no untyped seam is needed).
|
|
4
|
+
//
|
|
5
|
+
// Triggered on a schedule (EventBridge rate rule). Each invocation drains every
|
|
6
|
+
// Postgres log listed in HANDLER_CONFIG from its checkpoint — DCB (`dcb_event`)
|
|
7
|
+
// and classic aggregate (`event_log`) logs alike — and relays the events,
|
|
8
|
+
// transformed into the {id, meta, event} shape, onto the plugin EventCollector
|
|
9
|
+
// SQS queue — which then fans out to projections, aggregate command topics, and
|
|
10
|
+
// the cross-plugin SNS EventTopic.
|
|
11
|
+
// See docs/plans/done/aws-postgres-change-feed-bridge.md.
|
|
12
|
+
//
|
|
13
|
+
// Runtime-pure: `PgConnection.connectionConfig` is referenced as a type only
|
|
14
|
+
// (erased — no runtime import of the Pulumi-carrying PgConnection module);
|
|
15
|
+
// PgChangeFeedRelay_Runtime/Util_SQS_Runtime are runtime modules. Deploy-time
|
|
16
|
+
// wiring lives in PgChangeFeedRelay_Builder.
|
|
17
|
+
//
|
|
18
|
+
// HANDLER_CONFIG shape:
|
|
19
|
+
// { "logs": [ { "pgConnection": {host,port,database,username,secretArn},
|
|
20
|
+
// "logName": string, // dcb_event/event_log log_name
|
|
21
|
+
// "subscriber": string, // per-log checkpoint key
|
|
22
|
+
// "targetQueueUrl": string, // plugin EventCollector SQS URL
|
|
23
|
+
// "kind"?: "classic", // absent → DCB
|
|
24
|
+
// "partitionTag"?: <derivedPartitionTag> } ] } // DCB only
|
|
25
|
+
|
|
26
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
27
|
+
|
|
28
|
+
// Structured JSON logging shared by every deployed entry point (HandlerFactoryHelpers).
|
|
29
|
+
type logExtra = {comp?: string}
|
|
30
|
+
@module("./HandlerFactoryHelpers.mjs") @scope("log")
|
|
31
|
+
external logDebug: (string, logExtra) => unit = "debug"
|
|
32
|
+
@module("./HandlerFactoryHelpers.mjs") @scope("log")
|
|
33
|
+
external logWarn: (string, logExtra) => unit = "warn"
|
|
34
|
+
|
|
35
|
+
type relayLogConfig = {
|
|
36
|
+
pgConnection: PgConnection.connectionConfig,
|
|
37
|
+
logName: string,
|
|
38
|
+
subscriber: string,
|
|
39
|
+
targetQueueUrl: string,
|
|
40
|
+
// Absent → DCB (`dcb_event`); "classic" → aggregate `event_log`.
|
|
41
|
+
kind?: string,
|
|
42
|
+
// DCB only: the sury-encoded derived partition tag (parsed inside relayWithPool).
|
|
43
|
+
partitionTag?: JSON.t,
|
|
44
|
+
}
|
|
45
|
+
type handlerConfig = {logs?: array<relayLogConfig>}
|
|
46
|
+
@val @scope("JSON") external parseHandlerConfig: string => handlerConfig = "parse"
|
|
47
|
+
|
|
48
|
+
// Standard (non-FIFO) EventCollector queue — no message group id needed. The
|
|
49
|
+
// SQS handler parses each body straight back to JSON (Util_SQS_Runtime.parseSqsRecord).
|
|
50
|
+
let makeSendBatch = (targetQueueUrl: string): (array<JSON.t> => promise<unit>) => {
|
|
51
|
+
let queue: Util_SQS_Runtime.resolvedQueue = {id: targetQueueUrl, name: targetQueueUrl, arn: ""}
|
|
52
|
+
jsons =>
|
|
53
|
+
jsons
|
|
54
|
+
->Array.map(json => Util_SQS_Runtime.sendMessage(queue, json->JSON.stringify))
|
|
55
|
+
->Promise.all
|
|
56
|
+
->Promise.thenResolve(_ => ())
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Drain one log and relay it. Log-and-continue on failure — a failed log this
|
|
60
|
+
// tick is retried next tick from its unchanged checkpoint (at-least-once;
|
|
61
|
+
// projections are idempotent).
|
|
62
|
+
let relayLog = async (l: relayLogConfig): unit => {
|
|
63
|
+
let sendBatch = makeSendBatch(l.targetQueueUrl)
|
|
64
|
+
try {
|
|
65
|
+
let count = switch l.kind {
|
|
66
|
+
| Some("classic") =>
|
|
67
|
+
await PgChangeFeedRelay_Runtime.relayClassic(
|
|
68
|
+
~config=l.pgConnection,
|
|
69
|
+
~logName=l.logName,
|
|
70
|
+
~subscriber=l.subscriber,
|
|
71
|
+
~sendBatch,
|
|
72
|
+
)
|
|
73
|
+
| _ =>
|
|
74
|
+
await PgChangeFeedRelay_Runtime.relay(
|
|
75
|
+
~config=l.pgConnection,
|
|
76
|
+
~logName=l.logName,
|
|
77
|
+
~subscriber=l.subscriber,
|
|
78
|
+
~partitionTagJson=?l.partitionTag,
|
|
79
|
+
~sendBatch,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
logDebug(`relayed ${count->Int.toString} event(s) for ${l.logName}`, {comp: "PgChangeFeedRelay"})
|
|
83
|
+
} catch {
|
|
84
|
+
| exn =>
|
|
85
|
+
let msg = exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("unknown error")
|
|
86
|
+
logWarn(`relay failed for ${l.logName}: ${msg}`, {comp: "PgChangeFeedRelay"})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let handler = async (_event: JSON.t, _context: PulumiAws.Lambda.context) => {
|
|
91
|
+
let config =
|
|
92
|
+
processEnv->Dict.get("HANDLER_CONFIG")->Option.getOr(`{"logs":[]}`)->parseHandlerConfig
|
|
93
|
+
let logs = config.logs->Option.getOr([])
|
|
94
|
+
let _ = await logs->Array.map(relayLog)->Promise.all
|
|
95
|
+
""
|
|
96
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
6
|
+
import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
|
|
7
|
+
import * as Util_SQS_Runtime$ReventlessAws from "../../util/Util_SQS_Runtime.res.mjs";
|
|
8
|
+
import * as PgChangeFeedRelay_Runtime$ReventlessAws from "../Postgres/PgChangeFeedRelay_Runtime.res.mjs";
|
|
9
|
+
|
|
10
|
+
function logDebug(prim0, prim1) {
|
|
11
|
+
HandlerFactoryHelpersMjs.log.debug(prim0, prim1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function logWarn(prim0, prim1) {
|
|
15
|
+
HandlerFactoryHelpersMjs.log.warn(prim0, prim1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function makeSendBatch(targetQueueUrl) {
|
|
19
|
+
let queue = {
|
|
20
|
+
id: targetQueueUrl,
|
|
21
|
+
name: targetQueueUrl,
|
|
22
|
+
arn: ""
|
|
23
|
+
};
|
|
24
|
+
return jsons => Promise.all(jsons.map(json => Util_SQS_Runtime$ReventlessAws.sendMessage(queue, undefined, JSON.stringify(json)))).then(param => {});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function relayLog(l) {
|
|
28
|
+
let sendBatch = makeSendBatch(l.targetQueueUrl);
|
|
29
|
+
try {
|
|
30
|
+
let match = l.kind;
|
|
31
|
+
let count;
|
|
32
|
+
let exit = 0;
|
|
33
|
+
if (match === "classic") {
|
|
34
|
+
count = await PgChangeFeedRelay_Runtime$ReventlessAws.relayClassic(l.pgConnection, l.logName, l.subscriber, sendBatch);
|
|
35
|
+
} else {
|
|
36
|
+
exit = 1;
|
|
37
|
+
}
|
|
38
|
+
if (exit === 1) {
|
|
39
|
+
count = await PgChangeFeedRelay_Runtime$ReventlessAws.relay(l.pgConnection, l.logName, l.subscriber, l.partitionTag, sendBatch);
|
|
40
|
+
}
|
|
41
|
+
let prim0 = `relayed ` + count.toString() + ` event(s) for ` + l.logName;
|
|
42
|
+
HandlerFactoryHelpersMjs.log.debug(prim0, {
|
|
43
|
+
comp: "PgChangeFeedRelay"
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
} catch (raw_exn) {
|
|
47
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
48
|
+
let msg = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown error");
|
|
49
|
+
HandlerFactoryHelpersMjs.log.warn(`relay failed for ` + l.logName + `: ` + msg, {
|
|
50
|
+
comp: "PgChangeFeedRelay"
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function handler(_event, _context) {
|
|
57
|
+
let config = JSON.parse(Stdlib_Option.getOr(process.env["HANDLER_CONFIG"], `{"logs":[]}`));
|
|
58
|
+
let logs = Stdlib_Option.getOr(config.logs, []);
|
|
59
|
+
await Promise.all(logs.map(relayLog));
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
logDebug,
|
|
65
|
+
logWarn,
|
|
66
|
+
makeSendBatch,
|
|
67
|
+
relayLog,
|
|
68
|
+
handler,
|
|
69
|
+
}
|
|
70
|
+
/* Stdlib_JsExn Not a pure module */
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// One-shot Postgres schema-migration Lambda entry point (A3) — compiled,
|
|
2
|
+
// type-checked ReScript (replaces the hand-written PgMigrationEntryPoint.mjs
|
|
3
|
+
// shell; no dynamic user-module import, so no untyped seam is needed).
|
|
4
|
+
//
|
|
5
|
+
// Invoked once during `pulumi up` by an `aws.lambda.Invocation` resource, from
|
|
6
|
+
// INSIDE the DB's VPC, so the deploy runner needs no network path to a private
|
|
7
|
+
// RDS instance. Runs the exact idempotent `PgSchema.ensureSchema` the local
|
|
8
|
+
// backend runs at startup (reventless-local's `postgres` smart constructor), so
|
|
9
|
+
// the AWS schema is byte-identical to local — zero drift. Every statement is
|
|
10
|
+
// `IF NOT EXISTS`, so re-invocation (e.g. after a schema-DDL change bumps the
|
|
11
|
+
// Lambda layer) is safe.
|
|
12
|
+
//
|
|
13
|
+
// Runtime-pure: `PgConnection.connectionConfig` is referenced as a type only
|
|
14
|
+
// (erased — no runtime import of the Pulumi-carrying PgConnection module);
|
|
15
|
+
// PgRuntime/PgSchema are runtime modules. Deploy-time wiring lives in
|
|
16
|
+
// PgMigration_Builder.
|
|
17
|
+
//
|
|
18
|
+
// HANDLER_CONFIG shape:
|
|
19
|
+
// { "pgConnection": { host, port, database, username, secretArn } }
|
|
20
|
+
|
|
21
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
22
|
+
|
|
23
|
+
// Structured JSON logging shared by every deployed entry point (HandlerFactoryHelpers).
|
|
24
|
+
type logExtra = {comp?: string}
|
|
25
|
+
@module("./HandlerFactoryHelpers.mjs") @scope("log")
|
|
26
|
+
external logInfo: (string, logExtra) => unit = "info"
|
|
27
|
+
|
|
28
|
+
type handlerConfig = {pgConnection?: PgConnection.connectionConfig}
|
|
29
|
+
@val @scope("JSON") external parseHandlerConfig: string => handlerConfig = "parse"
|
|
30
|
+
|
|
31
|
+
// Config parse → pool → ensureSchema, with the pool source injectable so the
|
|
32
|
+
// orchestration is integration-testable against a real Postgres without the
|
|
33
|
+
// Secrets Manager round-trip (mirrors PgChangeFeedRelay_Runtime's
|
|
34
|
+
// relayWithPool/relay split). `~makePool` defaults to the production `poolFor`,
|
|
35
|
+
// which memoises one pool per secret ARN per container and resolves the
|
|
36
|
+
// RDS-managed password from Secrets Manager via a cached provider.
|
|
37
|
+
let runMigration = async (
|
|
38
|
+
config: handlerConfig,
|
|
39
|
+
~makePool: PgConnection.connectionConfig => ReventlessPostgres.PgDriver.pool=PgRuntime.poolFor,
|
|
40
|
+
): string => {
|
|
41
|
+
switch config.pgConnection {
|
|
42
|
+
| None => JsError.throwWithMessage("PgMigrationEntryPoint: HANDLER_CONFIG has no pgConnection")
|
|
43
|
+
| Some(conn) =>
|
|
44
|
+
let pool = makePool(conn)
|
|
45
|
+
await ReventlessPostgres.PgSchema.ensureSchema(pool)
|
|
46
|
+
logInfo(`ensureSchema completed for ${conn.database}`, {comp: "PgMigration"})
|
|
47
|
+
"ok"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let handler = async (_event: JSON.t, _context: PulumiAws.Lambda.context) => {
|
|
52
|
+
let config = processEnv->Dict.get("HANDLER_CONFIG")->Option.getOr("{}")->parseHandlerConfig
|
|
53
|
+
await runMigration(config)
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
+
import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
|
|
6
|
+
import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
|
|
7
|
+
import * as PgSchema$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgSchema.res.mjs";
|
|
8
|
+
|
|
9
|
+
function logInfo(prim0, prim1) {
|
|
10
|
+
HandlerFactoryHelpersMjs.log.info(prim0, prim1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function runMigration(config, makePoolOpt) {
|
|
14
|
+
let makePool = makePoolOpt !== undefined ? makePoolOpt : PgRuntime$ReventlessAws.poolFor;
|
|
15
|
+
let conn = config.pgConnection;
|
|
16
|
+
if (conn === undefined) {
|
|
17
|
+
return Stdlib_JsError.throwWithMessage("PgMigrationEntryPoint: HANDLER_CONFIG has no pgConnection");
|
|
18
|
+
}
|
|
19
|
+
let pool = makePool(conn);
|
|
20
|
+
await PgSchema$ReventlessPostgres.ensureSchema(pool);
|
|
21
|
+
HandlerFactoryHelpersMjs.log.info(`ensureSchema completed for ` + conn.database, {
|
|
22
|
+
comp: "PgMigration"
|
|
23
|
+
});
|
|
24
|
+
return "ok";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function handler(_event, _context) {
|
|
28
|
+
let config = JSON.parse(Stdlib_Option.getOr(process.env["HANDLER_CONFIG"], "{}"));
|
|
29
|
+
return await runMigration(config, undefined);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
logInfo,
|
|
34
|
+
runMigration,
|
|
35
|
+
handler,
|
|
36
|
+
}
|
|
37
|
+
/* PgRuntime-ReventlessAws Not a pure module */
|
|
@@ -4,78 +4,43 @@
|
|
|
4
4
|
// fields (they have no per-table AppSync data source). At cold start it reads
|
|
5
5
|
// QUERY_RESOLVER_CONFIG (one shared pgConnection + one handler per read model),
|
|
6
6
|
// dynamically imports each spec package, and registers a per-read-model
|
|
7
|
-
// `binding`
|
|
8
|
-
//
|
|
9
|
-
// PgQueryResolver_Lambda. AppSync invokes `handler` with the resolver payload
|
|
10
|
-
// { readModelName, kind, index?, arguments, identity }, which
|
|
7
|
+
// `binding` into PgQueryResolver_Lambda. AppSync invokes `handler` with the
|
|
8
|
+
// resolver payload { readModelName, kind, index?, arguments, identity }, which
|
|
11
9
|
// PgQueryResolver_Lambda.dispatch routes.
|
|
12
10
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
import { register, registerNodeType, handler as dispatchHandler } from "@reventlessdev/reventless-aws/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs";
|
|
11
|
+
// Thin untyped shell ("typed core, thin shell" —
|
|
12
|
+
// docs/plans/minimize-lambda-entrypoint-mjs-shell.md): this file owns ONLY the
|
|
13
|
+
// seams that are inherently untyped — the dynamic `import()` of the spec
|
|
14
|
+
// packages named in QUERY_RESOLVER_CONFIG, the `patchSpecId` fix-up, and the
|
|
15
|
+
// reads of the runtime-loaded modules' exports. QUERY_RESOLVER_CONFIG parsing,
|
|
16
|
+
// the pool/engine construction, the push-down record, and binding registration
|
|
17
|
+
// live type-checked in PgQueryResolverEntryPoint_Ops.res.
|
|
18
|
+
|
|
19
|
+
import { handler as dispatchHandler } from "@reventlessdev/reventless-aws/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs";
|
|
23
20
|
import { patchSpecId, log } from "./HandlerFactoryHelpers.mjs";
|
|
21
|
+
import * as Ops from "./PgQueryResolverEntryPoint_Ops.res.mjs";
|
|
24
22
|
|
|
25
23
|
const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
|
|
26
24
|
|
|
27
|
-
// A bounded full-materialisation for the list fallback (shapes listPage
|
|
28
|
-
// declines). Kept high; the fallback is only hit for search/searchPrefix/ids/
|
|
29
|
-
// backward pagination, which the AutoUI does not issue for large models.
|
|
30
|
-
const SCAN_ALL_LIMIT = 100000;
|
|
31
|
-
|
|
32
25
|
async function buildAllBindings() {
|
|
33
|
-
const
|
|
34
|
-
const config = JSON.parse(configStr);
|
|
26
|
+
const config = Ops.parseResolverConfig(process.env["QUERY_RESOLVER_CONFIG"] || "");
|
|
35
27
|
const pgConnection = config.pgConnection;
|
|
36
28
|
if (!pgConnection) {
|
|
37
29
|
log.warn("no pgConnection in QUERY_RESOLVER_CONFIG", { comp: "PgQueryResolver" });
|
|
38
30
|
return;
|
|
39
31
|
}
|
|
40
|
-
// Container-lifetime pool (memoised by poolFor); the engine and the ops set
|
|
41
|
-
// share it, so a Lambda holds one pool regardless of read model count.
|
|
42
|
-
const pool = poolFor(pgConnection);
|
|
43
|
-
const engine = makeEngine({ pool });
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
registerNodeType(typeName, rm);
|
|
48
|
-
}
|
|
33
|
+
Ops.registerNodeTypes(config);
|
|
34
|
+
const pushdowns = Ops.makePushdowns(pgConnection);
|
|
49
35
|
|
|
50
|
-
|
|
51
|
-
indexLookup: engine.indexLookup,
|
|
52
|
-
byIds: engine.byIds,
|
|
53
|
-
listPage: engine.listPage,
|
|
54
|
-
itemsPage: engine.itemsPage,
|
|
55
|
-
scanAll: (readModelName) => engine.scan(readModelName, [], SCAN_ALL_LIMIT),
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
await Promise.all((config.handlers || []).map(async h => {
|
|
36
|
+
await Promise.all(config.handlers.map(async h => {
|
|
59
37
|
const specModule = patchSpecId(await dynamicImport(h.specModule));
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
register(h.readModelName, {
|
|
66
|
-
ops,
|
|
67
|
-
pushdowns,
|
|
68
|
-
indexes,
|
|
69
|
-
subIdField,
|
|
70
|
-
capability,
|
|
71
|
-
labelField: h.labelField,
|
|
72
|
-
includeIdParam: h.includeIdParam,
|
|
73
|
-
// Compiled string variant (e.g. "AllowAuthenticated"), injected on the
|
|
74
|
-
// spec by @@reventless.spec — the shape Reventless.Authorization.isAllowed
|
|
75
|
-
// matches on.
|
|
38
|
+
Ops.registerBinding(pushdowns, pgConnection, h, {
|
|
39
|
+
config: specModule.config,
|
|
40
|
+
subIdConfig: specModule.subIdConfig,
|
|
41
|
+
stateSchema: specModule.stateSchema,
|
|
76
42
|
authorization: specModule.authorization,
|
|
77
43
|
});
|
|
78
|
-
log.debug("registered resolver binding for " + h.readModelName, { comp: "PgQueryResolver" });
|
|
79
44
|
}));
|
|
80
45
|
}
|
|
81
46
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Typed cold-start core for the shared PgQueryResolver Lambda entry point
|
|
2
|
+
// (B3.2b).
|
|
3
|
+
//
|
|
4
|
+
// The "typed core, thin shell" split (docs/plans/minimize-lambda-entrypoint-mjs-shell.md):
|
|
5
|
+
// PgQueryResolverEntryPoint.mjs keeps only the untyped seam — the dynamic
|
|
6
|
+
// `import()` of each spec package named in QUERY_RESOLVER_CONFIG, the
|
|
7
|
+
// `patchSpecId` fix-up, and the reads of the runtime-loaded module's exports
|
|
8
|
+
// (config / subIdConfig / stateSchema / authorization), handed back here as a
|
|
9
|
+
// typed `specInfo`. QUERY_RESOLVER_CONFIG parsing, the pool/engine
|
|
10
|
+
// construction (QueryEnginePostgres.Make over an inline module capturing the
|
|
11
|
+
// container-lifetime pool), the push-down record, and the per-read-model
|
|
12
|
+
// binding registration live here, fully type-checked.
|
|
13
|
+
|
|
14
|
+
@module("./HandlerFactoryHelpers.mjs") @scope("log")
|
|
15
|
+
external logDebug: (string, StreamRoutedEntryPoint_Ops.dispatchOpts) => unit = "debug"
|
|
16
|
+
|
|
17
|
+
// ── QUERY_RESOLVER_CONFIG ───────────────────────────────────────────────────
|
|
18
|
+
// Written by PgQueryResolver_Builder: one shared pgConnection + one handler
|
|
19
|
+
// per read model + the Relay node type → read-model map (B3.2c).
|
|
20
|
+
|
|
21
|
+
type handlerEntry = {
|
|
22
|
+
readModelName: string,
|
|
23
|
+
specModule: string,
|
|
24
|
+
labelField: string,
|
|
25
|
+
includeIdParam: bool,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type resolverConfig = {
|
|
29
|
+
pgConnection: option<PgConnection.connectionConfig>,
|
|
30
|
+
handlers: array<handlerEntry>,
|
|
31
|
+
nodeTypes: dict<string>,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// The builder writes `pgConnection` via PgConnection.connectionConfigToJson, so
|
|
35
|
+
// the parsed object IS the record shape — cast at the documented JSON contract
|
|
36
|
+
// (the AggregateEntryPoint_Ops.asConnectionConfig pattern).
|
|
37
|
+
external asPgConnection: JSON.t => PgConnection.connectionConfig = "%identity"
|
|
38
|
+
|
|
39
|
+
let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
|
|
40
|
+
obj->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
41
|
+
|
|
42
|
+
let decodeEntry = (json: JSON.t): option<handlerEntry> =>
|
|
43
|
+
json
|
|
44
|
+
->JSON.Decode.object
|
|
45
|
+
->Option.map(h => {
|
|
46
|
+
readModelName: h->strOf("readModelName")->Option.getOr(""),
|
|
47
|
+
specModule: h->strOf("specModule")->Option.getOr(""),
|
|
48
|
+
labelField: h->strOf("labelField")->Option.getOr(""),
|
|
49
|
+
includeIdParam: h
|
|
50
|
+
->Dict.get("includeIdParam")
|
|
51
|
+
->Option.flatMap(JSON.Decode.bool)
|
|
52
|
+
->Option.getOr(false),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
let parseResolverConfig = (rawJson: string): resolverConfig => {
|
|
56
|
+
let obj =
|
|
57
|
+
(rawJson == "" ? `{"handlers":[]}` : rawJson)
|
|
58
|
+
->JSON.parseOrThrow
|
|
59
|
+
->JSON.Decode.object
|
|
60
|
+
->Option.getOr(Dict.make())
|
|
61
|
+
{
|
|
62
|
+
pgConnection: obj
|
|
63
|
+
->Dict.get("pgConnection")
|
|
64
|
+
->Option.filter(j => j != JSON.Null)
|
|
65
|
+
->Option.map(asPgConnection),
|
|
66
|
+
handlers: obj
|
|
67
|
+
->Dict.get("handlers")
|
|
68
|
+
->Option.flatMap(JSON.Decode.array)
|
|
69
|
+
->Option.getOr([])
|
|
70
|
+
->Array.filterMap(decodeEntry),
|
|
71
|
+
nodeTypes: obj
|
|
72
|
+
->Dict.get("nodeTypes")
|
|
73
|
+
->Option.flatMap(JSON.Decode.object)
|
|
74
|
+
->Option.getOr(Dict.make())
|
|
75
|
+
->Dict.toArray
|
|
76
|
+
->Array.filterMap(((k, v)) => v->JSON.Decode.string->Option.map(s => (k, s)))
|
|
77
|
+
->Dict.fromArray,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let registerNodeTypes = (config: resolverConfig): unit =>
|
|
82
|
+
config.nodeTypes
|
|
83
|
+
->Dict.toArray
|
|
84
|
+
->Array.forEach(((typeName, readModelName)) =>
|
|
85
|
+
PgQueryResolver_Lambda.registerNodeType(~typeName, ~readModelName)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
// ── Push-downs ──────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
// A bounded full-materialisation for the list fallback (shapes listPage
|
|
91
|
+
// declines). Kept high; the fallback is only hit for search/searchPrefix/ids/
|
|
92
|
+
// backward pagination, which the AutoUI does not issue for large models.
|
|
93
|
+
let scanAllLimit = 100000
|
|
94
|
+
|
|
95
|
+
// Container-lifetime pool (memoised by poolFor); the engine and each binding's
|
|
96
|
+
// ops set share it, so the Lambda holds one pool regardless of read model
|
|
97
|
+
// count.
|
|
98
|
+
let makePushdowns = (
|
|
99
|
+
pgConnection: PgConnection.connectionConfig,
|
|
100
|
+
): PgQueryResolver_Lambda.pushdowns => {
|
|
101
|
+
module Engine = ReventlessPostgres.QueryEnginePostgres.Make({
|
|
102
|
+
let pool = PgRuntime.poolFor(pgConnection)
|
|
103
|
+
})
|
|
104
|
+
{
|
|
105
|
+
indexLookup: Engine.indexLookup,
|
|
106
|
+
byIds: Engine.byIds,
|
|
107
|
+
listPage: Engine.listPage,
|
|
108
|
+
itemsPage: Engine.itemsPage,
|
|
109
|
+
scanAll: (~readModelName) => Engine.scan(~readModelName, ~filterConfigs=[], ~limit=scanAllLimit),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Binding registration ────────────────────────────────────────────────────
|
|
114
|
+
// The runtime-loaded spec module's exports, read by the shell and passed as a
|
|
115
|
+
// typed record; `authorization` is the compiled string variant (e.g.
|
|
116
|
+
// "AllowAuthenticated") injected on the spec by @@reventless.spec — the shape
|
|
117
|
+
// Reventless.Authorization.isAllowed matches on.
|
|
118
|
+
|
|
119
|
+
type specInfo = {
|
|
120
|
+
config: option<ProjectionEntryPoint_Ops.specConfig>,
|
|
121
|
+
subIdConfig: option<Reventless.ReadModel.subIdConfig<JSON.t>>,
|
|
122
|
+
stateSchema: S.t<unknown>,
|
|
123
|
+
authorization: Reventless.Authorization.permission,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let registerBinding = (
|
|
127
|
+
pushdowns: PgQueryResolver_Lambda.pushdowns,
|
|
128
|
+
pgConnection: PgConnection.connectionConfig,
|
|
129
|
+
entry: handlerEntry,
|
|
130
|
+
spec: specInfo,
|
|
131
|
+
): unit => {
|
|
132
|
+
let indexes = ProjectionEntryPoint_Ops.indexesOf(spec.config)
|
|
133
|
+
let subIdField = ProjectionEntryPoint_Ops.subIdFieldOf(spec.subIdConfig)
|
|
134
|
+
PgQueryResolver_Lambda.register(
|
|
135
|
+
~readModelName=entry.readModelName,
|
|
136
|
+
{
|
|
137
|
+
ops: QueryDbStorage_Postgres_Runtime.opsFor(
|
|
138
|
+
pgConnection,
|
|
139
|
+
~name=entry.readModelName,
|
|
140
|
+
~indexes,
|
|
141
|
+
~subIdField=?subIdField,
|
|
142
|
+
),
|
|
143
|
+
pushdowns,
|
|
144
|
+
indexes,
|
|
145
|
+
subIdField,
|
|
146
|
+
capability: ReventlessCore.GraphQL_FragmentGenerator.deriveServerCapability(
|
|
147
|
+
spec.stateSchema,
|
|
148
|
+
),
|
|
149
|
+
labelField: entry.labelField,
|
|
150
|
+
includeIdParam: entry.includeIdParam,
|
|
151
|
+
authorization: spec.authorization,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
logDebug("registered resolver binding for " ++ entry.readModelName, {comp: "PgQueryResolver"})
|
|
155
|
+
}
|