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

Code before:
       )
parseGraphQLQuery gqlContext varDefs varValsM fields =
  resolveVariables varDefs (fromMaybe Map.empty varValsM) fields
  >>= (gqlQueryParser gqlContext >>> (`onLeft` reportParseErrors))
  where
    reportParseErrors errs = case NESeq.head errs of
      -- TODO: Our error reporting machinery doesn’t currently support reporting
      -- multiple errors at once, so we’re throwing away all but the first one
      -- here. It would be nice to report all of them!
      ParseError{ pePath, peMessage, peCode } ->
        throwError (err400 peCode peMessage){ qePath = pePath }

-- | A method for extracting profiling data from instrumented query results.
newtype ExtractProfile = ExtractProfile 
  { runExtractProfile :: forall m. (MonadIO m, Tracing.MonadTrace m) => EncJSON -> m EncJSON
  }
  
-- | A default implementation for queries with no instrumentation
noProfile :: ExtractProfile
noProfile = ExtractProfile pure

-- | Monads which support query instrumentation
class Monad m => MonadQueryInstrumentation m where
  -- | Add instrumentation to a query, based on the provided directives,
  -- and also return an 'ExtractProfile' structure to extract any profiling data
  -- from the instrumented result.
  askInstrumentQuery 
    :: [G.Directive G.Name]
    -> m (Q.Query -> Q.Query, ExtractProfile)

  -- A default for monad transformer instances
  default askInstrumentQuery 
    :: (m ~ t n, MonadTrans t, MonadQueryInstrumentation n) 
    => [G.Directive G.Name]
    -> m (Q.Query -> Q.Query, ExtractProfile)
  askInstrumentQuery = lift . askInstrumentQuery

instance MonadQueryInstrumentation m => MonadQueryInstrumentation (ReaderT r m)
instance MonadQueryInstrumentation m => MonadQueryInstrumentation (ExceptT e m)
instance MonadQueryInstrumentation m => MonadQueryInstrumentation (Tracing.TraceT m)

convertQuerySelSet
  :: forall m tx .
     ( MonadError QErr m
     , HasVersion
     , MonadIO m
     , Tracing.MonadTrace m
     , MonadQueryInstrumentation m
     , MonadIO tx
     , MonadTx tx
     , Tracing.MonadTrace tx

Review comment (bug):
I’d suggest the following minor expansion of the documentation here to make this function’s purpose clearer without knowing how this is used in Cloud/Pro:

```suggestion
  -- | Returns the appropriate /instrumentation/ (if any) for a SQL query, as
  -- requested by the provided directives. Instrumentation is “SQL middleware”:
  --
  --   * The @'Q.Query' -> 'Q.Query'@ function is applied to the query just
  --     prior to execution, and it can adjust the query in arbitrary ways.
  --
  --   * The 'ExtractProfile' function is applied to the query /result/, and it
  --     can perform arbitrary side-effects based on its contents. (This is
  --     currently just a hook for tracing, a la 'Tracing.MonadTrace'.)
  --
  -- The open-source version of graphql-engine does not currently perform any
  -- instrumentation, so this serves only as a hook for downstream clients.
```

Feel free to tweak or correct my wording if any of my guesses were wrong.

Code after:
  resolveVariables varDefs (fromMaybe Map.empty varValsM) fields
  >>= (gqlQueryParser gqlContext >>> (`onLeft` reportParseErrors))
  where
    reportParseErrors errs = case NESeq.head errs of
      -- TODO: Our error reporting machinery doesn’t currently support reporting
      -- multiple errors at once, so we’re throwing away all but the first one
      -- here. It would be nice to report all of them!
      ParseError{ pePath, peMessage, peCode } ->
        throwError (err400 peCode peMessage){ qePath = pePath }

-- | A method for extracting profiling data from instrumented query results.
newtype ExtractProfile = ExtractProfile 
  { runExtractProfile :: forall m. (MonadIO m, Tracing.MonadTrace m) => EncJSON -> m EncJSON
  }
  
-- | A default implementation for queries with no instrumentation
noProfile :: ExtractProfile
noProfile = ExtractProfile pure

-- | Monads which support query instrumentation
class Monad m => MonadQueryInstrumentation m where
  -- | Returns the appropriate /instrumentation/ (if any) for a SQL query, as
  -- requested by the provided directives. Instrumentation is “SQL middleware”:
  --
  --   * The @'Q.Query' -> 'Q.Query'@ function is applied to the query just
  --     prior to execution, and it can adjust the query in arbitrary ways.
  --
  --   * The 'ExtractProfile' function is applied to the query /result/, and it
  --     can perform arbitrary side-effects based on its contents. (This is
  --     currently just a hook for tracing, a la 'Tracing.MonadTrace'.)
  --
  -- The open-source version of graphql-engine does not currently perform any
  -- instrumentation, so this serves only as a hook for downstream clients.
  askInstrumentQuery 
    :: [G.Directive G.Name]
    -> m (Q.Query -> Q.Query, ExtractProfile)

  -- A default for monad transformer instances
  default askInstrumentQuery 
    :: (m ~ t n, MonadTrans t, MonadQueryInstrumentation n) 
    => [G.Directive G.Name]
    -> m (Q.Query -> Q.Query, ExtractProfile)
  askInstrumentQuery = lift . askInstrumentQuery

instance MonadQueryInstrumentation m => MonadQueryInstrumentation (ReaderT r m)
instance MonadQueryInstrumentation m => MonadQueryInstrumentation (ExceptT e m)
instance MonadQueryInstrumentation m => MonadQueryInstrumentation (Tracing.TraceT m)

convertQuerySelSet
  :: forall m tx .
     ( MonadError QErr m