Data Connectors update mutations support [GDC-713]

[GDC-713]: https://hasurahq.atlassian.net/browse/GDC-713?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7604
GitOrigin-RevId: e0d496b425bed48f2d53a66584f4c7ecd40b485e
This commit is contained in:
Daniel Chambers 2023-01-20 17:16:39 +11:00 committed by hasura-bot
parent ded69b361e
commit d6cbbe3e49
3 changed files with 620 additions and 15 deletions

View File

@ -104,6 +104,7 @@ library
Test.DataConnector.MockAgent.MetadataApiSpec
Test.DataConnector.MockAgent.QueryRelationshipsSpec
Test.DataConnector.MockAgent.TransformedConfigurationSpec
Test.DataConnector.MockAgent.UpdateMutationsSpec
Test.DataConnector.QuerySpec
Test.DataConnector.SelectPermissionsSpec
Test.EventTriggers.EventTriggersSpecialCharactersSpec

View File

@ -0,0 +1,501 @@
module Test.DataConnector.MockAgent.UpdateMutationsSpec
( spec,
)
where
import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentTest, mockMutationResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Language.GraphQL.Draft.Syntax.QQ qualified as G
import Test.Hspec
--------------------------------------------------------------------------------
spec :: SpecWith GlobalTestEnvironment
spec =
Fixture.runWithLocalTestEnvironment
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Mock.backendTypeMetadata)
{ Fixture.mkLocalTestEnvironment = Mock.mkLocalTestEnvironment,
Fixture.setupTeardown = \(testEnv, mockEnv) ->
[Mock.setupAction sourceMetadata Mock.agentConfig (testEnv, mockEnv)]
}
]
)
tests
--------------------------------------------------------------------------------
testRoleName :: ByteString
testRoleName = "test-role"
sourceMetadata :: Aeson.Value
sourceMetadata =
let source = BackendType.backendSourceName Mock.backendTypeMetadata
backendType = BackendType.backendTypeString Mock.backendTypeMetadata
in [yaml|
name : *source
kind: *backendType
tables:
- table: [Track]
object_relationships:
- name: Genre
using:
manual_configuration:
remote_table: [Genre]
column_mapping:
GenreId: GenreId
select_permissions:
- role: *testRoleName
permission:
columns:
- TrackId
- Name
- AlbumId
- MediaTypeId
- GenreId
- Composer
- Milliseconds
- Bytes
- UnitPrice
filter: {}
update_permissions:
- role: *testRoleName
permission:
filter:
AlbumId: "X-Hasura-AlbumId"
check:
UnitPrice:
_gt: 0
set:
AlbumId: "X-Hasura-AlbumId"
columns:
- Name
- AlbumId
- MediaTypeId
- GenreId
- Composer
- Milliseconds
- Bytes
- UnitPrice
- table: [Genre]
select_permissions:
- role: *testRoleName
permission:
columns:
- GenreId
- Name
filter: {}
configuration: {}
|]
--------------------------------------------------------------------------------
tests :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
tests _opts = do
mockAgentTest "update rows with update permissions" $ \performGraphqlRequest -> do
let headers = [("X-Hasura-AlbumId", "3"), ("X-Hasura-Role", testRoleName)]
let graphqlRequest =
[graphql|
mutation UpdateMutation {
update_Track(where: {GenreId: {_eq: 1}}, _set: {Name: "Another Name"}, _inc: {Milliseconds: 1000}) {
updatedRowCount: affected_rows
updatedRows: returning {
TrackId
Name
Genre {
Name
}
}
}
}
|]
let mockAgentResponse =
API.MutationResponse
[ API.MutationOperationResults
{ API._morAffectedRows = 3,
API._morReturning =
Just
[ HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
]
]
)
],
HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
]
]
)
],
HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
]
]
)
]
]
}
]
let mockConfig = Mock.chinookMock & mockMutationResponse mockAgentResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
update_Track:
updatedRowCount: 3
updatedRows:
- TrackId: 3
Name: Another Name
Genre:
Name: Rock
- TrackId: 4
Name: Another Name
Genre:
Name: Rock
- TrackId: 5
Name: Another Name
Genre:
Name: Rock
|]
let expectedRequest =
API.MutationRequest
{ API._mrTableRelationships =
[ API.TableRelationships
{ API._trSourceTable = API.TableName ("Track" :| []),
API._trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Genre",
API.Relationship
{ API._rTargetTable = API.TableName ("Genre" :| []),
API._rRelationshipType = API.ObjectRelationship,
API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
}
)
]
}
],
API._mrInsertSchema = [],
API._mrOperations =
[ API.UpdateOperation $
API.UpdateMutationOperation
{ API._umoTable = API.TableName ("Track" :| []),
API._umoUpdates =
[ API.SetColumn $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "Name",
API._rcovValue = Aeson.String "Another Name",
API._rcovValueType = API.ScalarType "string"
},
API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "Milliseconds",
API._rcovValue = Aeson.Number 1000,
API._rcovValueType = API.ScalarType "number"
},
API.SetColumn $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "AlbumId",
API._rcovValue = Aeson.Number 3,
API._rcovValueType = API.ScalarType "number"
}
],
API._umoWhere =
Just $
API.And
[ API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 3) $ API.ScalarType "number"),
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "GenreId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 1) $ API.ScalarType "number")
],
API._umoPostUpdateCheck =
Just $
API.ApplyBinaryComparisonOperator
API.GreaterThan
(API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 0) $ API.ScalarType "number"),
API._umoReturningFields =
HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
(API.FieldName "updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
( API.FieldName "updatedRows_Genre",
API.RelField
( API.RelationshipField
(API.RelationshipName "Genre")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
]
}
]
}
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
mockAgentTest "update_many rows with update permissions" $ \performGraphqlRequest -> do
let headers = [("X-Hasura-AlbumId", "3"), ("X-Hasura-Role", testRoleName)]
let graphqlRequest =
[graphql|
mutation UpdateMutation {
update_Track_many(updates: [
{ where: {TrackId: {_eq: 3}}, _set: {Name: "Another Name"}, _inc: {Milliseconds: 1000} },
{ where: {TrackId: {_gt: 3}}, _set: {Name: "Better Name"}, _inc: {UnitPrice: 1} }
]) {
updatedRowCount: affected_rows
updatedRows: returning {
TrackId
Name
Genre {
Name
}
}
}
}
|]
let mockAgentResponse =
API.MutationResponse
[ API.MutationOperationResults
{ API._morAffectedRows = 1,
API._morReturning =
Just
[ HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
]
]
)
]
]
},
API.MutationOperationResults
{ API._morAffectedRows = 2,
API._morReturning =
Just
[ HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
( API.FieldName "updatedRows_Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
]
]
)
],
HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
( API.FieldName "updatedRows_Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
]
]
)
]
]
}
]
let mockConfig = Mock.chinookMock & mockMutationResponse mockAgentResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
update_Track_many:
- updatedRowCount: 1
updatedRows:
- TrackId: 3
Name: Another Name
Genre:
Name: Rock
- updatedRowCount: 2
updatedRows:
- TrackId: 4
Name: Better Name
Genre:
Name: Rock
- TrackId: 5
Name: Better Name
Genre:
Name: Rock
|]
let sharedPostUpdateCheck =
Just $
API.ApplyBinaryComparisonOperator
API.GreaterThan
(API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 0) $ API.ScalarType "number")
let sharedReturning =
HashMap.fromList
[ (API.FieldName "updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
(API.FieldName "updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
( API.FieldName "updatedRows_Genre",
API.RelField
( API.RelationshipField
(API.RelationshipName "Genre")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
]
let expectedRequest =
API.MutationRequest
{ API._mrTableRelationships =
[ API.TableRelationships
{ API._trSourceTable = API.TableName ("Track" :| []),
API._trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Genre",
API.Relationship
{ API._rTargetTable = API.TableName ("Genre" :| []),
API._rRelationshipType = API.ObjectRelationship,
API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
}
)
]
}
],
API._mrInsertSchema = [],
API._mrOperations =
[ API.UpdateOperation $
API.UpdateMutationOperation
{ API._umoTable = API.TableName ("Track" :| []),
API._umoUpdates =
[ API.SetColumn $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "Name",
API._rcovValue = Aeson.String "Another Name",
API._rcovValueType = API.ScalarType "string"
},
API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "Milliseconds",
API._rcovValue = Aeson.Number 1000,
API._rcovValueType = API.ScalarType "number"
},
API.SetColumn $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "AlbumId",
API._rcovValue = Aeson.Number 3,
API._rcovValueType = API.ScalarType "number"
}
],
API._umoWhere =
Just $
API.And
[ API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 3) $ API.ScalarType "number"),
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "TrackId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 3) $ API.ScalarType "number")
],
API._umoPostUpdateCheck = sharedPostUpdateCheck,
API._umoReturningFields = sharedReturning
},
API.UpdateOperation $
API.UpdateMutationOperation
{ API._umoTable = API.TableName ("Track" :| []),
API._umoUpdates =
[ API.SetColumn $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "Name",
API._rcovValue = Aeson.String "Better Name",
API._rcovValueType = API.ScalarType "string"
},
API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "UnitPrice",
API._rcovValue = Aeson.Number 1,
API._rcovValueType = API.ScalarType "number"
},
API.SetColumn $
API.RowColumnOperatorValue
{ API._rcovColumn = API.ColumnName "AlbumId",
API._rcovValue = Aeson.Number 3,
API._rcovValueType = API.ScalarType "number"
}
],
API._umoWhere =
Just $
API.And
[ API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 3) $ API.ScalarType "number"),
API.ApplyBinaryComparisonOperator
API.GreaterThan
(API.ComparisonColumn API.CurrentTable (API.ColumnName "TrackId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 3) $ API.ScalarType "number")
],
API._umoPostUpdateCheck = sharedPostUpdateCheck,
API._umoReturningFields = sharedReturning
}
]
}
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -11,16 +11,19 @@ import Data.HashMap.Strict qualified as HashMap
import Data.Text.Extended (toTxt)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Backends.DataConnector.Adapter.Types
import Hasura.Backends.DataConnector.Adapter.Types.Mutations
import Hasura.Backends.DataConnector.Plan.Common
import Hasura.Backends.DataConnector.Plan.QueryPlan (reshapeAnnFields, translateAnnFields)
import Hasura.Base.Error (Code (..), QErr, throw400, throw500)
import Hasura.Prelude
import Hasura.RQL.IR.BoolExp (GBoolExp (..))
import Hasura.RQL.IR.Delete
import Hasura.RQL.IR.Insert hiding (Single)
import Hasura.RQL.IR.Returning
import Hasura.RQL.IR.Root
import Hasura.RQL.IR.Select
import Hasura.RQL.IR.Update
import Hasura.RQL.IR.Update.Batch
import Hasura.RQL.IR.Value
import Hasura.RQL.Types.Column
import Hasura.RQL.Types.Common
@ -80,8 +83,15 @@ translateMutationDB sessionVariables = \case
_mrInsertSchema = apiTableInsertSchema,
_mrOperations = [API.InsertOperation insertOperation]
}
MDBUpdate _update ->
throw400 NotSupported "translateMutationDB: update mutations not implemented for the Data Connector backend."
MDBUpdate update -> do
(updateOperations, tableRelationships) <- CPS.runWriterT $ translateUpdate sessionVariables update
let apiTableRelationships = uncurry API.TableRelationships <$> HashMap.toList (unTableRelationships tableRelationships)
pure $
API.MutationRequest
{ _mrTableRelationships = apiTableRelationships,
_mrInsertSchema = [],
_mrOperations = API.UpdateOperation <$> updateOperations
}
MDBDelete _delete ->
throw400 NotSupported "translateMutationDB: delete mutations not implemented for the Data Connector backend."
MDBFunction _returnsSet _select ->
@ -158,12 +168,71 @@ translateInsertRow sessionVariables tableName tableColumns defaultColumnValues i
& fmap (\(AIColumn columnNameAndValue) -> columnNameAndValue)
& HashMap.fromList
translateMutationOutputToReturningFields ::
translateUpdate ::
MonadError QErr m =>
SessionVariables ->
AnnotatedUpdateG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
CPS.WriterT TableRelationships m [API.UpdateMutationOperation]
translateUpdate sessionVariables annUpdate@AnnotatedUpdateG {..} = do
case _auUpdateVariant of
SingleBatch batch -> (: []) <$> translateUpdateBatch sessionVariables annUpdate batch
MultipleBatches batches -> traverse (translateUpdateBatch sessionVariables annUpdate) batches
translateUpdateBatch ::
MonadError QErr m =>
SessionVariables ->
AnnotatedUpdateG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
UpdateBatch 'DataConnector UpdateOperator (UnpreparedValue 'DataConnector) ->
CPS.WriterT TableRelationships m API.UpdateMutationOperation
translateUpdateBatch sessionVariables AnnotatedUpdateG {..} UpdateBatch {..} = do
updates <- lift $ translateUpdateOperations sessionVariables _ubOperations
whereExp <- translateBoolExpToExpression sessionVariables tableName (BoolAnd [_auUpdatePermissions, _ubWhere])
postUpdateCheck <- translateBoolExpToExpression sessionVariables tableName _auCheck
returningFields <- translateMutationOutputToReturningFields sessionVariables tableName _auOutput
pure $
API.UpdateMutationOperation
{ API._umoTable = tableName,
API._umoWhere = whereExp,
API._umoUpdates = updates,
API._umoPostUpdateCheck = postUpdateCheck,
API._umoReturningFields = HashMap.mapKeys (API.FieldName . getFieldNameTxt) returningFields
}
where
tableName = Witch.from _auTable
translateUpdateOperations ::
forall m.
MonadError QErr m =>
SessionVariables ->
HashMap ColumnName (UpdateOperator (UnpreparedValue 'DataConnector)) ->
m [API.RowUpdate]
translateUpdateOperations sessionVariables columnUpdates =
forM (HashMap.toList columnUpdates) $ \(columnName, updateOperator) -> do
let (mkRowUpdate, value) =
case updateOperator of
UpdateSet value' -> (API.SetColumn, value')
UpdateCustomOperator operatorName value' -> (API.CustomUpdateColumnOperator operatorName, value')
(scalarType, literalValue) <- prepareAndExtractLiteralValue value
let operatorValue = API.RowColumnOperatorValue (Witch.from columnName) literalValue (Witch.from scalarType)
pure $ mkRowUpdate operatorValue
where
prepareAndExtractLiteralValue :: UnpreparedValue 'DataConnector -> m (ScalarType, J.Value)
prepareAndExtractLiteralValue unpreparedValue = do
preparedLiteral <- prepareLiteral sessionVariables unpreparedValue
case preparedLiteral of
ValueLiteral scalarType value -> pure (scalarType, value)
ArrayLiteral _scalarType _values -> throw400 NotSupported "translateUpdateOperations: Array literals are not supported as column update values"
translateMutationOutputToReturningFields ::
( MonadError QErr m,
Has TableRelationships writerOutput,
Monoid writerOutput
) =>
SessionVariables ->
API.TableName ->
MutationOutputG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
CPS.WriterT (TableRelationships, TableInsertSchemas) m (HashMap FieldName API.Field)
CPS.WriterT writerOutput m (HashMap FieldName API.Field)
translateMutationOutputToReturningFields sessionVariables tableName = \case
MOutSinglerowObject annFields ->
translateAnnFields sessionVariables noPrefix tableName annFields
@ -171,12 +240,15 @@ translateMutationOutputToReturningFields sessionVariables tableName = \case
HashMap.unions <$> traverse (uncurry $ translateMutField sessionVariables tableName) mutFields
translateMutField ::
MonadError QErr m =>
( MonadError QErr m,
Has TableRelationships writerOutput,
Monoid writerOutput
) =>
SessionVariables ->
API.TableName ->
FieldName ->
MutFldG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
CPS.WriterT (TableRelationships, TableInsertSchemas) m (HashMap FieldName API.Field)
CPS.WriterT writerOutput m (HashMap FieldName API.Field)
translateMutField sessionVariables tableName fieldName = \case
MCount ->
-- All mutation operations in a request return their affected rows count.
@ -197,20 +269,51 @@ reshapeResponseToMutationGqlShape ::
MutationDB 'DataConnector Void v ->
API.MutationResponse ->
m J.Encoding
reshapeResponseToMutationGqlShape mutationDb API.MutationResponse {..} = do
reshapeResponseToMutationGqlShape mutationDb mutationResponse = do
case mutationDb of
MDBInsert AnnotatedInsert {..} ->
reshapeOutputForSingleBatchOperation _aiOutput mutationResponse
MDBUpdate AnnotatedUpdateG {..} ->
case _auUpdateVariant of
SingleBatch _batch ->
reshapeOutputForSingleBatchOperation _auOutput mutationResponse
MultipleBatches batches ->
let outputs = replicate (length batches) _auOutput
in reshapeOutputForMultipleBatchOperation outputs mutationResponse
MDBDelete AnnDel {..} ->
reshapeOutputForSingleBatchOperation _adOutput mutationResponse
MDBFunction _returnsSet _select ->
throw400 NotSupported "reshapeResponseToMutationGqlShape: function mutations not implemented for the Data Connector backend."
reshapeOutputForSingleBatchOperation ::
MonadError QErr m =>
MutationOutputG 'DataConnector Void v ->
API.MutationResponse ->
m J.Encoding
reshapeOutputForSingleBatchOperation mutationOutput API.MutationResponse {..} = do
mutationOperationResult <-
listToMaybe _mrOperationResults
`onNothing` throw500 "Unable to find expected mutation operation results"
mutationOutput <-
case mutationDb of
MDBInsert AnnotatedInsert {..} -> pure _aiOutput
MDBUpdate AnnotatedUpdateG {..} -> pure _auOutput
MDBDelete AnnDel {..} -> pure _adOutput
MDBFunction _returnsSet _select -> throw400 NotSupported "reshapeResponseToMutationGqlShape: function mutations not implemented for the Data Connector backend."
reshapeMutationOutput mutationOutput mutationOperationResult
reshapeOutputForMultipleBatchOperation ::
MonadError QErr m =>
[MutationOutputG 'DataConnector Void v] ->
API.MutationResponse ->
m J.Encoding
reshapeOutputForMultipleBatchOperation mutationOutputs API.MutationResponse {..} = do
unless (operationResultCount >= requiredResultCount) $
throw500 ("Data Connector agent returned " <> tshow operationResultCount <> " mutation operation results where at least " <> tshow requiredResultCount <> " was expected")
reshapedResults <-
zip mutationOutputs _mrOperationResults
& traverse (uncurry reshapeMutationOutput)
pure $ JE.list id reshapedResults
where
requiredResultCount = length mutationOutputs
operationResultCount = length _mrOperationResults
reshapeMutationOutput ::
MonadError QErr m =>
MutationOutputG 'DataConnector Void v ->