@reventlessdev/reventless-core 3.0.0-alpha.260 → 3.0.0-alpha.261
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 +8 -0
- package/package.json +9 -9
- package/src/Behavior.res +1 -4
- package/src/Message.res +17 -14
- package/src/Projection.res +51 -25
- package/src/RequestContext.res +8 -9
- package/src/ResourceAttribution.res +28 -14
- package/src/adapter/Adapter.res +48 -4
- package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +5 -5
- package/src/adapter/Messaging/Messaging_Log_Backend.res +10 -7
- package/src/adapter/Messaging/Messaging_Log_Backend.res.mjs +2 -4
- package/src/adapter/Monitoring/Monitoring.res +16 -17
- package/src/adapter/QueryInterception/QueryInterception.res +0 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +12 -5
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +11 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res +1 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Single.res +1 -7
- package/src/adapter/RuntimeExtension/RuntimeExtension.res +0 -1
- package/src/admin/Platform_Admin.res +17 -6
- package/src/admin/Platform_AdminApi.res +14 -9
- package/src/admin/Platform_Admin_Callback.res +1 -1
- package/src/admin/Platform_BakedManifest.res +34 -34
- package/src/admin/Platform_ComponentDefinitionsApi.res +9 -21
- package/src/admin/Platform_PluginStructuresApi.res +15 -12
- package/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry.res +1 -1
- package/src/components/Aggregate/Aggregate.res +13 -12
- package/src/components/Aggregate/Aggregate_Builder.res +22 -21
- package/src/components/Aggregate/Aggregate_Callback.res +91 -86
- package/src/components/Api/ApiNoApiHelpers.res +1 -3
- package/src/components/Api/Api_Ids.res +0 -1
- package/src/components/Api/Api_Naming.res +41 -18
- package/src/components/Api/GraphQL_FragmentGenerator.res +38 -51
- package/src/components/Api/GraphQL_SchemaInspector.res +5 -3
- package/src/components/Api/GraphQL_Stitcher.res +9 -10
- package/src/components/Api/MCP_SchemaGenerator.res +8 -12
- package/src/components/Api/OwnerScopeDiagnostics.res +1 -2
- package/src/components/Api/QueryDbListQuery.res +13 -6
- package/src/components/Api/SchemaType.res +28 -23
- package/src/components/Api/SuryToJsonSchema.res +9 -10
- package/src/components/AutomationSlice/AutomationSlice.res +4 -4
- package/src/components/AutomationSlice/AutomationSlice_Builder.res +15 -14
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -3
- package/src/components/Builder_Helpers.res +12 -23
- package/src/components/CommandGenerator/CommandGenerator.res +6 -1
- package/src/components/CommandGenerator/CommandGenerator_Builder.res +29 -21
- package/src/components/CommandGenerator/CommandGenerator_Callback.res +17 -21
- package/src/components/CommandTopic/CommandTopic.res +3 -3
- package/src/components/CommandTopic/CommandTopic_Builder.res +10 -10
- package/src/components/CommandTopic/CommandTopic_Callback.res +1 -4
- package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -2
- package/src/components/Counter/Counter_Builder.res +1 -6
- package/src/components/Counter/Counter_Callback.res +3 -3
- package/src/components/Dcb/Dcb_Builder.res +970 -956
- package/src/components/DcbEventLog/DcbEventLog.res +4 -4
- package/src/components/DcbEventLog/DcbEventLog_Builder.res +0 -1
- package/src/components/DcbEventLog/DcbEventLog_Operations.res +17 -18
- package/src/components/EventCollector/EventCollector_Builder.res +2 -1
- package/src/components/EventLog/EventLog.res +5 -1
- package/src/components/EventLog/EventLog_Operations.res +12 -8
- package/src/components/EventMapper/EventMapper.res +8 -10
- package/src/components/EventMapper/EventMapper_Builder.res +4 -1
- package/src/components/EventMapper/EventMapper_Callback.res +46 -31
- package/src/components/EventTopic/EventTopic.res +3 -3
- package/src/components/EventTopic/EventTopic_Builder.res +1 -1
- package/src/components/Extension/Extension.res +4 -1
- package/src/components/Extension/Extension_Operations.res +11 -3
- package/src/components/ExtensionPoint/ExtensionPoint.res +3 -3
- package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +9 -10
- package/src/components/ExtensionPoint/ExtensionPoint_Callback.res +2 -6
- package/src/components/ExtensionPoint/ExtensionPoint_Operations.res +13 -5
- package/src/components/Heartbeat/Heartbeat_Callback.res +3 -3
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +4 -4
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +7 -10
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res +4 -4
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +15 -14
- package/src/components/QueryDb/QueryDb_Adapter.res +1 -3
- package/src/components/QueryDb/QueryDb_Operations.res +10 -10
- package/src/components/ReadModel/ReadModel.res +3 -3
- package/src/components/ReadModel/ReadModel_Builder.res +1 -2
- package/src/components/ReadModel/ReadModel_Callback.res +2 -1
- package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +5 -2
- package/src/components/StateChangeSlice/StateChangeSlice.res +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +23 -4
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +22 -6
- package/src/components/StateTopic.res +1 -3
- package/src/components/StateViewSlice/StateViewSlice.res +4 -4
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +79 -68
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +3 -1
- package/src/components/Task/Task.res +3 -3
- package/src/components/Task/Task_Adapter.res +4 -1
- package/src/components/Task/Task_Builder.res +24 -21
- package/src/plugin/api/PluginBaseFragment.res +9 -3
- package/src/plugin/component/Capability_Inference.res +8 -10
- package/src/plugin/component/Plugin.res +6 -6
- package/src/plugin/component/Plugin_Builder.res +54 -50
- package/src/plugin/component/Plugin_Callback.res +10 -6
- package/src/plugin/component/Plugin_Helpers.res +264 -230
- package/src/plugin/component/Plugin_Structure.res +370 -324
- package/src/plugin/component/Plugin_SubscriptionSchema.res +8 -6
- package/src/plugin/component/SchemaWalker.res +11 -12
- package/src/plugin/connect/PluginConnectExtension_Mapping.res +45 -50
- package/src/plugin/lifecycle/PluginBehavior.res +3 -4
- package/src/plugin/lifecycle/PluginSpec.res +1 -2
- package/src/plugin/lifecycle/PluginsProjection.res +16 -5
- package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
- package/src/util/CommandPublisher.res +9 -2
- package/src/util/CsvStream.res +3 -3
- package/src/util/Interstack.res +7 -15
- package/src/util/Interstack.resi +3 -3
- package/src/util/LogFormat.res +5 -9
- package/src/util/Logger.res +1 -2
- package/src/util/Lru.res +2 -1
- package/src/util/Lru.resi +2 -3
- package/src/util/ScheduleOps.res +24 -27
- package/src/util/StorePrefixCollision.res +6 -9
- package/src/util/Util_Promise.res +4 -1
- package/src/util/Util_Promise.res.mjs +1 -1
- package/tests/IdentityTest.res +166 -113
- package/tests/ResourceAttributionTest.res +28 -25
- package/tests/StorePrefixCollisionTest.res +2 -1
- package/tests/TaskBucketNamingTest.res +3 -2
- package/tests/adapter/EventLogProvisioningTest.res +51 -48
- package/tests/adapter/MonitoringTest.res +81 -38
- package/tests/adapter/QueryInterceptionTest.res +2 -2
- package/tests/adapter/RefusalVocabularyTest.res +31 -23
- package/tests/adapter/RuntimeExtensionHookReachTest.res +9 -8
- package/tests/adapter/RuntimeExtensionTest.res +5 -3
- package/tests/admin/AdminApiSchemaDriftTest.res +22 -16
- package/tests/admin/Platform_Admin_StructureTest.res +10 -11
- package/tests/admin/Platform_BakedManifestTest.res +31 -35
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +18 -15
- package/tests/admin/Platform_PluginStructuresApiTest.res +5 -3
- package/tests/admin/Platform_UIFragmentsApiTest.res +6 -3
- package/tests/aggregate/AggregateCacheTest.res +10 -12
- package/tests/aggregate/AggregateCallbackTest.res +75 -46
- package/tests/aggregate/AggregateCausationTest.res +19 -20
- package/tests/aggregate/AggregateConflictTest.res +41 -19
- package/tests/aggregate/AggregateFixtures.res +15 -5
- package/tests/aggregate/AggregateRejectionTest.res +97 -80
- package/tests/aggregate/AggregateSnapshotTest.res +44 -16
- package/tests/api/Api_IdsTest.res +10 -4
- package/tests/api/BrandedMarkerCompositionTest.res +48 -36
- package/tests/api/GeolocationSemanticTest.res +3 -3
- package/tests/api/GraphQL_FragmentGeneratorTest.res +66 -54
- package/tests/api/OwnerScopeDiagnosticsTest.res +31 -27
- package/tests/api/StorageRefFieldsTest.res +30 -18
- package/tests/api/SuryToJsonSchemaTest.res +1315 -1014
- package/tests/api/TaggedUnionSchemaTest.res +17 -24
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res +235 -179
- package/tests/commandgenerator/CommandGeneratorFixtures.res +8 -13
- package/tests/commandgenerator/OwnerStampingTest.res +91 -76
- package/tests/commandtopic/CommandOutcomeHookTest.res +198 -155
- package/tests/commandtopic/CommandTopicCallbackFixtures.res +5 -4
- package/tests/commandtopic/CommandTopicCallbackTest.res +61 -43
- package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +70 -55
- package/tests/counter/CounterCallbackTest.res +40 -34
- package/tests/counter/CounterFixtures.res +4 -4
- package/tests/dcb/DcbDecodeTest.res +115 -84
- package/tests/dcb/DcbEventLogOperationsTest.res +194 -143
- package/tests/dcb/DcbEventLogSchemaTest.res +13 -13
- package/tests/dcb/DcbFixtures.res +4 -5
- package/tests/dcb/DcbScopeInferenceTest.res +173 -114
- package/tests/dcb/DcbStateChangeSliceTest.res +391 -262
- package/tests/dcb/DcbTagTest.res +22 -11
- package/tests/dcb/DcbValidationTest.res +288 -218
- package/tests/eventlog/EventLogFixtures.res +4 -4
- package/tests/eventlog/EventLogOperationsTest.res +102 -67
- package/tests/eventlog/EventLogRetryTest.res +104 -65
- package/tests/eventmapper/EventMapperCallbackTest.res +118 -83
- package/tests/eventmapper/EventMapperFixtures.res +15 -23
- package/tests/eventmapper/EventMapperTest.res +54 -38
- package/tests/eventtopic/EventTopicFixtures.res +3 -4
- package/tests/eventtopic/EventTopicOperationsTest.res +41 -29
- package/tests/extensionpoint/ExtensionPointCallbackTest.res +58 -45
- package/tests/extensionpoint/ExtensionPointFixtures.res +16 -20
- package/tests/extensionpoint/ExtensionPointOperationsTest.res +70 -49
- package/tests/extensionpoint/PluginDisconnectScheduleTest.res +2 -1
- package/tests/logger/LogFormatTest.res +104 -82
- package/tests/logger/LoggerLazyTest.res +21 -12
- package/tests/message/MessageTest.res +96 -93
- package/tests/message/MetaEnvelopeTest.res +26 -30
- package/tests/plugin/HeartbeatDisconnectGraceTest.res +31 -25
- package/tests/plugin/ManifestVisibilityTest.res +20 -12
- package/tests/plugin/PluginDefinitionScalars.res +1 -2
- package/tests/plugin/PluginLifecycleCorpusTest.res +15 -12
- package/tests/plugin/PluginSpecExperiment.res +0 -1
- package/tests/plugin/PluginStructureTest.res +1529 -1045
- package/tests/plugin/Plugin_EventQuerySchemaTest.res +21 -10
- package/tests/plugin/SchemaWalkerTest.res +116 -70
- package/tests/plugin/StateChange/PsReserveStock.res +4 -4
- package/tests/plugin/StateView/PsAnnotatedView.res +4 -4
- package/tests/plugin/StateView/PsCustomersView.res +4 -4
- package/tests/plugin/StateView/PsShipmentsView.res +3 -2
- package/tests/projection/LifecycleTrailTest.res +1 -4
- package/tests/projection/ProjectionOptimizeTest.res +13 -9
- package/tests/querydb/QueryDbFixtures.res +0 -1
- package/tests/querydb/QueryDbOperationsTest.res +99 -64
- package/tests/querydb/TaggedUnionStampTest.res +10 -8
- package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res +50 -35
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +4 -10
- package/tests/util/CommandPublisherTest.res +0 -1
- package/tests/util/CsvStreamTest.res +3 -3
- package/tests/util/LruTest.res +84 -56
|
@@ -69,43 +69,47 @@ module Make = (
|
|
|
69
69
|
//
|
|
70
70
|
// `~seq` is the replayed sequence number the batch decides from, carried in so
|
|
71
71
|
// the line below can name the state rather than serialise it.
|
|
72
|
-
let processCommand = (~seq: int) =>
|
|
73
|
-
(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
| Ok(generatedEvents) =>
|
|
91
|
-
let newState = generatedEvents->Array.reduce(state, Behavior.evolve)
|
|
92
|
-
(newState, Array.concat(outcomes, [(reference, CmdOk(generatedEvents), meta)]))
|
|
93
|
-
| Error(error) =>
|
|
94
|
-
let errorJson = error->Message.encode(Spec.errorSchema)
|
|
95
|
-
let errorCode = errorJson->Message.variantNameOfJson
|
|
96
|
-
// Strip the TAG so detail carries only the rejection payload (empty for unit errors).
|
|
97
|
-
let (_, payloadDict) = errorJson->Message.splitMessage
|
|
98
|
-
let errorDetail =
|
|
99
|
-
payloadDict->Dict.toArray->Array.length == 0
|
|
100
|
-
? ""
|
|
101
|
-
: payloadDict->JSON.Encode.object->JSON.stringify
|
|
102
|
-
EffectLogger.logError(
|
|
72
|
+
let processCommand = (~seq: int) =>
|
|
73
|
+
(
|
|
74
|
+
(state, outcomes): (Behavior.state, array<(string, cmdOutcome, Message.meta)>),
|
|
75
|
+
topicItem: CommandTopic.topicItem<Message.command'<Spec.Id.t, Spec.command>>,
|
|
76
|
+
) => {
|
|
77
|
+
let {reference, command: command'} = topicItem
|
|
78
|
+
let meta = command'->updateMeta
|
|
79
|
+
let id = command'.id->Spec.Id.toString
|
|
80
|
+
// Identity, not content: the folded state grows with the entity's whole
|
|
81
|
+
// history, so serialising it here makes the line's size a function of how
|
|
82
|
+
// long the system has been in use. The state at decision time is a
|
|
83
|
+
// debugger's subject, not a log's.
|
|
84
|
+
let cmdName =
|
|
85
|
+
command'.command
|
|
86
|
+
->Message.encode(Spec.commandSchema)
|
|
87
|
+
->Message.variantNameOfJson
|
|
88
|
+
->LogFormat.bold
|
|
89
|
+
EffectLogger.logDebug(
|
|
103
90
|
~comp,
|
|
104
|
-
`
|
|
91
|
+
`deciding: id=${id} seq=${seq->Int.toString} cmd=${cmdName}`,
|
|
105
92
|
)->Effect.runSync
|
|
106
|
-
(state,
|
|
93
|
+
switch Behavior.decide(state, command'.command) {
|
|
94
|
+
| Ok(generatedEvents) =>
|
|
95
|
+
let newState = generatedEvents->Array.reduce(state, Behavior.evolve)
|
|
96
|
+
(newState, Array.concat(outcomes, [(reference, CmdOk(generatedEvents), meta)]))
|
|
97
|
+
| Error(error) =>
|
|
98
|
+
let errorJson = error->Message.encode(Spec.errorSchema)
|
|
99
|
+
let errorCode = errorJson->Message.variantNameOfJson
|
|
100
|
+
// Strip the TAG so detail carries only the rejection payload (empty for unit errors).
|
|
101
|
+
let (_, payloadDict) = errorJson->Message.splitMessage
|
|
102
|
+
let errorDetail =
|
|
103
|
+
payloadDict->Dict.toArray->Array.length == 0
|
|
104
|
+
? ""
|
|
105
|
+
: payloadDict->JSON.Encode.object->JSON.stringify
|
|
106
|
+
EffectLogger.logError(
|
|
107
|
+
~comp,
|
|
108
|
+
`decide rejected: ${errorCode} ${errorDetail} id=${id}`,
|
|
109
|
+
)->Effect.runSync
|
|
110
|
+
(state, Array.concat(outcomes, [(reference, CmdRejected({errorCode, errorDetail}), meta)]))
|
|
111
|
+
}
|
|
107
112
|
}
|
|
108
|
-
}
|
|
109
113
|
|
|
110
114
|
let maxConflictRetries = 3
|
|
111
115
|
|
|
@@ -125,9 +129,7 @@ module Make = (
|
|
|
125
129
|
// the fixed capacity (a per-aggregate knob is a future refinement — see
|
|
126
130
|
// docs/plans/done/aggregate-snapshotting.md).
|
|
127
131
|
let replayCacheCapacity = 100
|
|
128
|
-
let replayCache: Lru.t<string, (Behavior.state, int)> = Lru.make(
|
|
129
|
-
~capacity=replayCacheCapacity,
|
|
130
|
-
)
|
|
132
|
+
let replayCache: Lru.t<string, (Behavior.state, int)> = Lru.make(~capacity=replayCacheCapacity)
|
|
131
133
|
|
|
132
134
|
let resetCache = () => replayCache->Lru.clear
|
|
133
135
|
|
|
@@ -188,8 +190,7 @@ module Make = (
|
|
|
188
190
|
let seedEffect = switch snapshotConfig {
|
|
189
191
|
| None => Effect.succeed((Behavior.initialState, 0))
|
|
190
192
|
| Some(_) =>
|
|
191
|
-
Effect.promise(() => Ops.eventLog.latestSnapshot(id))
|
|
192
|
-
->Effect.map(snapResult =>
|
|
193
|
+
Effect.promise(() => Ops.eventLog.latestSnapshot(id))->Effect.map(snapResult =>
|
|
193
194
|
switch snapResult {
|
|
194
195
|
| Ok(Some(snap)) if snap.EventLog.schemaHash == stateSchemaHash =>
|
|
195
196
|
switch decodeState(snap.state) {
|
|
@@ -269,7 +270,11 @@ module Make = (
|
|
|
269
270
|
| Some({interval}) if interval > 0 && newSeq / interval > oldSeq / interval =>
|
|
270
271
|
switch encodeState(state) {
|
|
271
272
|
| Some(stateJson) =>
|
|
272
|
-
fireSnapshotWrite(
|
|
273
|
+
fireSnapshotWrite(
|
|
274
|
+
id,
|
|
275
|
+
idStr,
|
|
276
|
+
{EventLog.seqNr: newSeq, state: stateJson, schemaHash: stateSchemaHash},
|
|
277
|
+
)
|
|
273
278
|
| None =>
|
|
274
279
|
EffectLogger.logWarn(
|
|
275
280
|
~comp,
|
|
@@ -342,8 +347,7 @@ module Make = (
|
|
|
342
347
|
| None => coldReadState(id)
|
|
343
348
|
}
|
|
344
349
|
|
|
345
|
-
readState
|
|
346
|
-
->Effect.flatMap(((initialState, sequenceNr)) => {
|
|
350
|
+
readState->Effect.flatMap(((initialState, sequenceNr)) => {
|
|
347
351
|
let (finalState, outcomes) =
|
|
348
352
|
topicItemsForId->Array.reduce((initialState, []), processCommand(~seq=sequenceNr))
|
|
349
353
|
|
|
@@ -379,51 +383,52 @@ module Make = (
|
|
|
379
383
|
~detail=eventJsons,
|
|
380
384
|
`produced ${eventCount} event(s): [${eventDetails}]`,
|
|
381
385
|
)
|
|
382
|
-
->Effect.flatMap(
|
|
383
|
-
|
|
386
|
+
->Effect.flatMap(_ =>
|
|
387
|
+
Effect.promise(() => Ops.eventLog.append(sequenceNr, id, generatedEvents'))
|
|
384
388
|
)
|
|
385
|
-
->Effect.flatMap(
|
|
386
|
-
appendResult
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
389
|
+
->Effect.flatMap(appendResult =>
|
|
390
|
+
switch appendResult {
|
|
391
|
+
| Ok(_) =>
|
|
392
|
+
// The post-decide fold state IS the post-append replay result;
|
|
393
|
+
// cache it so the next command for this id skips the replay.
|
|
394
|
+
let newSeq = sequenceNr + generatedEvents'->Array.length
|
|
395
|
+
replayCache->Lru.put(idStr, (finalState, newSeq))
|
|
396
|
+
// Persist a snapshot if this append crossed an interval boundary
|
|
397
|
+
// (fire-and-forget — never blocks or fails the command).
|
|
398
|
+
maybeWriteSnapshot(id, idStr, ~oldSeq=sequenceNr, ~newSeq, finalState)
|
|
399
|
+
let perRef = reportFinalOutcomes(
|
|
400
|
+
outcomes,
|
|
401
|
+
~entityId=idStr,
|
|
402
|
+
~appendSucceeded=true,
|
|
403
|
+
~appendedEventCount=generatedEvents'->Array.length,
|
|
404
|
+
)
|
|
405
|
+
EffectLogger.logInfo(~comp, `append: id=${idStr}`)->Effect.map(_ => Ok(perRef))
|
|
406
|
+
| Error(EventLog.Conflict) =>
|
|
407
|
+
// Another writer advanced the stream past our (possibly cached)
|
|
408
|
+
// sequenceNr — drop the entry so the retry replays cold.
|
|
409
|
+
replayCache->Lru.invalidate(idStr)
|
|
410
|
+
// Signal the outer replay+re-decide retry loop (which matches
|
|
411
|
+
// Error(_)); the string is an internal marker, no longer a
|
|
412
|
+
// cross-component substring sentinel.
|
|
413
|
+
EffectLogger.logWarn(~comp, `conflict: id=${idStr}, will retry`)->Effect.map(
|
|
414
|
+
_ => Error("conflict"),
|
|
415
|
+
)
|
|
416
|
+
| Error(EventLog.StorageFailure(msg)) =>
|
|
417
|
+
// Appends are atomic, so the read snapshot is likely still valid —
|
|
418
|
+
// but a storage error means we can't be sure what committed;
|
|
419
|
+
// drop the entry so the next attempt reads authoritative state.
|
|
420
|
+
replayCache->Lru.invalidate(idStr)
|
|
421
|
+
let perRef = reportFinalOutcomes(
|
|
422
|
+
outcomes,
|
|
423
|
+
~entityId=idStr,
|
|
424
|
+
~appendSucceeded=false,
|
|
425
|
+
~appendedEventCount=0,
|
|
426
|
+
~appendErrorDetail=msg,
|
|
427
|
+
)
|
|
428
|
+
EffectLogger.logError(~comp, `append failed: id=${idStr}: ${msg}`)->Effect.map(
|
|
429
|
+
_ => Ok(perRef),
|
|
430
|
+
)
|
|
431
|
+
}
|
|
427
432
|
)
|
|
428
433
|
}
|
|
429
434
|
})
|
|
@@ -10,9 +10,7 @@ let excludedByMembers = (commandSchema: S.t<unknown>): array<string> =>
|
|
|
10
10
|
switch commandSchema {
|
|
11
11
|
| AnyOf({anyOf}) =>
|
|
12
12
|
anyOf->Array.filterMap(member =>
|
|
13
|
-
member->S.Metadata.get(~id=noApiVariantId)->Option.getOr(false)
|
|
14
|
-
? variantNameOf(member)
|
|
15
|
-
: None
|
|
13
|
+
member->S.Metadata.get(~id=noApiVariantId)->Option.getOr(false) ? variantNameOf(member) : None
|
|
16
14
|
)
|
|
17
15
|
| _ => []
|
|
18
16
|
}
|
|
@@ -12,7 +12,6 @@ door applies it, and so provider adapters share one definition rather than three
|
|
|
12
12
|
Provider-neutral on purpose: the contract belongs to neither the local platform
|
|
13
13
|
nor AWS.
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
15
|
@val external btoa: string => string = "btoa"
|
|
17
16
|
@val external atob: string => string = "atob"
|
|
18
17
|
|
|
@@ -12,9 +12,15 @@ type queryNames = {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
let pluralize = (n: string) =>
|
|
15
|
-
if RegExp.test(
|
|
15
|
+
if RegExp.test(/[^aeiou]y$/i, n) {
|
|
16
16
|
n->String.slice(~start=0, ~end=String.length(n) - 1) ++ "ies"
|
|
17
|
-
} else if
|
|
17
|
+
} else if (
|
|
18
|
+
n->String.endsWith("s") ||
|
|
19
|
+
n->String.endsWith("x") ||
|
|
20
|
+
n->String.endsWith("z") ||
|
|
21
|
+
n->String.endsWith("ch") ||
|
|
22
|
+
n->String.endsWith("sh")
|
|
23
|
+
) {
|
|
18
24
|
n ++ "es"
|
|
19
25
|
} else {
|
|
20
26
|
n ++ "s"
|
|
@@ -23,7 +29,13 @@ let pluralize = (n: string) =>
|
|
|
23
29
|
let singularize = (n: string) =>
|
|
24
30
|
if n->String.endsWith("ies") {
|
|
25
31
|
n->String.slice(~start=0, ~end=n->String.length - 3) ++ "y"
|
|
26
|
-
} else if
|
|
32
|
+
} else if (
|
|
33
|
+
n->String.endsWith("ses") ||
|
|
34
|
+
n->String.endsWith("xes") ||
|
|
35
|
+
n->String.endsWith("zes") ||
|
|
36
|
+
n->String.endsWith("ches") ||
|
|
37
|
+
n->String.endsWith("shes")
|
|
38
|
+
) {
|
|
27
39
|
n->String.slice(~start=0, ~end=n->String.length - 2)
|
|
28
40
|
} else if n->String.endsWith("s") {
|
|
29
41
|
n->String.slice(~start=0, ~end=n->String.length - 1)
|
|
@@ -76,16 +88,17 @@ let assertSubscriptionNameFits = (~fieldName: string) => {
|
|
|
76
88
|
// expect that shape (not aggregate-style `${plugin}_${slice}_${command}`). @noApi
|
|
77
89
|
// variants are filtered out. Every emitted name is guarded against the 50-char AppSync
|
|
78
90
|
// subscription cap at build time. Returns [(fieldName, constructorName)] in declaration order.
|
|
79
|
-
let sliceMutationFields = (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
): array<(string, string)> => {
|
|
91
|
+
let sliceMutationFields = (~plugin: string, ~slice: string, ~commandSchema: S.t<unknown>): array<(
|
|
92
|
+
string,
|
|
93
|
+
string,
|
|
94
|
+
)> => {
|
|
84
95
|
let allNames = Reventless.DcbTag.extractAllVariantNames(commandSchema->Obj.magic)
|
|
85
96
|
let filtered = ApiNoApiHelpers.filterNoApiVariants(allNames, commandSchema)
|
|
86
97
|
switch filtered {
|
|
87
98
|
| [] => []
|
|
88
|
-
| [single] => [
|
|
99
|
+
| [single] => [
|
|
100
|
+
(assertSubscriptionNameFits(~fieldName=sliceMutationField(~plugin, ~slice)), single),
|
|
101
|
+
]
|
|
89
102
|
| _ =>
|
|
90
103
|
filtered->Array.map(ctor => (
|
|
91
104
|
assertSubscriptionNameFits(~fieldName=dcbCommandMutationField(~plugin, ~command=ctor)),
|
|
@@ -115,7 +128,11 @@ let sliceMutationFieldFor = (
|
|
|
115
128
|
// "ProductDemand" the singularize is a no-op and pluralize behaves as usual.
|
|
116
129
|
let canonicalPlural = (n: string) => pluralize(singularize(n))
|
|
117
130
|
|
|
118
|
-
let queryFieldNamesForReadModel = (
|
|
131
|
+
let queryFieldNamesForReadModel = (
|
|
132
|
+
~plugin: string,
|
|
133
|
+
~name: string,
|
|
134
|
+
~connectionSpec: bool=true,
|
|
135
|
+
): queryNames => {
|
|
119
136
|
let singular = singularize(name)
|
|
120
137
|
let plural = canonicalPlural(name)
|
|
121
138
|
{
|
|
@@ -144,18 +161,16 @@ returning a type the plugin does not define fails the merge — and on AWS the
|
|
|
144
161
|
target table's `dynamodb:*` grant is attached to the *declaring* plugin's API
|
|
145
162
|
role, so even a schema that merged would be refused at read time.
|
|
146
163
|
*/
|
|
147
|
-
let resolvedFieldsOfConfig = (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
): array<ReventlessInfra.Api.resolvedFieldEntry> => {
|
|
164
|
+
let resolvedFieldsOfConfig = (~plugin: string, config: Reventless.ReadModel.config): array<
|
|
165
|
+
ReventlessInfra.Api.resolvedFieldEntry,
|
|
166
|
+
> => {
|
|
151
167
|
let assertSamePlugin = (~pluginName: option<string>, ~tableName: string, ~fieldName: string) =>
|
|
152
168
|
switch pluginName {
|
|
153
169
|
| Some(p) if p != plugin =>
|
|
154
170
|
JsError.throwWithMessage(
|
|
155
171
|
`Field \`${fieldName}\` resolves to "${tableName}" in plugin "${p}", but cross-plugin ` ++
|
|
156
172
|
`resolvers are not supported: a plugin's GraphQL document must be valid standalone, and ` ++
|
|
157
|
-
`"${p}"'s table is not readable by "${plugin}"'s API role. Query the other view directly, ` ++
|
|
158
|
-
`or project the fields you need into this one.`,
|
|
173
|
+
`"${p}"'s table is not readable by "${plugin}"'s API role. Query the other view directly, ` ++ `or project the fields you need into this one.`,
|
|
159
174
|
)
|
|
160
175
|
| _ => ()
|
|
161
176
|
}
|
|
@@ -186,7 +201,11 @@ let resolvedFieldsOfConfig = (
|
|
|
186
201
|
Array.concat(single, many)
|
|
187
202
|
}
|
|
188
203
|
|
|
189
|
-
let queryFieldNamesForStateView = (
|
|
204
|
+
let queryFieldNamesForStateView = (
|
|
205
|
+
~plugin: string,
|
|
206
|
+
~viewName: string,
|
|
207
|
+
~connectionSpec: bool=true,
|
|
208
|
+
): queryNames => {
|
|
190
209
|
let entity = stripViewSuffix(viewName)
|
|
191
210
|
let singular = singularize(entity)
|
|
192
211
|
let plural = canonicalPlural(entity)
|
|
@@ -206,7 +225,11 @@ let queryFieldNamesForStateView = (~plugin: string, ~viewName: string, ~connecti
|
|
|
206
225
|
// the resolver path (`GetItem` instead of `Scan`), and the `*ByIds` batch
|
|
207
226
|
// resolver all line up. StateViewSlice uses `queryFieldNamesForStateView`
|
|
208
227
|
// instead and keeps its own (id-free) shape.
|
|
209
|
-
let queryFieldNamesForSliceQueryDb = (
|
|
228
|
+
let queryFieldNamesForSliceQueryDb = (
|
|
229
|
+
~plugin: string,
|
|
230
|
+
~queryDbName: string,
|
|
231
|
+
~connectionSpec: bool=true,
|
|
232
|
+
): queryNames => {
|
|
210
233
|
{
|
|
211
234
|
singleFieldName: `${plugin}_${queryDbName}`,
|
|
212
235
|
listFieldName: `${plugin}_${canonicalPlural(queryDbName)}`,
|
|
@@ -237,7 +237,9 @@ let deriveObjectTypeWithNested = (
|
|
|
237
237
|
// — for every plugin in the merge, not just this one. Refuse at build time,
|
|
238
238
|
// naming both halves.
|
|
239
239
|
resolvedFields->Array.forEach(({fieldName}) =>
|
|
240
|
-
if
|
|
240
|
+
if (
|
|
241
|
+
filteredFields->Dict.get(fieldName)->Option.isSome || (includeIdParam && fieldName == "id")
|
|
242
|
+
) {
|
|
241
243
|
JsError.throwWithMessage(
|
|
242
244
|
`${typeName}.${fieldName} is declared by @resolves/@resolvesMany and is already a field of the state record. Name the resolved field something the state does not use.`,
|
|
243
245
|
)
|
|
@@ -303,16 +305,15 @@ let rec scalarOfSchemaType = (st: SchemaType.schemaType): string =>
|
|
|
303
305
|
// `SchemaType.isIdFieldName`, which lowercases before testing the suffix and so
|
|
304
306
|
// accepts `paid` and `valid` — harmless where it is used, but here it would
|
|
305
307
|
// nominate an ordinary word as a row's key.
|
|
306
|
-
let isKeyFieldName = (name: string): bool =>
|
|
307
|
-
name->String.length > 2 && name->String.endsWith("Id")
|
|
308
|
+
let isKeyFieldName = (name: string): bool => name->String.length > 2 && name->String.endsWith("Id")
|
|
308
309
|
|
|
309
310
|
/** What the ladder below concluded. The two ways to have no key are opposite
|
|
310
311
|
mistakes, so they are kept apart rather than collapsed into `None`. */
|
|
311
312
|
type keyFieldResolution =
|
|
312
313
|
| Resolved({field: string, rung: string})
|
|
313
|
-
|
|
314
|
+
/** Several `*Id` fields and none matching the name — usually a field
|
|
314
315
|
somebody added to a view that used to have exactly one. */
|
|
315
|
-
Ambiguous({candidates: array<string>, conventional: string})
|
|
316
|
+
| Ambiguous({candidates: array<string>, conventional: string})
|
|
316
317
|
| NoCandidate
|
|
317
318
|
|
|
318
319
|
/**
|
|
@@ -384,8 +385,7 @@ let keyFieldGapMessage = (resolution: keyFieldResolution): option<string> =>
|
|
|
384
385
|
`has no row key: it declares no @id and its \`*Id\` fields ` ++
|
|
385
386
|
`(${candidates->Array.join(", ")}) include no "${conventional}" for the name to ` ++
|
|
386
387
|
`pick. Adding a second \`*Id\` field to a view that had one is what lands here, ` ++
|
|
387
|
-
`and it costs the view its filter and its whole orderBy in the schema. Declare ` ++
|
|
388
|
-
`@id on the field that identifies a row.`,
|
|
388
|
+
`and it costs the view its filter and its whole orderBy in the schema. Declare ` ++ `@id on the field that identifies a row.`,
|
|
389
389
|
)
|
|
390
390
|
}
|
|
391
391
|
|
|
@@ -497,15 +497,14 @@ let deriveConnectionFilterType = (
|
|
|
497
497
|
~capability: serverCapability=emptyCapability,
|
|
498
498
|
): string => {
|
|
499
499
|
let baseFields = ["search: String", "searchPrefix: String", "ids: [ID!]"]
|
|
500
|
-
let perFieldFilters =
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
})
|
|
500
|
+
let perFieldFilters = capability.filterFields->Array.flatMap(f => {
|
|
501
|
+
let eq = `${f.name}Eq: ${f.gqlType}`
|
|
502
|
+
if f.range {
|
|
503
|
+
[eq, `${f.name}From: ${f.gqlType}`, `${f.name}To: ${f.gqlType}`]
|
|
504
|
+
} else {
|
|
505
|
+
[eq]
|
|
506
|
+
}
|
|
507
|
+
})
|
|
509
508
|
let allFields = Array.concat(baseFields, perFieldFilters)
|
|
510
509
|
let body = allFields->Array.map(f => ` ${f}`)->Array.join("\n")
|
|
511
510
|
`input ${filterTypeName} {\n${body}\n}`
|
|
@@ -514,10 +513,9 @@ let deriveConnectionFilterType = (
|
|
|
514
513
|
// Emits an `enum <Type>OrderField` and `input <Type>OrderBy` pair when the
|
|
515
514
|
// capability has any sort fields. Returns [] when no field is sortable so
|
|
516
515
|
// the connection field doesn't reference a non-existent OrderBy type.
|
|
517
|
-
let deriveConnectionOrderByType = (
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
): array<string> =>
|
|
516
|
+
let deriveConnectionOrderByType = (~singularTypeName: string, ~capability: serverCapability): array<
|
|
517
|
+
string,
|
|
518
|
+
> =>
|
|
521
519
|
if capability.sortFields->Array.length == 0 {
|
|
522
520
|
[]
|
|
523
521
|
} else {
|
|
@@ -561,10 +559,7 @@ let deriveObjectQueryField = (
|
|
|
561
559
|
` ${singleFieldName}: ${typeName}`
|
|
562
560
|
}
|
|
563
561
|
|
|
564
|
-
let deriveListQueryField = (
|
|
565
|
-
~listFieldName: string,
|
|
566
|
-
~pluralTypeName: string,
|
|
567
|
-
): string =>
|
|
562
|
+
let deriveListQueryField = (~listFieldName: string, ~pluralTypeName: string): string =>
|
|
568
563
|
` ${listFieldName}(nextToken: String, limit: Int): ${pluralTypeName}!`
|
|
569
564
|
|
|
570
565
|
// Batched-by-ids query: fetches multiple entities in a single BatchGetItem.
|
|
@@ -572,10 +567,7 @@ let deriveListQueryField = (
|
|
|
572
567
|
// caller correlates by `id` on each returned item. Single-key tables only —
|
|
573
568
|
// composite-key BatchGetItem requires both pk + sk per key entry, which is
|
|
574
569
|
// out of scope for this field.
|
|
575
|
-
let deriveByIdsQueryField = (
|
|
576
|
-
~listFieldName: string,
|
|
577
|
-
~returnTypeName: string,
|
|
578
|
-
): string =>
|
|
570
|
+
let deriveByIdsQueryField = (~listFieldName: string, ~returnTypeName: string): string =>
|
|
579
571
|
` ${listFieldName}ByIds(ids: [String!]!, includeRetired: Boolean): [${returnTypeName}!]!`
|
|
580
572
|
|
|
581
573
|
// ── The reference door ─────────────────────────────────────────────────────
|
|
@@ -614,10 +606,8 @@ let deriveRefTypeSdl = (~returnTypeName: string): string =>
|
|
|
614
606
|
// `retiredState` is null in two cases that do not need telling apart by a
|
|
615
607
|
// consumer: a live row, and a boolean-form retirement, where the field is the
|
|
616
608
|
// state and `retired: true` has already said everything there is to say.
|
|
617
|
-
let deriveRefsQueryField = (
|
|
618
|
-
|
|
619
|
-
~returnTypeName: string,
|
|
620
|
-
): string => ` ${listFieldName}Refs(ids: [ID!]!): [${returnTypeName}Ref!]!`
|
|
609
|
+
let deriveRefsQueryField = (~listFieldName: string, ~returnTypeName: string): string =>
|
|
610
|
+
` ${listFieldName}Refs(ids: [ID!]!): [${returnTypeName}Ref!]!`
|
|
621
611
|
|
|
622
612
|
// `includeRetired` sits beside the paging arguments rather than inside `filter`,
|
|
623
613
|
// and that placement is the point. `filter` is the caller's description of the
|
|
@@ -751,10 +741,7 @@ let mutationArgTypes = (~fieldName: string, variantSchema: S.t<unknown>): option
|
|
|
751
741
|
fields
|
|
752
742
|
->Dict.toArray
|
|
753
743
|
->Array.forEach(((argName, argType)) =>
|
|
754
|
-
out->Dict.set(
|
|
755
|
-
argName,
|
|
756
|
-
fromSchemaType(~required=true, ~asInput=true, argType, [], Set.make()),
|
|
757
|
-
)
|
|
744
|
+
out->Dict.set(argName, fromSchemaType(~required=true, ~asInput=true, argType, [], Set.make()))
|
|
758
745
|
)
|
|
759
746
|
out
|
|
760
747
|
})
|
|
@@ -774,8 +761,10 @@ let generate = (
|
|
|
774
761
|
// `Plugin_Activate` / `Platform_Plugin_Activate` — always the last
|
|
775
762
|
// underscore-separated segment (e.g. `Activate`).
|
|
776
763
|
let constructorNameOf = (fieldName: string): string =>
|
|
777
|
-
fieldName
|
|
778
|
-
|
|
764
|
+
fieldName
|
|
765
|
+
->String.split("_")
|
|
766
|
+
->Array.get(fieldName->String.split("_")->Array.length - 1)
|
|
767
|
+
->Option.getOr(fieldName)
|
|
779
768
|
|
|
780
769
|
mutationEntries->Array.forEach(entry => {
|
|
781
770
|
let schema = entry.commandSchema
|
|
@@ -803,9 +792,8 @@ let generate = (
|
|
|
803
792
|
entry.fieldNames->Array.forEach(fieldName => {
|
|
804
793
|
let cname = constructorNameOf(fieldName)
|
|
805
794
|
let variantIndex = variantNames->Array.indexOf(cname)
|
|
806
|
-
let variantSchema =
|
|
807
|
-
? anyOf->Array.get(variantIndex)->Option.getOr(schema)
|
|
808
|
-
: schema
|
|
795
|
+
let variantSchema =
|
|
796
|
+
variantIndex >= 0 ? anyOf->Array.get(variantIndex)->Option.getOr(schema) : schema
|
|
809
797
|
switch deriveMutationFieldFromObject(
|
|
810
798
|
~fieldName,
|
|
811
799
|
~collectedTypes=types,
|
|
@@ -838,8 +826,7 @@ let generate = (
|
|
|
838
826
|
~collectedTypes=types,
|
|
839
827
|
~seenTypes,
|
|
840
828
|
schema,
|
|
841
|
-
)
|
|
842
|
-
->Option.forEach(field => mutations->Array.push(field))
|
|
829
|
+
)->Option.forEach(field => mutations->Array.push(field))
|
|
843
830
|
}
|
|
844
831
|
| _ => ()
|
|
845
832
|
}
|
|
@@ -984,8 +971,9 @@ let generate = (
|
|
|
984
971
|
let edgeName = entry.returnTypeName ++ "Edge"
|
|
985
972
|
seenTypes->Set.add(edgeName)
|
|
986
973
|
seenTypes->Set.add(connectionTypeName)
|
|
987
|
-
deriveConnectionTypes(~singularTypeName=entry.returnTypeName)
|
|
988
|
-
|
|
974
|
+
deriveConnectionTypes(~singularTypeName=entry.returnTypeName)->Array.forEach(t =>
|
|
975
|
+
types->Array.push(t)
|
|
976
|
+
)
|
|
989
977
|
}
|
|
990
978
|
let capability = deriveServerCapability(~entityName=entityNameOf(entry), entry.stateSchema)
|
|
991
979
|
let connectionFilterTypeName = entry.returnTypeName ++ "Filter"
|
|
@@ -995,8 +983,10 @@ let generate = (
|
|
|
995
983
|
deriveConnectionFilterType(~filterTypeName=connectionFilterTypeName, ~capability),
|
|
996
984
|
)
|
|
997
985
|
}
|
|
998
|
-
let orderByTypes =
|
|
999
|
-
|
|
986
|
+
let orderByTypes = deriveConnectionOrderByType(
|
|
987
|
+
~singularTypeName=entry.returnTypeName,
|
|
988
|
+
~capability,
|
|
989
|
+
)
|
|
1000
990
|
let hasOrderBy = orderByTypes->Array.length > 0
|
|
1001
991
|
if hasOrderBy {
|
|
1002
992
|
let orderFieldEnumName = entry.returnTypeName ++ "OrderField"
|
|
@@ -1024,10 +1014,7 @@ let generate = (
|
|
|
1024
1014
|
)
|
|
1025
1015
|
types->Array.push(pluralType)
|
|
1026
1016
|
}
|
|
1027
|
-
let listField = deriveListQueryField(
|
|
1028
|
-
~listFieldName,
|
|
1029
|
-
~pluralTypeName=listFieldName,
|
|
1030
|
-
)
|
|
1017
|
+
let listField = deriveListQueryField(~listFieldName, ~pluralTypeName=listFieldName)
|
|
1031
1018
|
queries->Array.push(listField)
|
|
1032
1019
|
}
|
|
1033
1020
|
})
|
|
@@ -19,7 +19,10 @@ let inspectScalar = (schema: S.t<'a>): string => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
let inspectObjectType = (~typeName: string, schema: S.t<'a>): option<string> => {
|
|
22
|
-
let types = GraphQL_FragmentGenerator.deriveObjectTypeWithNested(
|
|
22
|
+
let types = GraphQL_FragmentGenerator.deriveObjectTypeWithNested(
|
|
23
|
+
~typeName,
|
|
24
|
+
schema->S.castToUnknown,
|
|
25
|
+
)
|
|
23
26
|
types->Array.get(types->Array.length - 1)
|
|
24
27
|
}
|
|
25
28
|
|
|
@@ -103,8 +106,7 @@ let inspectFragment = (fragment: Reventless.Plugin.apiSchemaFragment): fragmentI
|
|
|
103
106
|
| true => `type Mutation {\n${parts.mutations->Array.join("\n")}\n}`
|
|
104
107
|
| false => ""
|
|
105
108
|
}
|
|
106
|
-
let sdlParts =
|
|
107
|
-
[typesSdl, queriesSdl, mutationsSdl]->Array.filter(p => p->String.length > 0)
|
|
109
|
+
let sdlParts = [typesSdl, queriesSdl, mutationsSdl]->Array.filter(p => p->String.length > 0)
|
|
108
110
|
let sdlPreview = sdlParts->Array.join("\n\n")
|
|
109
111
|
{types: parts.types, mutations: parts.mutations, queries: parts.queries, sdlPreview}
|
|
110
112
|
}
|
|
@@ -73,11 +73,12 @@ let decode = (fragment: Reventless.Plugin.apiSchemaFragment): fragmentParts => {
|
|
|
73
73
|
let field = obj->Dict.get("field")->Option.flatMap(JSON.Decode.string)
|
|
74
74
|
let mutations = switch obj->Dict.get("mutations") {
|
|
75
75
|
| Some(JSON.Array(ms)) =>
|
|
76
|
-
ms->Array.filterMap(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
ms->Array.filterMap(
|
|
77
|
+
m =>
|
|
78
|
+
switch m {
|
|
79
|
+
| JSON.String(s) => Some(s)
|
|
80
|
+
| _ => None
|
|
81
|
+
},
|
|
81
82
|
)
|
|
82
83
|
| _ => []
|
|
83
84
|
}
|
|
@@ -169,9 +170,7 @@ let relayBaseTypes = [
|
|
|
169
170
|
`enum SortOrder {\n ASC\n DESC\n}`,
|
|
170
171
|
]
|
|
171
172
|
|
|
172
|
-
let relayBaseQueries = [
|
|
173
|
-
` node(id: ID!): Node`,
|
|
174
|
-
]
|
|
173
|
+
let relayBaseQueries = [` node(id: ID!): Node`]
|
|
175
174
|
|
|
176
175
|
/**
|
|
177
176
|
Assemble fragments into a complete GraphQL SDL string.
|
|
@@ -293,8 +292,8 @@ let assembleSdl = (
|
|
|
293
292
|
: None
|
|
294
293
|
|
|
295
294
|
let sdlParts =
|
|
296
|
-
[Some(typesSdl), Some(queriesSdl), Some(mutationsSdl), subscriptionsSdl]->Array.filterMap(
|
|
297
|
-
x
|
|
295
|
+
[Some(typesSdl), Some(queriesSdl), Some(mutationsSdl), subscriptionsSdl]->Array.filterMap(x =>
|
|
296
|
+
x
|
|
298
297
|
)
|
|
299
298
|
sdlParts->Array.join("\n\n")
|
|
300
299
|
}
|