@reventlessdev/reventless-aws 3.0.0-alpha.175 → 3.0.0-alpha.177
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 +22 -0
- package/package.json +10 -10
- package/src/Platform.res +127 -31
- package/src/Platform.res.mjs +226 -44
- package/src/adapter/DcbEventLog/DcbBackend.res +2 -2
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +4 -1
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +1 -1
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res +14 -4
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs +19 -14
- package/src/adapter/EventLog/EventLogBackend.res +82 -0
- package/src/adapter/EventLog/EventLogBackend.res.mjs +53 -0
- package/src/adapter/EventLog/EventLogStorage.res +15 -0
- package/src/adapter/EventLog/EventLogStorage.res.mjs +20 -1
- package/src/adapter/EventLog/EventLogStorage_Postgres.res +37 -0
- package/src/adapter/EventLog/EventLogStorage_Postgres.res.mjs +21 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +26 -11
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +15 -6
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +54 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +23 -0
- package/src/adapter/Postgres/PgProjectionFeed.res +100 -0
- package/src/adapter/Postgres/PgProjectionFeed.res.mjs +54 -0
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res +281 -0
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +189 -0
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +335 -0
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +252 -0
- package/src/adapter/QueryDb/QueryDbBackend.res +52 -0
- package/src/adapter/QueryDb/QueryDbBackend.res.mjs +44 -0
- package/src/adapter/QueryDb/QueryDbResolvers.res +49 -0
- package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +20 -1
- package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +264 -0
- package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res.mjs +164 -0
- package/src/adapter/QueryDb/QueryDbStorage.res +44 -0
- package/src/adapter/QueryDb/QueryDbStorage.res.mjs +34 -1
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +47 -0
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +21 -0
- package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +19 -0
- package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +15 -0
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +12 -1
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +2 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +83 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +46 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +83 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +48 -3
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +130 -5
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +66 -6
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +15 -10
- package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +88 -0
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +32 -15
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -12
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +138 -8
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +76 -11
- package/src/components/Aggregate_Builder_Single.res +12 -5
- package/src/components/Aggregate_Builder_Single.res.mjs +12 -6
- package/src/components/Aggregate_Builder_Single_Async.res +12 -5
- package/src/components/Aggregate_Builder_Single_Async.res.mjs +12 -6
- package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -1
- package/src/components/ReadModel_Builder_NoResolver_Stream.res +13 -4
- package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +14 -9
- package/src/components/ReadModel_Builder_Single.res +14 -5
- package/src/components/ReadModel_Builder_Single.res.mjs +15 -12
- package/src/components/ReadModel_Builder_Single_Stream.res +14 -5
- package/src/components/ReadModel_Builder_Single_Stream.res.mjs +15 -12
- package/src/components/StateViewSlice_Builder.res +10 -4
- package/src/components/StateViewSlice_Builder.res.mjs +8 -10
- package/src/components/StateViewSlice_Builder_Stream.res +10 -4
- package/src/components/StateViewSlice_Builder_Stream.res.mjs +8 -10
- package/src/plugin/runtime/PluginRuntime_Builder.res +12 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +4 -0
- package/tests/EventCollectorChannelStreamRuntimeTest.res +55 -0
- package/tests/EventCollectorChannelStreamRuntimeTest.res.mjs +78 -0
- package/tests/PgChangeFeedRelay_IntegrationTest.res +115 -4
- package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +84 -0
- package/tests/PgPipeline_IntegrationTest.res +208 -0
- package/tests/PgPipeline_IntegrationTest.res.mjs +260 -0
- package/tests/PgQueryResolver_LambdaTest.res +308 -0
- package/tests/PgQueryResolver_LambdaTest.res.mjs +392 -0
|
@@ -1,2 +1,51 @@
|
|
|
1
1
|
module AppSync = QueryDbResolvers_AppSync
|
|
2
2
|
module NoOp = QueryDbResolvers_NoOp
|
|
3
|
+
module Lambda = QueryDbResolvers_Lambda
|
|
4
|
+
|
|
5
|
+
// B3.2b: Postgres-backed read models have no DynamoDB data source — their
|
|
6
|
+
// GraphQL Query fields are served by the shared PgQueryResolver Lambda data
|
|
7
|
+
// source (`QueryDbResolvers_Lambda`, Invoke templates → PgQueryResolver_Lambda).
|
|
8
|
+
// DynamoDB-backed (incl. admin-exempt) read models keep the direct resolver set.
|
|
9
|
+
module Selectable = {
|
|
10
|
+
type api = QueryDbResolvers_AppSync_NoOp.api
|
|
11
|
+
type role = QueryDbResolvers_AppSync_NoOp.role
|
|
12
|
+
let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
13
|
+
~name,
|
|
14
|
+
~api,
|
|
15
|
+
~apiRole,
|
|
16
|
+
~dataSourceName,
|
|
17
|
+
~indexes,
|
|
18
|
+
~subIdField,
|
|
19
|
+
~idResolverConfigs,
|
|
20
|
+
~idsResolverConfigs,
|
|
21
|
+
~authorization,
|
|
22
|
+
~opts,
|
|
23
|
+
) =>
|
|
24
|
+
if QueryDbBackend.isPostgresFor(name) {
|
|
25
|
+
QueryDbResolvers_Lambda.make(
|
|
26
|
+
~name,
|
|
27
|
+
~api,
|
|
28
|
+
~apiRole,
|
|
29
|
+
~dataSourceName,
|
|
30
|
+
~indexes,
|
|
31
|
+
~subIdField,
|
|
32
|
+
~idResolverConfigs,
|
|
33
|
+
~idsResolverConfigs,
|
|
34
|
+
~authorization,
|
|
35
|
+
~opts,
|
|
36
|
+
)
|
|
37
|
+
} else {
|
|
38
|
+
QueryDbResolvers_AppSync.make(
|
|
39
|
+
~name,
|
|
40
|
+
~api,
|
|
41
|
+
~apiRole,
|
|
42
|
+
~dataSourceName,
|
|
43
|
+
~indexes,
|
|
44
|
+
~subIdField,
|
|
45
|
+
~idResolverConfigs,
|
|
46
|
+
~idsResolverConfigs,
|
|
47
|
+
~authorization,
|
|
48
|
+
~opts,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as QueryDbBackend$ReventlessAws from "./QueryDbBackend.res.mjs";
|
|
4
|
+
import * as QueryDbResolvers_Lambda$ReventlessAws from "./QueryDbResolvers_Lambda.res.mjs";
|
|
5
|
+
import * as QueryDbResolvers_AppSync$ReventlessAws from "./QueryDbResolvers_AppSync.res.mjs";
|
|
6
|
+
|
|
7
|
+
function make(name, api, apiRole, dataSourceName, indexes, subIdField, idResolverConfigs, idsResolverConfigs, authorization, opts) {
|
|
8
|
+
if (QueryDbBackend$ReventlessAws.isPostgresFor(name)) {
|
|
9
|
+
return QueryDbResolvers_Lambda$ReventlessAws.make(name, api, apiRole, dataSourceName, indexes, subIdField, idResolverConfigs, idsResolverConfigs, authorization, opts);
|
|
10
|
+
} else {
|
|
11
|
+
return QueryDbResolvers_AppSync$ReventlessAws.make(name, api, apiRole, dataSourceName, indexes, subIdField, idResolverConfigs, idsResolverConfigs, authorization, opts);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let Selectable = {
|
|
16
|
+
make: make
|
|
17
|
+
};
|
|
3
18
|
|
|
4
19
|
let AppSync;
|
|
5
20
|
|
|
6
21
|
let NoOp;
|
|
7
22
|
|
|
23
|
+
let Lambda;
|
|
24
|
+
|
|
8
25
|
export {
|
|
9
26
|
AppSync,
|
|
10
27
|
NoOp,
|
|
28
|
+
Lambda,
|
|
29
|
+
Selectable,
|
|
11
30
|
}
|
|
12
|
-
/*
|
|
31
|
+
/* QueryDbResolvers_Lambda-ReventlessAws Not a pure module */
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// AppSync resolvers for Postgres-backed read models (B3.2b) — the Lambda-data-
|
|
2
|
+
// source parallel to QueryDbResolvers_AppSync's direct DynamoDB resolvers.
|
|
3
|
+
//
|
|
4
|
+
// Every Query field becomes a thin APPSYNC_JS unit resolver whose `Invoke`
|
|
5
|
+
// template carries { readModelName, kind, index?, arguments, identity } to the
|
|
6
|
+
// ONE shared PgQueryResolver Lambda (PgQueryResolver_Builder), which dispatches
|
|
7
|
+
// via PgQueryResolver_Lambda.dispatch. `dataSourceName` is the shared data
|
|
8
|
+
// source's (deferred) name, the same for every Postgres read model.
|
|
9
|
+
//
|
|
10
|
+
// Field names / includeIdParam / connectionSpec come from the same registry the
|
|
11
|
+
// AppSync path reads, so the SDL emitted by GraphQL_FragmentGenerator stays in
|
|
12
|
+
// lockstep. Kinds covered: getById, list (connection), index, byIds. Deferred to
|
|
13
|
+
// B3.2c: items (sub-id connection), @resolves/@resolvesMany, node, auth-table.
|
|
14
|
+
|
|
15
|
+
module Resolver = AppSync_Resolver_Retrying
|
|
16
|
+
open Reventless.ReadModel
|
|
17
|
+
|
|
18
|
+
type api = Types.AppSync.api
|
|
19
|
+
type role = Types.AppSync.role
|
|
20
|
+
|
|
21
|
+
// Identity block shared by every Invoke template (Cognito vs IAM), matching the
|
|
22
|
+
// shape PgQueryResolver_Lambda decodes (Reventless.Identity.t).
|
|
23
|
+
let identityBlock = `id != null && id.sub != null
|
|
24
|
+
? { userId: id.sub, username: id.username, groups: id.claims?.['cognito:groups'] ?? [], claims: id.claims, provider: 'Cognito' }
|
|
25
|
+
: id != null
|
|
26
|
+
? { userArn: id.userArn ?? null, accountId: id.accountId ?? null, username: id.username ?? null, provider: 'IAM' }
|
|
27
|
+
: null`
|
|
28
|
+
|
|
29
|
+
let invokeTemplate = (~readModelName: string, ~kind: string, ~index: option<string>=?) => {
|
|
30
|
+
let indexFrag = switch index {
|
|
31
|
+
| Some(ix) => `\n index: '${ix}',`
|
|
32
|
+
| None => ""
|
|
33
|
+
}
|
|
34
|
+
`import { util } from '@aws-appsync/utils';
|
|
35
|
+
export function request(ctx) {
|
|
36
|
+
const id = ctx.identity;
|
|
37
|
+
return {
|
|
38
|
+
operation: 'Invoke',
|
|
39
|
+
payload: {
|
|
40
|
+
readModelName: '${readModelName}',
|
|
41
|
+
kind: '${kind}',${indexFrag}
|
|
42
|
+
arguments: ctx.args,
|
|
43
|
+
identity: ${identityBlock}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function response(ctx) {
|
|
48
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
49
|
+
return ctx.result;
|
|
50
|
+
}
|
|
51
|
+
`->Pulumi.Input.make
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Cross-table field resolver (@resolves/@resolvesMany, B3.2c): on a parent
|
|
55
|
+
// entity type's field, carrying the parent object (ctx.source) + baked target
|
|
56
|
+
// metadata (`extra`). `sourceType` is the parent read model (used only for auth
|
|
57
|
+
// scoping in dispatch); `target` is the target read model's binding key.
|
|
58
|
+
let invokeFieldTemplate = (~sourceType: string, ~kind: string, ~target: string, ~extra: string) =>
|
|
59
|
+
`import { util } from '@aws-appsync/utils';
|
|
60
|
+
export function request(ctx) {
|
|
61
|
+
const id = ctx.identity;
|
|
62
|
+
return {
|
|
63
|
+
operation: 'Invoke',
|
|
64
|
+
payload: {
|
|
65
|
+
readModelName: '${sourceType}',
|
|
66
|
+
kind: '${kind}',
|
|
67
|
+
target: '${target}',
|
|
68
|
+
source: ctx.source,
|
|
69
|
+
arguments: ctx.args,${extra}
|
|
70
|
+
identity: ${identityBlock}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function response(ctx) {
|
|
75
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
76
|
+
return ctx.result;
|
|
77
|
+
}
|
|
78
|
+
`->Pulumi.Input.make
|
|
79
|
+
|
|
80
|
+
let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
81
|
+
~name: string,
|
|
82
|
+
~api: api,
|
|
83
|
+
~apiRole as _: role,
|
|
84
|
+
~dataSourceName,
|
|
85
|
+
~indexes: array<indexConfig>,
|
|
86
|
+
~subIdField,
|
|
87
|
+
~idResolverConfigs: array<idResolverConfig>,
|
|
88
|
+
~idsResolverConfigs: array<idsResolverConfig>,
|
|
89
|
+
~authorization as _: Reventless.Authorization.permission,
|
|
90
|
+
~opts,
|
|
91
|
+
) => {
|
|
92
|
+
let dataSourceName = dataSourceName->Pulumi.Output.asInput
|
|
93
|
+
let name = name->String.capitalize
|
|
94
|
+
let registryEntry = ReventlessCore.Plugin_Helpers.queryFieldNamesRegistry->Dict.get(name)
|
|
95
|
+
|
|
96
|
+
let fieldNameForSingle = switch registryEntry {
|
|
97
|
+
| Some({singleFieldName}) => singleFieldName
|
|
98
|
+
| None => name->Resolver.Functions.uncapitalize
|
|
99
|
+
}
|
|
100
|
+
let includeIdParam = switch registryEntry {
|
|
101
|
+
| Some({includeIdParam}) => includeIdParam
|
|
102
|
+
| None => true
|
|
103
|
+
}
|
|
104
|
+
let connectionSpec = switch registryEntry {
|
|
105
|
+
| Some({connectionSpec}) => connectionSpec
|
|
106
|
+
| None => true
|
|
107
|
+
}
|
|
108
|
+
let fieldNameForAll = switch registryEntry {
|
|
109
|
+
| Some({listFieldName}) => listFieldName
|
|
110
|
+
| None => name ++ "s"
|
|
111
|
+
}
|
|
112
|
+
let labelField = switch registryEntry {
|
|
113
|
+
| Some({labelField: ?lf}) => lf->Option.getOr("id")
|
|
114
|
+
| None => "id"
|
|
115
|
+
}
|
|
116
|
+
let returnTypeName = switch registryEntry {
|
|
117
|
+
| Some({returnTypeName: rt}) => rt
|
|
118
|
+
| None => name
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Register the binding info the shared Lambda's env config needs (the entry
|
|
122
|
+
// point gets indexes/subIdField/schema from the spec module; labelField and
|
|
123
|
+
// includeIdParam come from this deploy-time registry). Keyed by spec name.
|
|
124
|
+
PgQueryResolver_Builder.register({readModelName: name, labelField, includeIdParam})
|
|
125
|
+
|
|
126
|
+
// Relay node type → this read model (for the shared node(id) resolver, B3.2c).
|
|
127
|
+
// Only entities addressable by id participate in node resolution.
|
|
128
|
+
if includeIdParam {
|
|
129
|
+
PgQueryResolver_Builder.registerNodeType(~typeName=returnTypeName, ~readModelName=name)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let mkResolver = (~resolverName, ~field, ~kind, ~index=?) =>
|
|
133
|
+
Resolver.makeUnitJsResolver(
|
|
134
|
+
~name=resolverName,
|
|
135
|
+
~api,
|
|
136
|
+
~dataSourceName,
|
|
137
|
+
~type_="Query"->Pulumi.Input.make,
|
|
138
|
+
~field=field->Pulumi.Input.make,
|
|
139
|
+
~code=invokeTemplate(~readModelName=name, ~kind, ~index?),
|
|
140
|
+
~opts,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
let stripLeadingBy = s =>
|
|
144
|
+
if s->String.startsWith("by") && s->String.length > 2 {
|
|
145
|
+
s->String.slice(~start=2, ~end=s->String.length)
|
|
146
|
+
} else {
|
|
147
|
+
s
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Resolvers are deferred into resourcesMaker (created inside the schema-pushed
|
|
151
|
+
// builderOutputs.apply, like the AppSync path) — so they only exist after the
|
|
152
|
+
// fields are ACTIVE and after PgQueryResolver_Builder.provision has resolved
|
|
153
|
+
// the shared data source name.
|
|
154
|
+
let resourcesMaker: ReventlessCore.QueryDb.resolversResourcesMaker = _allQueryDbs => {
|
|
155
|
+
// getById (or listAll when the read model has no id param, e.g. singletons).
|
|
156
|
+
let byId = mkResolver(
|
|
157
|
+
~resolverName=fieldNameForSingle->String.capitalize,
|
|
158
|
+
~field=fieldNameForSingle,
|
|
159
|
+
~kind=includeIdParam ? "getById" : "list",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// Main list — Relay connection (or legacy list when connectionSpec=false).
|
|
163
|
+
let all = mkResolver(
|
|
164
|
+
~resolverName=fieldNameForAll->String.capitalize,
|
|
165
|
+
~field=fieldNameForAll,
|
|
166
|
+
~kind=connectionSpec ? "list" : "list",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
// Sub-id connection — {single}Items(id, filter, …); only when subId configured.
|
|
170
|
+
let items = switch subIdField {
|
|
171
|
+
| Some(_) => [
|
|
172
|
+
mkResolver(
|
|
173
|
+
~resolverName=fieldNameForSingle->String.capitalize ++ "Items",
|
|
174
|
+
~field=fieldNameForSingle ++ "Items",
|
|
175
|
+
~kind="items",
|
|
176
|
+
),
|
|
177
|
+
]
|
|
178
|
+
| None => []
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Batched-by-ids — single-key projections only (mirrors the SDL).
|
|
182
|
+
let byIds = if includeIdParam && subIdField === None {
|
|
183
|
+
let byIdsField = fieldNameForAll ++ "ByIds"
|
|
184
|
+
[
|
|
185
|
+
mkResolver(
|
|
186
|
+
~resolverName=byIdsField->String.capitalize,
|
|
187
|
+
~field=byIdsField,
|
|
188
|
+
~kind="byIds",
|
|
189
|
+
),
|
|
190
|
+
]
|
|
191
|
+
} else {
|
|
192
|
+
[]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Per-index equality queries: {single}By{Index}.
|
|
196
|
+
let byIndex = indexes->Array.map(({index}) => {
|
|
197
|
+
let resolverName =
|
|
198
|
+
fieldNameForSingle->String.capitalize ++ "By" ++ index->stripLeadingBy->String.capitalize
|
|
199
|
+
let field = fieldNameForSingle ++ "By" ++ index->stripLeadingBy->String.capitalize
|
|
200
|
+
mkResolver(~resolverName, ~field, ~kind="index", ~index)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// @resolves — single cross-table field resolver on this (parent) type. The
|
|
204
|
+
// target's binding key is its capitalized spec name (mirrors how the binding
|
|
205
|
+
// registry is keyed); the parent object flows via ctx.source.
|
|
206
|
+
let idResolvers = idResolverConfigs->Array.map(config => {
|
|
207
|
+
let {source: {idField, subId, resolvedField}, target} = config
|
|
208
|
+
let targetKey = target.tableName->String.capitalize
|
|
209
|
+
let (field, multi) = switch resolvedField {
|
|
210
|
+
| Single(f) => (f, false)
|
|
211
|
+
| Multi(f) => (f, true)
|
|
212
|
+
}
|
|
213
|
+
let targetFrag = switch target.idField {
|
|
214
|
+
| Index(ix) => `\n targetIndex: '${ix}',\n targetIndexIdField: '${ix}',`
|
|
215
|
+
| IndexWithId(ix, idf) => `\n targetIndex: '${ix}',\n targetIndexIdField: '${idf}',`
|
|
216
|
+
| Id => ""
|
|
217
|
+
}
|
|
218
|
+
let subIdFrag = switch subId {
|
|
219
|
+
| Field(f) => `\n sourceSubId: { kind: 'field', name: '${f}' },`
|
|
220
|
+
| Argument(a) => `\n sourceSubId: { kind: 'arg', name: '${a}' },`
|
|
221
|
+
| NoSubId => ""
|
|
222
|
+
}
|
|
223
|
+
let extra =
|
|
224
|
+
`\n sourceIdField: '${idField}',\n multi: ${multi ? "true" : "false"},` ++
|
|
225
|
+
targetFrag ++
|
|
226
|
+
subIdFrag
|
|
227
|
+
Resolver.makeUnitJsResolver(
|
|
228
|
+
~name=name ++ field->String.capitalize,
|
|
229
|
+
~api,
|
|
230
|
+
~dataSourceName,
|
|
231
|
+
~type_=name->Pulumi.Input.make,
|
|
232
|
+
~field=field->Pulumi.Input.make,
|
|
233
|
+
~code=invokeFieldTemplate(~sourceType=name, ~kind="resolveOne", ~target=targetKey, ~extra),
|
|
234
|
+
~opts,
|
|
235
|
+
)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
// @resolvesMany — batch cross-table field resolver (BatchGet by ids).
|
|
239
|
+
let idsResolvers = idsResolverConfigs->Array.map(config => {
|
|
240
|
+
let {source: {idsField, resolvedField}, target} = config
|
|
241
|
+
let targetKey = target.tableName->String.capitalize
|
|
242
|
+
let extra = `\n sourceIdsField: '${idsField}',`
|
|
243
|
+
Resolver.makeUnitJsResolver(
|
|
244
|
+
~name=name ++ resolvedField->String.capitalize,
|
|
245
|
+
~api,
|
|
246
|
+
~dataSourceName,
|
|
247
|
+
~type_=name->Pulumi.Input.make,
|
|
248
|
+
~field=resolvedField->Pulumi.Input.make,
|
|
249
|
+
~code=invokeFieldTemplate(~sourceType=name, ~kind="resolveMany", ~target=targetKey, ~extra),
|
|
250
|
+
~opts,
|
|
251
|
+
)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
[byId, all]
|
|
255
|
+
->Array.concat(items)
|
|
256
|
+
->Array.concat(byIds)
|
|
257
|
+
->Array.concat(byIndex)
|
|
258
|
+
->Array.concat(idResolvers)
|
|
259
|
+
->Array.concat(idsResolvers)
|
|
260
|
+
->Array.map(Util.AppSync.toResourceNative)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
{ReventlessCore.QueryDb_Adapter.resources: [], resourcesMaker}
|
|
264
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
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_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
|
|
5
|
+
import * as Util_AppSync$ReventlessAws from "../../util/Util_AppSync.res.mjs";
|
|
6
|
+
import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
|
|
7
|
+
import * as AppSync_Resolver_Functions$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/AppSync/AppSync_Resolver_Functions.res.mjs";
|
|
8
|
+
import * as PgQueryResolver_Builder$ReventlessAws from "./PgQueryResolver_Builder.res.mjs";
|
|
9
|
+
import * as AppSync_Resolver_Retrying$ReventlessAws from "../Api/AppSync_Resolver_Retrying.res.mjs";
|
|
10
|
+
|
|
11
|
+
let identityBlock = `id != null && id.sub != null
|
|
12
|
+
? { userId: id.sub, username: id.username, groups: id.claims?.['cognito:groups'] ?? [], claims: id.claims, provider: 'Cognito' }
|
|
13
|
+
: id != null
|
|
14
|
+
? { userArn: id.userArn ?? null, accountId: id.accountId ?? null, username: id.username ?? null, provider: 'IAM' }
|
|
15
|
+
: null`;
|
|
16
|
+
|
|
17
|
+
function invokeTemplate(readModelName, kind, index) {
|
|
18
|
+
let indexFrag = index !== undefined ? `\n index: '` + index + `',` : "";
|
|
19
|
+
return `import { util } from '@aws-appsync/utils';
|
|
20
|
+
export function request(ctx) {
|
|
21
|
+
const id = ctx.identity;
|
|
22
|
+
return {
|
|
23
|
+
operation: 'Invoke',
|
|
24
|
+
payload: {
|
|
25
|
+
readModelName: '` + readModelName + `',
|
|
26
|
+
kind: '` + kind + `',` + indexFrag + `
|
|
27
|
+
arguments: ctx.args,
|
|
28
|
+
identity: ` + identityBlock + `
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function response(ctx) {
|
|
33
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
34
|
+
return ctx.result;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function invokeFieldTemplate(sourceType, kind, target, extra) {
|
|
40
|
+
return `import { util } from '@aws-appsync/utils';
|
|
41
|
+
export function request(ctx) {
|
|
42
|
+
const id = ctx.identity;
|
|
43
|
+
return {
|
|
44
|
+
operation: 'Invoke',
|
|
45
|
+
payload: {
|
|
46
|
+
readModelName: '` + sourceType + `',
|
|
47
|
+
kind: '` + kind + `',
|
|
48
|
+
target: '` + target + `',
|
|
49
|
+
source: ctx.source,
|
|
50
|
+
arguments: ctx.args,` + extra + `
|
|
51
|
+
identity: ` + identityBlock + `
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function response(ctx) {
|
|
56
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
57
|
+
return ctx.result;
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function make(name, api, param, dataSourceName, indexes, subIdField, idResolverConfigs, idsResolverConfigs, param$1, opts) {
|
|
63
|
+
let name$1 = Stdlib_String.capitalize(name);
|
|
64
|
+
let registryEntry = Plugin_Helpers$ReventlessCore.queryFieldNamesRegistry[name$1];
|
|
65
|
+
let fieldNameForSingle = registryEntry !== undefined ? registryEntry.singleFieldName : AppSync_Resolver_Functions$PulumiAws.uncapitalize(name$1);
|
|
66
|
+
let includeIdParam = registryEntry !== undefined ? registryEntry.includeIdParam : true;
|
|
67
|
+
let fieldNameForAll = registryEntry !== undefined ? registryEntry.listFieldName : name$1 + "s";
|
|
68
|
+
let labelField = registryEntry !== undefined ? Stdlib_Option.getOr(registryEntry.labelField, "id") : "id";
|
|
69
|
+
let returnTypeName = registryEntry !== undefined ? registryEntry.returnTypeName : name$1;
|
|
70
|
+
PgQueryResolver_Builder$ReventlessAws.register({
|
|
71
|
+
readModelName: name$1,
|
|
72
|
+
labelField: labelField,
|
|
73
|
+
includeIdParam: includeIdParam
|
|
74
|
+
});
|
|
75
|
+
if (includeIdParam) {
|
|
76
|
+
PgQueryResolver_Builder$ReventlessAws.registerNodeType(returnTypeName, name$1);
|
|
77
|
+
}
|
|
78
|
+
let mkResolver = (resolverName, field, kind, index) => AppSync_Resolver_Retrying$ReventlessAws.makeUnitJsResolver(resolverName, api, dataSourceName, "Query", field, invokeTemplate(name$1, kind, index), opts);
|
|
79
|
+
let stripLeadingBy = s => {
|
|
80
|
+
if (s.startsWith("by") && s.length > 2) {
|
|
81
|
+
return s.slice(2, s.length);
|
|
82
|
+
} else {
|
|
83
|
+
return s;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
let resourcesMaker = _allQueryDbs => {
|
|
87
|
+
let byId = mkResolver(Stdlib_String.capitalize(fieldNameForSingle), fieldNameForSingle, includeIdParam ? "getById" : "list", undefined);
|
|
88
|
+
let all = mkResolver(Stdlib_String.capitalize(fieldNameForAll), fieldNameForAll, "list", undefined);
|
|
89
|
+
let items = subIdField !== undefined ? [mkResolver(Stdlib_String.capitalize(fieldNameForSingle) + "Items", fieldNameForSingle + "Items", "items", undefined)] : [];
|
|
90
|
+
let byIds;
|
|
91
|
+
if (includeIdParam && subIdField === undefined) {
|
|
92
|
+
let byIdsField = fieldNameForAll + "ByIds";
|
|
93
|
+
byIds = [mkResolver(Stdlib_String.capitalize(byIdsField), byIdsField, "byIds", undefined)];
|
|
94
|
+
} else {
|
|
95
|
+
byIds = [];
|
|
96
|
+
}
|
|
97
|
+
let byIndex = indexes.map(param => {
|
|
98
|
+
let index = param.index;
|
|
99
|
+
let resolverName = Stdlib_String.capitalize(fieldNameForSingle) + "By" + Stdlib_String.capitalize(stripLeadingBy(index));
|
|
100
|
+
let field = fieldNameForSingle + "By" + Stdlib_String.capitalize(stripLeadingBy(index));
|
|
101
|
+
return mkResolver(resolverName, field, "index", index);
|
|
102
|
+
});
|
|
103
|
+
let idResolvers = idResolverConfigs.map(config => {
|
|
104
|
+
let target = config.target;
|
|
105
|
+
let match = config.source;
|
|
106
|
+
let resolvedField = match.resolvedField;
|
|
107
|
+
let subId = match.subId;
|
|
108
|
+
let targetKey = Stdlib_String.capitalize(target.tableName);
|
|
109
|
+
let match$1;
|
|
110
|
+
match$1 = resolvedField.TAG === "Single" ? [
|
|
111
|
+
resolvedField._0,
|
|
112
|
+
false
|
|
113
|
+
] : [
|
|
114
|
+
resolvedField._0,
|
|
115
|
+
true
|
|
116
|
+
];
|
|
117
|
+
let field = match$1[0];
|
|
118
|
+
let ix = target.idField;
|
|
119
|
+
let targetFrag;
|
|
120
|
+
if (typeof ix !== "object") {
|
|
121
|
+
targetFrag = "";
|
|
122
|
+
} else if (ix.TAG === "Index") {
|
|
123
|
+
let ix$1 = ix._0;
|
|
124
|
+
targetFrag = `\n targetIndex: '` + ix$1 + `',\n targetIndexIdField: '` + ix$1 + `',`;
|
|
125
|
+
} else {
|
|
126
|
+
targetFrag = `\n targetIndex: '` + ix._0 + `',\n targetIndexIdField: '` + ix._1 + `',`;
|
|
127
|
+
}
|
|
128
|
+
let subIdFrag;
|
|
129
|
+
subIdFrag = typeof subId !== "object" ? "" : (
|
|
130
|
+
subId.TAG === "Field" ? `\n sourceSubId: { kind: 'field', name: '` + subId._0 + `' },` : `\n sourceSubId: { kind: 'arg', name: '` + subId._0 + `' },`
|
|
131
|
+
);
|
|
132
|
+
let extra = `\n sourceIdField: '` + match.idField + `',\n multi: ` + (
|
|
133
|
+
match$1[1] ? "true" : "false"
|
|
134
|
+
) + `,` + targetFrag + subIdFrag;
|
|
135
|
+
return AppSync_Resolver_Retrying$ReventlessAws.makeUnitJsResolver(name$1 + Stdlib_String.capitalize(field), api, dataSourceName, name$1, field, invokeFieldTemplate(name$1, "resolveOne", targetKey, extra), opts);
|
|
136
|
+
});
|
|
137
|
+
let idsResolvers = idsResolverConfigs.map(config => {
|
|
138
|
+
let match = config.source;
|
|
139
|
+
let resolvedField = match.resolvedField;
|
|
140
|
+
let targetKey = Stdlib_String.capitalize(config.target.tableName);
|
|
141
|
+
let extra = `\n sourceIdsField: '` + match.idsField + `',`;
|
|
142
|
+
return AppSync_Resolver_Retrying$ReventlessAws.makeUnitJsResolver(name$1 + Stdlib_String.capitalize(resolvedField), api, dataSourceName, name$1, resolvedField, invokeFieldTemplate(name$1, "resolveMany", targetKey, extra), opts);
|
|
143
|
+
});
|
|
144
|
+
return [
|
|
145
|
+
byId,
|
|
146
|
+
all
|
|
147
|
+
].concat(items).concat(byIds).concat(byIndex).concat(idResolvers).concat(idsResolvers).map(Util_AppSync$ReventlessAws.toResourceNative);
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
resources: [],
|
|
151
|
+
resourcesMaker: resourcesMaker
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let Resolver;
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
Resolver,
|
|
159
|
+
identityBlock,
|
|
160
|
+
invokeTemplate,
|
|
161
|
+
invokeFieldTemplate,
|
|
162
|
+
make,
|
|
163
|
+
}
|
|
164
|
+
/* Util_AppSync-ReventlessAws Not a pure module */
|
|
@@ -3,3 +3,47 @@ let name = "QueryDbStorage"
|
|
|
3
3
|
module DynamoDb = QueryDbStorage_DynamoDb
|
|
4
4
|
module DynamoDbStream = QueryDbStorage_DynamoDbStream
|
|
5
5
|
module DynamoDb_Runtime = QueryDbStorage_DynamoDb_Runtime
|
|
6
|
+
module Postgres = QueryDbStorage_Postgres
|
|
7
|
+
|
|
8
|
+
// Build-time QueryDb storage selectors (B3.1). Read `QueryDbBackend` — set by the
|
|
9
|
+
// platform when a `PgConnection` is supplied — and dispatch per read model:
|
|
10
|
+
// Postgres (no table, no data source) unless the name is admin-exempt. The Stream
|
|
11
|
+
// selector falls back to the streamed DynamoDB maker; on Postgres there is no
|
|
12
|
+
// stream — live updates for Postgres-backed read models are deferred (B3.3).
|
|
13
|
+
module Selectable = {
|
|
14
|
+
type api = QueryDbStorage_Postgres.api
|
|
15
|
+
type role = QueryDbStorage_Postgres.role
|
|
16
|
+
let make: ReventlessCore.QueryDb_Adapter.storageMaker<api, role> = (
|
|
17
|
+
~name,
|
|
18
|
+
~indexes,
|
|
19
|
+
~subIdField=?,
|
|
20
|
+
~ttl=?,
|
|
21
|
+
~api,
|
|
22
|
+
~apiRole,
|
|
23
|
+
~opts,
|
|
24
|
+
) =>
|
|
25
|
+
if QueryDbBackend.isPostgresFor(name) {
|
|
26
|
+
Postgres.make(~name, ~indexes, ~subIdField?, ~ttl?, ~api, ~apiRole, ~opts)
|
|
27
|
+
} else {
|
|
28
|
+
DynamoDb.make(~name, ~indexes, ~subIdField?, ~ttl?, ~api, ~apiRole, ~opts)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module SelectableStream = {
|
|
33
|
+
type api = QueryDbStorage_Postgres.api
|
|
34
|
+
type role = QueryDbStorage_Postgres.role
|
|
35
|
+
let make: ReventlessCore.QueryDb_Adapter.storageMaker<api, role> = (
|
|
36
|
+
~name,
|
|
37
|
+
~indexes,
|
|
38
|
+
~subIdField=?,
|
|
39
|
+
~ttl=?,
|
|
40
|
+
~api,
|
|
41
|
+
~apiRole,
|
|
42
|
+
~opts,
|
|
43
|
+
) =>
|
|
44
|
+
if QueryDbBackend.isPostgresFor(name) {
|
|
45
|
+
Postgres.make(~name, ~indexes, ~subIdField?, ~ttl?, ~api, ~apiRole, ~opts)
|
|
46
|
+
} else {
|
|
47
|
+
DynamoDbStream.make(~name, ~indexes, ~subIdField?, ~ttl?, ~api, ~apiRole, ~opts)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as QueryDbBackend$ReventlessAws from "./QueryDbBackend.res.mjs";
|
|
4
|
+
import * as QueryDbStorage_DynamoDb$ReventlessAws from "./QueryDbStorage_DynamoDb.res.mjs";
|
|
5
|
+
import * as QueryDbStorage_Postgres$ReventlessAws from "./QueryDbStorage_Postgres.res.mjs";
|
|
6
|
+
import * as QueryDbStorage_DynamoDbStream$ReventlessAws from "./QueryDbStorage_DynamoDbStream.res.mjs";
|
|
7
|
+
|
|
8
|
+
function make(name, indexes, subIdField, ttl, api, apiRole, opts) {
|
|
9
|
+
if (QueryDbBackend$ReventlessAws.isPostgresFor(name)) {
|
|
10
|
+
return QueryDbStorage_Postgres$ReventlessAws.make(name, indexes, subIdField, ttl, api, apiRole, opts);
|
|
11
|
+
} else {
|
|
12
|
+
return QueryDbStorage_DynamoDb$ReventlessAws.make(name, indexes, subIdField, ttl, api, apiRole, opts);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let Selectable = {
|
|
17
|
+
make: make
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function make$1(name, indexes, subIdField, ttl, api, apiRole, opts) {
|
|
21
|
+
if (QueryDbBackend$ReventlessAws.isPostgresFor(name)) {
|
|
22
|
+
return QueryDbStorage_Postgres$ReventlessAws.make(name, indexes, subIdField, ttl, api, apiRole, opts);
|
|
23
|
+
} else {
|
|
24
|
+
return QueryDbStorage_DynamoDbStream$ReventlessAws.make(name, indexes, subIdField, ttl, api, apiRole, opts);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let SelectableStream = {
|
|
29
|
+
make: make$1
|
|
30
|
+
};
|
|
3
31
|
|
|
4
32
|
let name = "QueryDbStorage";
|
|
5
33
|
|
|
@@ -9,10 +37,15 @@ let DynamoDbStream;
|
|
|
9
37
|
|
|
10
38
|
let DynamoDb_Runtime;
|
|
11
39
|
|
|
40
|
+
let Postgres;
|
|
41
|
+
|
|
12
42
|
export {
|
|
13
43
|
name,
|
|
14
44
|
DynamoDb,
|
|
15
45
|
DynamoDbStream,
|
|
16
46
|
DynamoDb_Runtime,
|
|
47
|
+
Postgres,
|
|
48
|
+
Selectable,
|
|
49
|
+
SelectableStream,
|
|
17
50
|
}
|
|
18
|
-
/*
|
|
51
|
+
/* QueryDbStorage_DynamoDb-ReventlessAws Not a pure module */
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Deploy-time QueryDb storage for the Postgres backend (B3.1) — the read-model
|
|
2
|
+
// analogue of `EventLogStorage_Postgres` / `DcbEventLogStorage_Postgres`.
|
|
3
|
+
//
|
|
4
|
+
// Creates NO DynamoDB table and NO AppSync data source: read-model state lives in
|
|
5
|
+
// Postgres (`qdb_<name>`, JSONB) and is written by the projection Lambdas via the
|
|
6
|
+
// runtime ops. Returns:
|
|
7
|
+
// - `resources: []` — no stream, no direct AppSync resolvers (suppressed by
|
|
8
|
+
// `QueryDbResolvers.Selectable` until B3.2's Lambda data source);
|
|
9
|
+
// - `dataSourceName: ""` — nothing to attach resolvers to;
|
|
10
|
+
// - `operations` — the Postgres op set bound to the shared `PgConnection` pool.
|
|
11
|
+
// Schema setup is lazy (first operation), so nothing connects during
|
|
12
|
+
// `pulumi up` — a real query only runs inside a Lambda at runtime.
|
|
13
|
+
//
|
|
14
|
+
// Selected by `QueryDbStorage.Selectable(Stream)` when `QueryDbBackend` holds a
|
|
15
|
+
// selection and the read model is not admin-exempt.
|
|
16
|
+
|
|
17
|
+
type api = Types.AppSync.api
|
|
18
|
+
type role = Types.AppSync.role
|
|
19
|
+
|
|
20
|
+
let make: ReventlessCore.QueryDb_Adapter.storageMaker<api, role> = (
|
|
21
|
+
~name,
|
|
22
|
+
~indexes,
|
|
23
|
+
~subIdField=?,
|
|
24
|
+
~ttl as _=?,
|
|
25
|
+
~api as _,
|
|
26
|
+
~apiRole as _,
|
|
27
|
+
~opts as _,
|
|
28
|
+
) => {
|
|
29
|
+
let operations = switch QueryDbBackend.get() {
|
|
30
|
+
| Some({connectionConfig}) =>
|
|
31
|
+
connectionConfig->Pulumi.Output.apply(config =>
|
|
32
|
+
QueryDbStorage_Postgres_Runtime.opsFor(config, ~name, ~indexes, ~subIdField?)
|
|
33
|
+
)
|
|
34
|
+
| None =>
|
|
35
|
+
// Selectable only routes here when a selection is set; guard defensively.
|
|
36
|
+
JsError.throwWithMessage("QueryDbStorage_Postgres.make called without a QueryDbBackend selection")
|
|
37
|
+
}
|
|
38
|
+
{
|
|
39
|
+
ReventlessCore.QueryDb_Adapter.resources: [],
|
|
40
|
+
// B3.2b: the shared PgQueryResolver Lambda data source. Deferred — resolved
|
|
41
|
+
// by PgQueryResolver_Builder.provision after every plugin is built (the
|
|
42
|
+
// resolvers that read it are created even later, inside the schema-pushed
|
|
43
|
+
// resourcesMaker). Same value for every Postgres read model.
|
|
44
|
+
dataSourceName: PgQueryResolver_Builder.dataSourceName,
|
|
45
|
+
operations,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
4
|
+
import * as QueryDbBackend$ReventlessAws from "./QueryDbBackend.res.mjs";
|
|
5
|
+
import * as PgQueryResolver_Builder$ReventlessAws from "./PgQueryResolver_Builder.res.mjs";
|
|
6
|
+
import * as QueryDbStorage_Postgres_Runtime$ReventlessAws from "./QueryDbStorage_Postgres_Runtime.res.mjs";
|
|
7
|
+
|
|
8
|
+
function make(name, indexes, subIdField, param, param$1, param$2, param$3) {
|
|
9
|
+
let match = QueryDbBackend$ReventlessAws.get();
|
|
10
|
+
let operations = match !== undefined ? match.connectionConfig.apply(config => QueryDbStorage_Postgres_Runtime$ReventlessAws.opsFor(config, name, indexes, subIdField)) : Stdlib_JsError.throwWithMessage("QueryDbStorage_Postgres.make called without a QueryDbBackend selection");
|
|
11
|
+
return {
|
|
12
|
+
resources: [],
|
|
13
|
+
dataSourceName: PgQueryResolver_Builder$ReventlessAws.dataSourceName,
|
|
14
|
+
operations: operations
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
make,
|
|
20
|
+
}
|
|
21
|
+
/* PgQueryResolver_Builder-ReventlessAws Not a pure module */
|