@reventlessdev/reventless-local 3.0.0-alpha.226 → 3.0.0-alpha.228
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 +14 -14
- package/src/LocalPlatformRegistry.res +2 -2
- package/src/LocalPlatformRegistry.res.mjs +14 -13
- package/src/Platform.res +14 -15
- package/src/Platform.res.mjs +20 -21
- package/src/adapter/Auth/LocalAuth.res +3 -3
- package/src/adapter/Auth/LocalAuth.res.mjs +3 -2
- package/src/adapter/Auth/UserStore.res +1 -1
- package/src/adapter/Auth/UserStore.res.mjs +7 -6
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +1 -1
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs +1 -1
- package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +1 -1
- package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res.mjs +3 -3
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +2 -2
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +4 -4
- package/src/adapter/DomainGraphQL_Server.res +2 -2
- package/src/adapter/DomainGraphQL_Server.res.mjs +3 -3
- package/src/adapter/DomainMCP_Server.res +1 -1
- package/src/adapter/MCP_ServerInstance.res +1 -1
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +223 -22
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +176 -18
- package/tests/BakedManifestFilesTest.res +0 -1
- package/tests/PluginEventDecodeTest.res +3 -3
- package/tests/PluginEventDecodeTest.res.mjs +4 -4
- package/tests/ShellConfigTest.res +0 -1
- package/tests/UiHintsTest.res +33 -6
- package/tests/UiHintsTest.res.mjs +15 -2
- package/tests/adapter/CommandAuthorizationTest.res.mjs +7 -7
- package/tests/adapter/GraphQL_SchemaInspectorTest.res +9 -7
- package/tests/adapter/GraphQL_SchemaInspectorTest.res.mjs +64 -64
- package/tests/adapter/LocalAuthHttpTest.res +1 -1
- package/tests/adapter/LocalAuthHttpTest.res.mjs +2 -2
- package/tests/adapter/QueryDbListResolverTest.res +82 -2
- package/tests/adapter/QueryDbListResolverTest.res.mjs +111 -16
- package/tests/components/aggregate/AggregateFixtures.res.mjs +6 -6
- package/tests/components/automationslice/AutomationSliceFixtures.res +4 -4
- package/tests/components/automationslice/AutomationSliceFixtures.res.mjs +20 -19
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +2 -2
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +23 -22
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +4 -4
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res.mjs +21 -20
- package/tests/components/commandgenerator/CommandGeneratorFixtures.res +0 -1
- package/tests/components/commandgenerator/CommandGeneratorFixtures.res.mjs +6 -8
- package/tests/components/commandgenerator/CommandGeneratorTest.res +1 -1
- package/tests/components/commandgenerator/CommandGeneratorTest.res.mjs +2 -2
- package/tests/components/commandtopic/CommandTopicFixtures.res.mjs +9 -9
- package/tests/components/commandtopic/CommandTopicStreamFixtures.res.mjs +9 -9
- package/tests/components/dcb/DcbCrossPartitionFixtures.res +3 -3
- package/tests/components/dcb/DcbCrossPartitionFixtures.res.mjs +22 -21
- package/tests/components/dcb/DcbEventLogAppendStreamTest.res +1 -1
- package/tests/components/dcb/DcbEventLogAppendStreamTest.res.mjs +2 -2
- package/tests/components/dcb/DcbFixtures.res +2 -2
- package/tests/components/dcb/DcbFixtures.res.mjs +12 -11
- package/tests/components/eventlog/EventLogFixtures.res.mjs +6 -6
- package/tests/components/eventlog/EventLogProvisioningSeamTest.res.mjs +3 -3
- package/tests/components/eventtopic/EventTopicFixtures.res.mjs +6 -6
- package/tests/components/eventtopic/EventTopicStreamFixtures.res.mjs +6 -6
- package/tests/components/extensionpoint/ExtensionPointFixtures.res +0 -1
- package/tests/components/extensionpoint/ExtensionPointFixtures.res.mjs +11 -13
- package/tests/components/extensionpoint/ExtensionPointTest.res +3 -3
- package/tests/components/extensionpoint/ExtensionPointTest.res.mjs +4 -4
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +10 -10
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs +9 -9
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res.mjs +23 -21
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +2 -2
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res.mjs +17 -14
- package/tests/components/querydb/QueryDbFixtures.res.mjs +4 -4
- package/tests/components/querydb/QueryDbSubIdFixtures.res.mjs +6 -6
- package/tests/components/readmodel/DcbReadModelE2EFixtures.res +3 -3
- package/tests/components/readmodel/DcbReadModelE2EFixtures.res.mjs +17 -16
- package/tests/components/readmodel/DcbReadModelTypoFailFastTest.res.mjs +7 -7
- package/tests/components/readmodel/MixedSourceReadModelFixtures.res +5 -5
- package/tests/components/readmodel/MixedSourceReadModelFixtures.res.mjs +20 -19
- package/tests/components/readmodel/ReadModelFixtures.res +3 -3
- package/tests/components/readmodel/ReadModelFixtures.res.mjs +9 -8
- package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +2 -3
- package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +7 -8
- package/tests/components/stateviewslice/StateViewSliceFixtures.res +2 -2
- package/tests/components/stateviewslice/StateViewSliceFixtures.res.mjs +22 -21
- package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +2 -2
- package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res.mjs +27 -26
- package/tests/components/task/TaskFixtures.res +0 -1
- package/tests/components/task/TaskFixtures.res.mjs +0 -3
|
@@ -110,7 +110,7 @@ let handleLogin = (req: nodeRequest, res: nodeResponse): unit =>
|
|
|
110
110
|
| None => Reventless.Identity.anonymous
|
|
111
111
|
}
|
|
112
112
|
let identityJson =
|
|
113
|
-
identity->
|
|
113
|
+
identity->Reventless.Util_Sury.toJson(Reventless.Identity.schema)
|
|
114
114
|
_writeJson(
|
|
115
115
|
res,
|
|
116
116
|
~status=200,
|
|
@@ -171,7 +171,7 @@ let handleSwitchRole = (req: nodeRequest, res: nodeResponse): unit =>
|
|
|
171
171
|
("token", JSON.Encode.string(newToken)),
|
|
172
172
|
(
|
|
173
173
|
"identity",
|
|
174
|
-
identity->
|
|
174
|
+
identity->Reventless.Util_Sury.toJson(Reventless.Identity.schema),
|
|
175
175
|
),
|
|
176
176
|
]),
|
|
177
177
|
),
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as S from "sury/src/S.res.mjs";
|
|
4
3
|
import * as Http from "http";
|
|
5
4
|
import * as Graphql from "graphql";
|
|
6
5
|
import * as GraphqlYoga from "@reventlessdev/rescript-graphql-yoga/src/GraphqlYoga.res.mjs";
|
|
@@ -14,6 +13,7 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
14
13
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
15
14
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
16
15
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
16
|
+
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
17
17
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
18
18
|
import * as Api_Ids$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/Api_Ids.res.mjs";
|
|
19
19
|
import * as LocalAuth$ReventlessLocal from "./Auth/LocalAuth.res.mjs";
|
|
@@ -75,7 +75,7 @@ function handleLogin(req, res) {
|
|
|
75
75
|
if (result.TAG === "Ok") {
|
|
76
76
|
let i = LocalAuth$ReventlessLocal.Login.mintedIdentity(username, activeRole);
|
|
77
77
|
let identity = i !== undefined ? i : Identity$Reventless.anonymous;
|
|
78
|
-
let identityJson =
|
|
78
|
+
let identityJson = Util_Sury$Reventless.toJson(identity, Identity$Reventless.schema);
|
|
79
79
|
_writeJson(res, 200, Object.fromEntries([
|
|
80
80
|
[
|
|
81
81
|
"token",
|
|
@@ -130,7 +130,7 @@ function handleSwitchRole(req, res) {
|
|
|
130
130
|
],
|
|
131
131
|
[
|
|
132
132
|
"identity",
|
|
133
|
-
|
|
133
|
+
Util_Sury$Reventless.toJson(identity, Identity$Reventless.schema)
|
|
134
134
|
]
|
|
135
135
|
]));
|
|
136
136
|
});
|
|
@@ -143,7 +143,7 @@ let extractIdentity = (req: McpSdk.incomingMessage): Reventless.Identity.t => {
|
|
|
143
143
|
| Some(json) =>
|
|
144
144
|
json
|
|
145
145
|
->JSON.parseOrThrow
|
|
146
|
-
->S.parseOrThrow(Reventless.Identity.schema)
|
|
146
|
+
->S.parseOrThrow(~to=Reventless.Identity.schema)
|
|
147
147
|
| None => Reventless.Identity.anonymous
|
|
148
148
|
}
|
|
149
149
|
} catch {
|
|
@@ -157,7 +157,7 @@ let make = (~label: string="MCP"): t => {
|
|
|
157
157
|
| Some(json) =>
|
|
158
158
|
json
|
|
159
159
|
->JSON.parseOrThrow
|
|
160
|
-
->S.parseOrThrow(Reventless.Identity.schema)
|
|
160
|
+
->S.parseOrThrow(~to=Reventless.Identity.schema)
|
|
161
161
|
| None => Reventless.Identity.anonymous
|
|
162
162
|
}
|
|
163
163
|
} catch {
|
|
@@ -228,8 +228,6 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
228
228
|
)
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
let cap = s => s->String.charAt(0)->String.toUpperCase ++ s->String.slice(~start=1)
|
|
232
|
-
|
|
233
231
|
// Resolve query field names: check registry first, fall back to safe defaults.
|
|
234
232
|
// Fallbacks use simple GraphQL built-in types to avoid referencing non-existent custom types.
|
|
235
233
|
let registryEntry = Plugin_Helpers.queryFieldNamesRegistry->Dict.get(name)
|
|
@@ -271,10 +269,16 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
271
269
|
}
|
|
272
270
|
|
|
273
271
|
// -- Main query: getById ---------------------------------------------------
|
|
272
|
+
// `includeRetired` here matches the connection field and the AppSync SDL the
|
|
273
|
+
// FragmentGenerator emits. The resolver below already reads it — `retiredAllows`
|
|
274
|
+
// consults `askedForRetired(~args)` on every door — so this argument was the
|
|
275
|
+
// only missing half, and without it an elevated caller could see an archived
|
|
276
|
+
// row in a list and not open it.
|
|
274
277
|
let byIdSdl = if includeIdParam {
|
|
275
278
|
switch subIdField {
|
|
276
|
-
| Some(sf) =>
|
|
277
|
-
|
|
279
|
+
| Some(sf) =>
|
|
280
|
+
` ${singleQueryName}(id: ID!, ${sf}: String, includeRetired: Boolean): ${returnTypeName}`
|
|
281
|
+
| None => ` ${singleQueryName}(id: ID!, includeRetired: Boolean): ${returnTypeName}`
|
|
278
282
|
}
|
|
279
283
|
} else {
|
|
280
284
|
` ${singleQueryName}: ${returnTypeName}`
|
|
@@ -402,6 +406,124 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
402
406
|
| None => "id"
|
|
403
407
|
}
|
|
404
408
|
|
|
409
|
+
// -- Reference door: {listFieldName}Refs -----------------------------------
|
|
410
|
+
// Names the rows a caller already holds pointers to, and nothing more. Three
|
|
411
|
+
// fields, fixed by the type, so this cannot be widened by what a caller
|
|
412
|
+
// selects.
|
|
413
|
+
//
|
|
414
|
+
// Retirement is lifted here only where the view declared `@namedWhenRetired`.
|
|
415
|
+
// Ownership is not lifted at all, and the asymmetry is the point: an archived
|
|
416
|
+
// product is a thing the shop sold and every order naming one may say what it
|
|
417
|
+
// was, while a deactivated customer is somebody's row before it is anything
|
|
418
|
+
// else. One predicate answers "is this row still on offer", the other "is this
|
|
419
|
+
// row yours", and only the first is what an archive withdraws.
|
|
420
|
+
let refsSdl = if includeIdParam && subIdField === None {
|
|
421
|
+
[GraphQL_FragmentGenerator.deriveRefsQueryField(~listFieldName=listQueryName, ~returnTypeName)]
|
|
422
|
+
} else {
|
|
423
|
+
[]
|
|
424
|
+
}
|
|
425
|
+
let refsResolverEntry: option<(string, ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn)> =
|
|
426
|
+
if includeIdParam && subIdField === None {
|
|
427
|
+
server.registerTypes(~sdlTypes=[GraphQL_FragmentGenerator.deriveRefTypeSdl(~returnTypeName)])
|
|
428
|
+
let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (
|
|
429
|
+
_root,
|
|
430
|
+
args,
|
|
431
|
+
ctx,
|
|
432
|
+
) => {
|
|
433
|
+
switch await runInterceptor(~ctx, ~args) {
|
|
434
|
+
| Deny(_) => []->JSON.Encode.array
|
|
435
|
+
| Allow =>
|
|
436
|
+
let ids =
|
|
437
|
+
args
|
|
438
|
+
->JSON.Decode.object
|
|
439
|
+
->Option.flatMap(d => d->Dict.get("ids"))
|
|
440
|
+
->Option.flatMap(JSON.Decode.array)
|
|
441
|
+
->Option.getOr([])
|
|
442
|
+
->Array.filterMap(JSON.Decode.string)
|
|
443
|
+
let spec = retiredSpecOf()
|
|
444
|
+
let namesRetired = spec->Option.mapOr(false, r => r.namedWhenRetired)
|
|
445
|
+
// The row's own retirement, read with the same `isRetiredValue` every
|
|
446
|
+
// other door narrows by — so "retired" here and "withheld" there are
|
|
447
|
+
// one answer, not two that drift.
|
|
448
|
+
let retirementOf = (item: JSON.t) =>
|
|
449
|
+
switch spec {
|
|
450
|
+
| None => (false, None)
|
|
451
|
+
| Some(r) =>
|
|
452
|
+
let cell = item->JSON.Decode.object->Option.flatMap(d => d->Dict.get(r.field))
|
|
453
|
+
let scope: Reventless.OwnerScope.retiredScope = {field: r.field, values: r.values}
|
|
454
|
+
let retired = scope->Reventless.OwnerScope.isRetiredValue(cell)
|
|
455
|
+
(
|
|
456
|
+
retired,
|
|
457
|
+
// The state that retired it, and only that. A live row reports
|
|
458
|
+
// none — this door names rows, it does not publish a lifecycle
|
|
459
|
+
// column to callers the list withholds. Nor does the boolean
|
|
460
|
+
// form, where the field is the state and `retired` said it.
|
|
461
|
+
retired && r.values->Option.isSome
|
|
462
|
+
? cell->Option.flatMap(JSON.Decode.string)
|
|
463
|
+
: None,
|
|
464
|
+
)
|
|
465
|
+
}
|
|
466
|
+
switch Bus.getQueryDb(name) {
|
|
467
|
+
| Some(ops) =>
|
|
468
|
+
let load = key =>
|
|
469
|
+
ops.loadStream(key)
|
|
470
|
+
->Stream.runCollect
|
|
471
|
+
->Effect.catchAll(_ => Effect.succeed([]))
|
|
472
|
+
->Effect.runPromise
|
|
473
|
+
let loaded = await ids->Array.map(async id =>
|
|
474
|
+
switch (await load(id))->Array.get(0) {
|
|
475
|
+
| Some(item) => (id, Some(item))
|
|
476
|
+
| None =>
|
|
477
|
+
switch Api_Ids.alternateKey(id) {
|
|
478
|
+
| Some(localId) => (localId, (await load(localId))->Array.get(0))
|
|
479
|
+
| None => (id, None)
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
)->Promise.all
|
|
483
|
+
loaded
|
|
484
|
+
->Array.filterMap(((id, opt)) =>
|
|
485
|
+
opt
|
|
486
|
+
->Option.filter(item => ownerAllows(~ctx, item))
|
|
487
|
+
->Option.flatMap(item => {
|
|
488
|
+
let (retired, state) = retirementOf(item)
|
|
489
|
+
// A retired row leaves through this door only where the view
|
|
490
|
+
// said it may. Where it did not, the door answers exactly as
|
|
491
|
+
// every other one does — with nothing.
|
|
492
|
+
if retired && !namesRetired {
|
|
493
|
+
None
|
|
494
|
+
} else {
|
|
495
|
+
let label =
|
|
496
|
+
item
|
|
497
|
+
->JSON.Decode.object
|
|
498
|
+
->Option.flatMap(d => d->Dict.get(labelField))
|
|
499
|
+
->Option.flatMap(JSON.Decode.string)
|
|
500
|
+
// A view with no label field resolves to its id, which is
|
|
501
|
+
// what `labelField`'s own fallback already decided.
|
|
502
|
+
->Option.getOr(id)
|
|
503
|
+
Some(
|
|
504
|
+
Dict.fromArray([
|
|
505
|
+
("id", JSON.Encode.string(id)),
|
|
506
|
+
("label", JSON.Encode.string(label)),
|
|
507
|
+
("retired", JSON.Encode.bool(retired)),
|
|
508
|
+
(
|
|
509
|
+
"retiredState",
|
|
510
|
+
state->Option.mapOr(JSON.Encode.null, JSON.Encode.string),
|
|
511
|
+
),
|
|
512
|
+
])->JSON.Encode.object,
|
|
513
|
+
)
|
|
514
|
+
}
|
|
515
|
+
})
|
|
516
|
+
)
|
|
517
|
+
->JSON.Encode.array
|
|
518
|
+
| None => []->JSON.Encode.array
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
Some((listQueryName ++ "Refs", resolver))
|
|
523
|
+
} else {
|
|
524
|
+
None
|
|
525
|
+
}
|
|
526
|
+
|
|
405
527
|
// -- List query -------------------------------------------------------------
|
|
406
528
|
// Look up the registered state schema (populated alongside queryFieldNamesRegistry)
|
|
407
529
|
// so the resolver derives the same serverCapability the FragmentGenerator emitted.
|
|
@@ -666,20 +788,95 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
666
788
|
}
|
|
667
789
|
|
|
668
790
|
// -- Index queries: {name}By{Index} ---------------------------------------
|
|
791
|
+
// Name, argument and return type all come from `GraphQL_FragmentGenerator`,
|
|
792
|
+
// which is also where the AppSync SDL gets them. They used to be spelled out
|
|
793
|
+
// here independently and disagreed with that emitter on all three, so the
|
|
794
|
+
// same door had a different signature per backend and worked on neither.
|
|
669
795
|
let indexSdlFields = indexes->Array.map((ic: Reventless.ReadModel.indexConfig) =>
|
|
670
|
-
|
|
796
|
+
GraphQL_FragmentGenerator.deriveIndexQueryField(
|
|
797
|
+
~singleFieldName=singleQueryName,
|
|
798
|
+
~indexConfig=ic,
|
|
799
|
+
~connectionTypeName=returnTypeName ++ "Connection",
|
|
800
|
+
)
|
|
671
801
|
)
|
|
802
|
+
// Backward paging is refused rather than ignored, and refused on both
|
|
803
|
+
// backends rather than on the one that cannot do it. `listAllItemsConnection`
|
|
804
|
+
// already sets the rule for a door whose read cannot walk backwards — "fail
|
|
805
|
+
// loud rather than silently returning the forward page" — and its reasoning
|
|
806
|
+
// is about the answer, not about DynamoDB: `last: 2` handed the first two
|
|
807
|
+
// rows is a different question answered without saying so.
|
|
808
|
+
//
|
|
809
|
+
// The arguments stay in the SDL. One that appeared and disappeared with the
|
|
810
|
+
// backend would make adding a sort key a breaking schema change and force
|
|
811
|
+
// every client to feature-detect, which is the same argument `includeRetired`
|
|
812
|
+
// and the reference door are emitted unconditionally for.
|
|
813
|
+
let rejectBackwardPaging = (~args) => {
|
|
814
|
+
let given = key =>
|
|
815
|
+
switch args->JSON.Decode.object->Option.flatMap(d => d->Dict.get(key)) {
|
|
816
|
+
| None | Some(JSON.Null) => false
|
|
817
|
+
| Some(_) => true
|
|
818
|
+
}
|
|
819
|
+
if given("last") || given("before") {
|
|
820
|
+
throw(
|
|
821
|
+
GraphQL_CallerError.badUserInput(
|
|
822
|
+
"Backward pagination (last/before) is not supported on by-index connections; use first/after.",
|
|
823
|
+
),
|
|
824
|
+
)
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
// A positional connection. This door answers an equality lookup on one index
|
|
828
|
+
// value and has no ordering key to cut a cursor from — the list door borrows
|
|
829
|
+
// its sort key because it orders by one. An offset is stable for as long as
|
|
830
|
+
// a page-forward takes, which is what the field advertises and no more.
|
|
831
|
+
let indexConnection = (~args, items: array<JSON.t>) => {
|
|
832
|
+
let arg = key => args->JSON.Decode.object->Option.flatMap(d => d->Dict.get(key))
|
|
833
|
+
let after =
|
|
834
|
+
arg("after")->Option.flatMap(JSON.Decode.string)->Option.flatMap(s => Int.fromString(s))->Option.getOr(-1)
|
|
835
|
+
let first =
|
|
836
|
+
arg("first")->Option.flatMap(JSON.Decode.float)->Option.map(Float.toInt)->Option.getOr(50)
|
|
837
|
+
let start = after + 1
|
|
838
|
+
let page = items->Array.slice(~start, ~end=start + first)
|
|
839
|
+
let taken = page->Array.length
|
|
840
|
+
let edges =
|
|
841
|
+
page->Array.mapWithIndex((item, i) =>
|
|
842
|
+
Dict.fromArray([
|
|
843
|
+
("node", item),
|
|
844
|
+
("cursor", (start + i)->Int.toString->JSON.Encode.string),
|
|
845
|
+
])->JSON.Encode.object
|
|
846
|
+
)
|
|
847
|
+
let cursorAt = i => i->Int.toString->JSON.Encode.string
|
|
848
|
+
Dict.fromArray([
|
|
849
|
+
("edges", edges->JSON.Encode.array),
|
|
850
|
+
(
|
|
851
|
+
"pageInfo",
|
|
852
|
+
Dict.fromArray([
|
|
853
|
+
("hasNextPage", JSON.Encode.bool(start + taken < items->Array.length)),
|
|
854
|
+
("hasPreviousPage", JSON.Encode.bool(start > 0)),
|
|
855
|
+
("startCursor", taken > 0 ? cursorAt(start) : JSON.Encode.null),
|
|
856
|
+
("endCursor", taken > 0 ? cursorAt(start + taken - 1) : JSON.Encode.null),
|
|
857
|
+
])->JSON.Encode.object,
|
|
858
|
+
),
|
|
859
|
+
])->JSON.Encode.object
|
|
860
|
+
}
|
|
672
861
|
let indexResolvers: array<(string, ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn)> = indexes->Array.map(
|
|
673
862
|
(ic: Reventless.ReadModel.indexConfig) => {
|
|
674
|
-
let
|
|
675
|
-
|
|
676
|
-
|
|
863
|
+
let resolverName = GraphQL_FragmentGenerator.indexQueryFieldName(
|
|
864
|
+
~singleFieldName=singleQueryName,
|
|
865
|
+
~index=ic.index,
|
|
866
|
+
)
|
|
867
|
+
// The argument the caller passes and the row field this filters on are
|
|
868
|
+
// one and the same — the door reads the index's key, not its name.
|
|
869
|
+
let filterField = GraphQL_FragmentGenerator.indexKeyField(ic)
|
|
677
870
|
let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (_root, args, ctx) => {
|
|
678
871
|
switch await runInterceptor(~ctx, ~args) {
|
|
679
|
-
| Deny(_) => []
|
|
872
|
+
| Deny(_) => indexConnection(~args, [])
|
|
680
873
|
| Allow =>
|
|
874
|
+
// After the interceptor, matching the AppSync pipeline, where the
|
|
875
|
+
// interceptor leads the chain and the query function's `request`
|
|
876
|
+
// raises this second.
|
|
877
|
+
rejectBackwardPaging(~args)
|
|
681
878
|
let value =
|
|
682
|
-
args->JSON.Decode.object->Option.flatMap(d => d->Dict.get(
|
|
879
|
+
args->JSON.Decode.object->Option.flatMap(d => d->Dict.get(filterField))->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
683
880
|
// Applied to whichever arm answers, rather than inside one of them: the
|
|
684
881
|
// push-down and the scan are two ways to reach the same rows, and a
|
|
685
882
|
// narrowing that lives in only one is a hole that appears when a
|
|
@@ -690,22 +887,24 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
690
887
|
// in-memory reuses its lazy snapshot). Fall back to scan+filter only
|
|
691
888
|
// if no lookup is registered for this QueryDb.
|
|
692
889
|
switch Bus.getQueryDbIndexLookup(name) {
|
|
693
|
-
| Some(lookup) => lookup(filterField, value)->scoped
|
|
890
|
+
| Some(lookup) => indexConnection(~args, lookup(filterField, value)->scoped)
|
|
694
891
|
| None =>
|
|
695
892
|
switch Bus.getQueryDbScan(name) {
|
|
696
893
|
| Some(scanAll) =>
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
->
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
894
|
+
indexConnection(
|
|
895
|
+
~args,
|
|
896
|
+
scanAll()
|
|
897
|
+
->Array.filter(item =>
|
|
898
|
+
item
|
|
899
|
+
->JSON.Decode.object
|
|
900
|
+
->Option.flatMap(d => d->Dict.get(filterField))
|
|
901
|
+
->Option.flatMap(JSON.Decode.string)
|
|
902
|
+
->Option.map(v => v == value)
|
|
903
|
+
->Option.getOr(false)
|
|
904
|
+
)
|
|
905
|
+
->scoped,
|
|
705
906
|
)
|
|
706
|
-
|
|
707
|
-
->JSON.Encode.array
|
|
708
|
-
| None => []->JSON.Encode.array
|
|
907
|
+
| None => indexConnection(~args, [])
|
|
709
908
|
}
|
|
710
909
|
}
|
|
711
910
|
}
|
|
@@ -718,6 +917,7 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
718
917
|
let allSdl =
|
|
719
918
|
[byIdSdl]
|
|
720
919
|
->Array.concat(byIdsSdl)
|
|
920
|
+
->Array.concat(refsSdl)
|
|
721
921
|
->Array.concat(listSdl)
|
|
722
922
|
->Array.concat(itemsSdl)
|
|
723
923
|
->Array.concat(indexSdlFields)
|
|
@@ -725,6 +925,7 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
725
925
|
let resolvers = Dict.make()
|
|
726
926
|
resolvers->Dict.set(singleQueryName, byIdResolver)
|
|
727
927
|
byIdsResolverEntry->Option.forEach(((k, v)) => resolvers->Dict.set(k, v))
|
|
928
|
+
refsResolverEntry->Option.forEach(((k, v)) => resolvers->Dict.set(k, v))
|
|
728
929
|
listResolvers->Array.forEach(((k, v)) => resolvers->Dict.set(k, v))
|
|
729
930
|
itemsResolvers->Array.forEach(((k, v)) => resolvers->Dict.set(k, v))
|
|
730
931
|
indexResolvers->Array.forEach(((k, v)) => resolvers->Dict.set(k, v))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
4
|
+
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
4
5
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
6
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
7
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
@@ -16,6 +17,7 @@ import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/s
|
|
|
16
17
|
import * as SortKey_Filter$ReventlessLocal from "./SortKey_Filter.res.mjs";
|
|
17
18
|
import * as QueryDbListQuery$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/QueryDbListQuery.res.mjs";
|
|
18
19
|
import * as QueryDb_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/QueryDb/QueryDb_Callback.res.mjs";
|
|
20
|
+
import * as GraphQL_CallerError$ReventlessLocal from "../GraphQL_CallerError.res.mjs";
|
|
19
21
|
import * as DomainGraphQL_Server$ReventlessLocal from "../DomainGraphQL_Server.res.mjs";
|
|
20
22
|
import * as OwnerScopeDiagnostics$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/OwnerScopeDiagnostics.res.mjs";
|
|
21
23
|
import * as GraphQL_FragmentGenerator$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/GraphQL_FragmentGenerator.res.mjs";
|
|
@@ -112,7 +114,6 @@ function Make(Bus) {
|
|
|
112
114
|
return true;
|
|
113
115
|
}
|
|
114
116
|
};
|
|
115
|
-
let cap = s => s.charAt(0).toUpperCase() + s.slice(1);
|
|
116
117
|
let registryEntry = Plugin_Helpers$ReventlessCore.queryFieldNamesRegistry[name];
|
|
117
118
|
let singleQueryName = registryEntry !== undefined ? registryEntry.singleFieldName : name.charAt(0).toLowerCase() + name.slice(1);
|
|
118
119
|
let listQueryName = registryEntry !== undefined ? registryEntry.listFieldName : name + "s";
|
|
@@ -124,7 +125,7 @@ function Make(Bus) {
|
|
|
124
125
|
relay.registerNodeType(returnTypeName, name);
|
|
125
126
|
}
|
|
126
127
|
let byIdSdl = includeIdParam ? (
|
|
127
|
-
subIdField !== undefined ? ` ` + singleQueryName + `(id: ID!, ` + subIdField + `: String): ` + returnTypeName : ` ` + singleQueryName + `(id: ID
|
|
128
|
+
subIdField !== undefined ? ` ` + singleQueryName + `(id: ID!, ` + subIdField + `: String, includeRetired: Boolean): ` + returnTypeName : ` ` + singleQueryName + `(id: ID!, includeRetired: Boolean): ` + returnTypeName
|
|
128
129
|
) : ` ` + singleQueryName + `: ` + returnTypeName;
|
|
129
130
|
let byIdResolver = async (_root, args, ctx) => {
|
|
130
131
|
let match = await runInterceptor(ctx, args);
|
|
@@ -219,6 +220,101 @@ function Make(Bus) {
|
|
|
219
220
|
byIdsResolverEntry = undefined;
|
|
220
221
|
}
|
|
221
222
|
let labelField = registryEntry !== undefined ? Stdlib_Option.getOr(registryEntry.labelField, "id") : "id";
|
|
223
|
+
let refsSdl = includeIdParam && subIdField === undefined ? [GraphQL_FragmentGenerator$ReventlessCore.deriveRefsQueryField(listQueryName, returnTypeName)] : [];
|
|
224
|
+
let refsResolverEntry;
|
|
225
|
+
if (includeIdParam && subIdField === undefined) {
|
|
226
|
+
server.registerTypes([GraphQL_FragmentGenerator$ReventlessCore.deriveRefTypeSdl(returnTypeName)]);
|
|
227
|
+
let resolver$1 = async (_root, args, ctx) => {
|
|
228
|
+
let match = await runInterceptor(ctx, args);
|
|
229
|
+
if (typeof match === "object") {
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
let ids = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(args), d => d["ids"]), Stdlib_JSON.Decode.array), []), Stdlib_JSON.Decode.string);
|
|
233
|
+
let spec = retiredSpecOf();
|
|
234
|
+
let namesRetired = Stdlib_Option.mapOr(spec, false, r => r.namedWhenRetired);
|
|
235
|
+
let retirementOf = item => {
|
|
236
|
+
if (spec === undefined) {
|
|
237
|
+
return [
|
|
238
|
+
false,
|
|
239
|
+
undefined
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
let cell = Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(item), d => d[spec.field]);
|
|
243
|
+
let scope_field = spec.field;
|
|
244
|
+
let scope_values = spec.values;
|
|
245
|
+
let scope = {
|
|
246
|
+
field: scope_field,
|
|
247
|
+
values: scope_values
|
|
248
|
+
};
|
|
249
|
+
let retired = OwnerScope$Reventless.isRetiredValue(scope, cell);
|
|
250
|
+
return [
|
|
251
|
+
retired,
|
|
252
|
+
retired && Stdlib_Option.isSome(spec.values) ? Stdlib_Option.flatMap(cell, Stdlib_JSON.Decode.string) : undefined
|
|
253
|
+
];
|
|
254
|
+
};
|
|
255
|
+
let ops = Bus.getQueryDb(name);
|
|
256
|
+
if (ops === undefined) {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
let load = key => Effect.runPromise(Effect.catchAll(Stream.runCollect(ops.loadStream(key)), param => Effect.succeed([])));
|
|
260
|
+
let loaded = await Promise.all(ids.map(async id => {
|
|
261
|
+
let item = (await load(id))[0];
|
|
262
|
+
if (item !== undefined) {
|
|
263
|
+
return [
|
|
264
|
+
id,
|
|
265
|
+
item
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
let localId = Api_Ids$ReventlessCore.alternateKey(id);
|
|
269
|
+
if (localId !== undefined) {
|
|
270
|
+
return [
|
|
271
|
+
localId,
|
|
272
|
+
(await load(localId))[0]
|
|
273
|
+
];
|
|
274
|
+
} else {
|
|
275
|
+
return [
|
|
276
|
+
id,
|
|
277
|
+
undefined
|
|
278
|
+
];
|
|
279
|
+
}
|
|
280
|
+
}));
|
|
281
|
+
return Stdlib_Array.filterMap(loaded, param => {
|
|
282
|
+
let id = param[0];
|
|
283
|
+
return Stdlib_Option.flatMap(Stdlib_Option.filter(param[1], item => ownerAllows(ctx, item)), item => {
|
|
284
|
+
let match = retirementOf(item);
|
|
285
|
+
let retired = match[0];
|
|
286
|
+
if (retired && !namesRetired) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
let label = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(item), d => d[labelField]), Stdlib_JSON.Decode.string), id);
|
|
290
|
+
return Object.fromEntries([
|
|
291
|
+
[
|
|
292
|
+
"id",
|
|
293
|
+
id
|
|
294
|
+
],
|
|
295
|
+
[
|
|
296
|
+
"label",
|
|
297
|
+
label
|
|
298
|
+
],
|
|
299
|
+
[
|
|
300
|
+
"retired",
|
|
301
|
+
retired
|
|
302
|
+
],
|
|
303
|
+
[
|
|
304
|
+
"retiredState",
|
|
305
|
+
Stdlib_Option.mapOr(match[1], null, prim => prim)
|
|
306
|
+
]
|
|
307
|
+
]);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
};
|
|
311
|
+
refsResolverEntry = [
|
|
312
|
+
listQueryName + "Refs",
|
|
313
|
+
resolver$1
|
|
314
|
+
];
|
|
315
|
+
} else {
|
|
316
|
+
refsResolverEntry = undefined;
|
|
317
|
+
}
|
|
222
318
|
let stateSchemaOpt = Plugin_Helpers$ReventlessCore.stateSchemaRegistry[name];
|
|
223
319
|
let capability = stateSchemaOpt !== undefined ? GraphQL_FragmentGenerator$ReventlessCore.deriveServerCapability(name, stateSchemaOpt) : GraphQL_FragmentGenerator$ReventlessCore.emptyCapability;
|
|
224
320
|
let fetchAllItems = async () => {
|
|
@@ -250,7 +346,7 @@ function Make(Bus) {
|
|
|
250
346
|
endCursor: null
|
|
251
347
|
}
|
|
252
348
|
};
|
|
253
|
-
let resolver$
|
|
349
|
+
let resolver$2 = async (_root, args, ctx) => {
|
|
254
350
|
let match = await runInterceptor(ctx, args);
|
|
255
351
|
if (typeof match === "object") {
|
|
256
352
|
return emptyConnection;
|
|
@@ -277,11 +373,11 @@ function Make(Bus) {
|
|
|
277
373
|
};
|
|
278
374
|
match = [
|
|
279
375
|
sdl,
|
|
280
|
-
resolver$
|
|
376
|
+
resolver$2
|
|
281
377
|
];
|
|
282
378
|
} else {
|
|
283
379
|
let sdl$1 = [` ` + listQueryName + `(nextToken: String, limit: Int): ` + pluralTypeName + `!`];
|
|
284
|
-
let resolver$
|
|
380
|
+
let resolver$3 = async (_root, args, ctx) => {
|
|
285
381
|
let match = await runInterceptor(ctx, args);
|
|
286
382
|
if (typeof match === "object") {
|
|
287
383
|
return {
|
|
@@ -306,7 +402,7 @@ function Make(Bus) {
|
|
|
306
402
|
};
|
|
307
403
|
match = [
|
|
308
404
|
sdl$1,
|
|
309
|
-
resolver$
|
|
405
|
+
resolver$3
|
|
310
406
|
];
|
|
311
407
|
}
|
|
312
408
|
let listResolvers = [[
|
|
@@ -324,7 +420,7 @@ function Make(Bus) {
|
|
|
324
420
|
}
|
|
325
421
|
let itemsResolvers;
|
|
326
422
|
if (subIdField !== undefined) {
|
|
327
|
-
let resolver$
|
|
423
|
+
let resolver$4 = async (_root, args, ctx) => {
|
|
328
424
|
let emptyConn = {
|
|
329
425
|
edges: [],
|
|
330
426
|
pageInfo: {
|
|
@@ -423,22 +519,81 @@ function Make(Bus) {
|
|
|
423
519
|
};
|
|
424
520
|
itemsResolvers = [[
|
|
425
521
|
singleQueryName + "Items",
|
|
426
|
-
resolver$
|
|
522
|
+
resolver$4
|
|
427
523
|
]];
|
|
428
524
|
} else {
|
|
429
525
|
itemsResolvers = [];
|
|
430
526
|
}
|
|
431
|
-
let indexSdlFields = indexes.map(ic =>
|
|
527
|
+
let indexSdlFields = indexes.map(ic => GraphQL_FragmentGenerator$ReventlessCore.deriveIndexQueryField(singleQueryName, ic, returnTypeName + "Connection"));
|
|
528
|
+
let rejectBackwardPaging = args => {
|
|
529
|
+
let given = key => {
|
|
530
|
+
let match = Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(args), d => d[key]);
|
|
531
|
+
if (match !== undefined) {
|
|
532
|
+
return match !== null;
|
|
533
|
+
} else {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
if (!(given("last") || given("before"))) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
throw GraphQL_CallerError$ReventlessLocal.badUserInput("Backward pagination (last/before) is not supported on by-index connections; use first/after.");
|
|
541
|
+
};
|
|
542
|
+
let indexConnection = (args, items) => {
|
|
543
|
+
let arg = key => Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(args), d => d[key]);
|
|
544
|
+
let after = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(arg("after"), Stdlib_JSON.Decode.string), s => Stdlib_Int.fromString(s, undefined)), -1);
|
|
545
|
+
let first = Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(arg("first"), Stdlib_JSON.Decode.float), prim => prim | 0), 50);
|
|
546
|
+
let start = after + 1 | 0;
|
|
547
|
+
let page = items.slice(start, start + first | 0);
|
|
548
|
+
let taken = page.length;
|
|
549
|
+
let edges = page.map((item, i) => Object.fromEntries([
|
|
550
|
+
[
|
|
551
|
+
"node",
|
|
552
|
+
item
|
|
553
|
+
],
|
|
554
|
+
[
|
|
555
|
+
"cursor",
|
|
556
|
+
(start + i | 0).toString()
|
|
557
|
+
]
|
|
558
|
+
]));
|
|
559
|
+
return Object.fromEntries([
|
|
560
|
+
[
|
|
561
|
+
"edges",
|
|
562
|
+
edges
|
|
563
|
+
],
|
|
564
|
+
[
|
|
565
|
+
"pageInfo",
|
|
566
|
+
Object.fromEntries([
|
|
567
|
+
[
|
|
568
|
+
"hasNextPage",
|
|
569
|
+
(start + taken | 0) < items.length
|
|
570
|
+
],
|
|
571
|
+
[
|
|
572
|
+
"hasPreviousPage",
|
|
573
|
+
start > 0
|
|
574
|
+
],
|
|
575
|
+
[
|
|
576
|
+
"startCursor",
|
|
577
|
+
taken > 0 ? start.toString() : null
|
|
578
|
+
],
|
|
579
|
+
[
|
|
580
|
+
"endCursor",
|
|
581
|
+
taken > 0 ? ((start + taken | 0) - 1 | 0).toString() : null
|
|
582
|
+
]
|
|
583
|
+
])
|
|
584
|
+
]
|
|
585
|
+
]);
|
|
586
|
+
};
|
|
432
587
|
let indexResolvers = indexes.map(ic => {
|
|
433
|
-
let
|
|
434
|
-
let
|
|
435
|
-
let filterField = Stdlib_Option.getOr(ic.idField, index);
|
|
588
|
+
let resolverName = GraphQL_FragmentGenerator$ReventlessCore.indexQueryFieldName(singleQueryName, ic.index);
|
|
589
|
+
let filterField = GraphQL_FragmentGenerator$ReventlessCore.indexKeyField(ic);
|
|
436
590
|
let resolver = async (_root, args, ctx) => {
|
|
437
591
|
let match = await runInterceptor(ctx, args);
|
|
438
592
|
if (typeof match === "object") {
|
|
439
|
-
return [];
|
|
593
|
+
return indexConnection(args, []);
|
|
440
594
|
}
|
|
441
|
-
|
|
595
|
+
rejectBackwardPaging(args);
|
|
596
|
+
let value = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(args), d => d[filterField]), Stdlib_JSON.Decode.string), "");
|
|
442
597
|
let scoped = rows => rows.filter(item => {
|
|
443
598
|
if (ownerAllows(ctx, item)) {
|
|
444
599
|
return retiredAllows(ctx, args, item);
|
|
@@ -448,13 +603,13 @@ function Make(Bus) {
|
|
|
448
603
|
});
|
|
449
604
|
let lookup = Bus.getQueryDbIndexLookup(name);
|
|
450
605
|
if (lookup !== undefined) {
|
|
451
|
-
return scoped(lookup(filterField, value));
|
|
606
|
+
return indexConnection(args, scoped(lookup(filterField, value)));
|
|
452
607
|
}
|
|
453
608
|
let scanAll = Bus.getQueryDbScan(name);
|
|
454
609
|
if (scanAll !== undefined) {
|
|
455
|
-
return scoped(scanAll().filter(item => Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(item), d => d[filterField]), Stdlib_JSON.Decode.string), v => v === value), false)));
|
|
610
|
+
return indexConnection(args, scoped(scanAll().filter(item => Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(item), d => d[filterField]), Stdlib_JSON.Decode.string), v => v === value), false))));
|
|
456
611
|
} else {
|
|
457
|
-
return [];
|
|
612
|
+
return indexConnection(args, []);
|
|
458
613
|
}
|
|
459
614
|
};
|
|
460
615
|
return [
|
|
@@ -462,12 +617,15 @@ function Make(Bus) {
|
|
|
462
617
|
resolver
|
|
463
618
|
];
|
|
464
619
|
});
|
|
465
|
-
let allSdl = [byIdSdl].concat(byIdsSdl).concat(match[0]).concat(itemsSdl).concat(indexSdlFields);
|
|
620
|
+
let allSdl = [byIdSdl].concat(byIdsSdl).concat(refsSdl).concat(match[0]).concat(itemsSdl).concat(indexSdlFields);
|
|
466
621
|
let resolvers = {};
|
|
467
622
|
resolvers[singleQueryName] = byIdResolver;
|
|
468
623
|
Stdlib_Option.forEach(byIdsResolverEntry, param => {
|
|
469
624
|
resolvers[param[0]] = param[1];
|
|
470
625
|
});
|
|
626
|
+
Stdlib_Option.forEach(refsResolverEntry, param => {
|
|
627
|
+
resolvers[param[0]] = param[1];
|
|
628
|
+
});
|
|
471
629
|
listResolvers.forEach(param => {
|
|
472
630
|
resolvers[param[0]] = param[1];
|
|
473
631
|
});
|