Repository: hasura/graphql-engine
File: server/src-lib/Hasura/GraphQL/Schema/Action.hs (Haskell)

Code before:
    mkQueryActionFieldsAndTypes actionInfo permission
  where
    adminPermission = ActionPermissionInfo adminRoleName
    permissions = Map.insert adminRoleName adminPermission $ _aiPermissions actionInfo

mkActionsSchema
  :: ActionCache
  -> Map.HashMap RoleName (RootFields, TyAgg)
mkActionsSchema =
  foldl'
  (\aggregate actionInfo ->
     case _adType $ _aiDefinition actionInfo of
       ActionQuery ->
         Map.foldrWithKey (accumulateQuery (_aiPgScalars actionInfo)) aggregate $
           mkQueryActionSchemaOne actionInfo
       ActionMutation kind ->
         Map.foldrWithKey (accumulateMutation (_aiPgScalars actionInfo)) aggregate $
           mkMutationActionSchemaOne actionInfo kind
  )
  mempty
  where
    -- we'll need to add uuid and timestamptz for actions
    initRoleState =
      ( mempty
      , foldr addScalarToTyAgg mempty [PGJSON, PGTimeStampTZ, PGUUID]
      )

    addScalarsToTyAgg = foldr addScalarToTyAgg

    accumulateQuery pgScalars roleName (actionField, fields) =
      Map.alter (Just . addToStateSync . fromMaybe initRoleState) roleName
      where
        addToStateSync (rootFields, tyAgg) =
          ( addQueryField (first QCAction actionField) rootFields
          , addFieldsToTyAgg fields $ addScalarsToTyAgg tyAgg pgScalars
          )

    accumulateMutation pgScalars roleName (queryFieldM, actionField, fields) =
      Map.alter (Just . addToState . fromMaybe initRoleState) roleName
      where
        addToState (rootFields, tyAgg) =
          let tyAggWithPgScalars = addScalarsToTyAgg tyAgg pgScalars
          in case queryFieldM of
               Just (fldCtx, fldDefinition, responseTypeInfo) ->
                 addToStateAsync (fldCtx, fldDefinition)
                   responseTypeInfo (rootFields, tyAggWithPgScalars)
               Nothing -> addToStateSync (rootFields, tyAggWithPgScalars)
        addToStateSync (rootFields, tyAgg) =
          ( addMutationField (first MCAction actionField) rootFields
          , addFieldsToTyAgg fields tyAgg
          )

Review comment (suggestion):
This function is causing the issue. The `mkNewRoleState` function initiates the state with empty `RootFields` and `TyAgg` with PG scalars reused and mandatory scalars for actions. This function is invoked only for the first action in the list (unfortunately) and remaining actions' reused PG scalars were not added to `TyAgg`. Hence the issue only exist if multiple actions are defined. The fix is to remove adding PG scalars in this function and add PG scalars to `TyAgg` explicitly for each action in accumulate functions.


Code after:
    mkQueryActionFieldsAndTypes actionInfo permission
  where
    adminPermission = ActionPermissionInfo adminRoleName
    permissions = Map.insert adminRoleName adminPermission $ _aiPermissions actionInfo

mkActionsSchema
  :: ActionCache
  -> Map.HashMap RoleName (RootFields, TyAgg)
mkActionsSchema =
  foldl'
  (\aggregate actionInfo ->
     case _adType $ _aiDefinition actionInfo of
       ActionQuery ->
         Map.foldrWithKey (accumulateQuery (_aiPgScalars actionInfo)) aggregate $
           mkQueryActionSchemaOne actionInfo
       ActionMutation kind ->
         Map.foldrWithKey (accumulateMutation (_aiPgScalars actionInfo)) aggregate $
           mkMutationActionSchemaOne actionInfo kind
  )
  mempty
  where
    -- we'll need to add uuid and timestamptz for actions
    initRoleState =
      ( mempty
      , foldr addScalarToTyAgg mempty [PGJSON, PGTimeStampTZ, PGUUID]
      )

    addScalarsToTyAgg = foldr addScalarToTyAgg

    accumulateQuery pgScalars roleName (actionField, fields) =
      Map.alter (Just . addToStateSync . fromMaybe initRoleState) roleName
      where
        addToStateSync (rootFields, tyAgg) =
          ( addQueryField (first QCAction actionField) rootFields
          -- Add reused PG scalars to TyAgg
          , addFieldsToTyAgg fields $ addScalarsToTyAgg tyAgg pgScalars
          )

    accumulateMutation pgScalars roleName (queryFieldM, actionField, fields) =
      Map.alter (Just . addToState . fromMaybe initRoleState) roleName
      where
        addToState (rootFields, tyAgg) =
          let rootFieldsWithActionMutation =
                addMutationField (first MCAction actionField) rootFields
              -- Add reused PG scalars to TyAgg
              tyAggWithFieldsAndPgScalars =
                addFieldsToTyAgg fields $ addScalarsToTyAgg tyAgg pgScalars
          in case queryFieldM of
               Just (fldCtx, fldDefinition, responseTypeInfo) ->
                 -- Add async action's query resolver and response type
                 ( addQueryField (QCAsyncActionFetch fldCtx, fldDefinition)