@@ -9,6 +9,7 @@ Some of its functionality includes:
99- Producing HTTP Headers according to RFCs.
1010- Content Negotiation
1111-}
12+ {-# LANGUAGE FlexibleContexts #-}
1213{-# LANGUAGE NamedFieldPuns #-}
1314{-# LANGUAGE RecordWildCards #-}
1415{-# LANGUAGE ScopedTypeVariables #-}
@@ -61,6 +62,7 @@ import PostgREST.SchemaCache (SchemaCache (..))
6162import PostgREST.TimeIt (timeItT )
6263import PostgREST.Version (docsVersion , prettyVersion )
6364
65+ import Control.Monad.Writer
6466import qualified Data.ByteString.Char8 as BS
6567import qualified Data.List as L
6668import Data.Streaming.Network (bindPortTCP )
@@ -127,24 +129,21 @@ postgrest appState connWorker =
127129 appConf@ AppConfig {.. } <- AppState. getConfig appState -- the config must be read again because it can reload
128130 maybeSchemaCache <- AppState. getSchemaCache appState
129131
130- let observer = AppState. getObserver appState
131- bearerAuth = ApiRequest. userBearerAuth req
132+ (response, user) <-
133+ -- has to be before runExceptT to make sure role is not lost on error
134+ runWriterT $
135+ -- handle errors
136+ fmap (either (Error. errorResponseFor configClientErrorVerbosity) identity) $
137+ runExceptT $
138+ do
139+ (jwtTime, authResult@ AuthResult {.. }) <- withTiming appConf $
140+ Auth. getAuthResult appState $ ApiRequest. userBearerAuth req
132141
133- response <- do
134- authResultE <- runExceptT $ withTiming appConf $
135- liftIO (Auth. getAuthResult appState bearerAuth) >>= liftEither
142+ tell $ pure authRole
136143
137- case authResultE of
138- Left err -> do
139- let resp = Error. errorResponseFor configClientErrorVerbosity err
140- observer $ genResponseObs Nothing req resp
141- pure resp
144+ postgrestResponse appState appConf maybeSchemaCache jwtTime authResult req
142145
143- Right (jwtTime, authResult@ AuthResult {.. }) -> do
144- resp <- either (Error. errorResponseFor configClientErrorVerbosity) identity <$>
145- runExceptT (postgrestResponse appState appConf maybeSchemaCache jwtTime authResult req)
146- observer $ genResponseObs (Just authRole) req resp
147- pure resp
146+ AppState. getObserver appState $ genResponseObs (getLast user) req response
148147
149148 -- Launch the connWorker when the connection is down. The postgrest
150149 -- function can respond successfully (with a stale schema cache) before
@@ -164,13 +163,14 @@ postgrest appState connWorker =
164163 ResponseObs user req (Wai. responseStatus resp) (WaiHeader. contentLength $ Wai. responseHeaders resp)
165164
166165postgrestResponse
167- :: AppState. AppState
166+ :: (MonadError Error m , MonadIO m )
167+ => AppState. AppState
168168 -> AppConfig
169169 -> Maybe SchemaCache
170170 -> Maybe Double
171171 -> AuthResult
172172 -> Wai. Request
173- -> ExceptT Error IO Wai. Response
173+ -> m Wai. Response
174174postgrestResponse appState conf@ AppConfig {.. } maybeSchemaCache jwtTime authResult@ AuthResult {.. } req = do
175175 let observer = AppState. getObserver appState
176176
@@ -179,12 +179,12 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul
179179 Just sCache ->
180180 return sCache
181181 Nothing -> do
182- lift $ observer SchemaCacheEmptyObs
182+ liftIO $ observer SchemaCacheEmptyObs
183183 throwError Error. NoSchemaCacheError
184184
185185 let prefs = ApiRequest. userPreferences conf req (dbTimezones sCache)
186186
187- body <- lift $ Wai. strictRequestBody req
187+ body <- liftIO $ Wai. strictRequestBody req
188188
189189 (parseTime, apiReq@ ApiRequest {.. }) <- withTiming conf $ liftEither . mapLeft Error. ApiRequestErr $ ApiRequest. userApiRequest conf prefs req body
190190 (planTime, plan) <- withTiming conf $ liftEither $ Plan. actionPlan iAction conf apiReq sCache
@@ -197,21 +197,21 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul
197197 case tx of
198198 MainTx. NoDbTx r -> pure r
199199 MainTx. DbTx {.. } -> do
200- dbRes <- lift $ AppState. usePool appState (dqTransaction dqIsoLevel dqTxMode $ runExceptT dqDbHandler)
200+ dbRes <- liftIO $ AppState. usePool appState (dqTransaction dqIsoLevel dqTxMode $ runExceptT dqDbHandler)
201201 let eitherResp = join $ mapLeft (Error. PgErr . Error. PgError (Just authRole /= configDbAnonRole)) dbRes
202202
203203 -- TODO: we use obsQuery twice, one here and one below because in case of an error with the usePool above, the request will finish here and return an error message.
204204 -- This is because of a combination of ExceptT + our Error module which has Wai.responseLBS.
205205 -- This needs refactoring so only the below obsQuery is used.
206- lift $ whenLeft eitherResp $ obsQuery . Error. status
206+ liftIO $ whenLeft eitherResp $ obsQuery . Error. status
207207 liftEither eitherResp
208208
209209 (respTime, resp) <- withTiming conf $ do
210210 let response = Response. actionResponse txResult apiReq (T. decodeUtf8 prettyVersion, docsVersion) conf sCache
211211 status' = either Error. status Response. pgrstStatus response
212212
213213 -- TODO: see above obsQuery, only this obsQuery should remain after refactoring (because the QueryObs depends on the status)
214- lift $ obsQuery status'
214+ liftIO $ obsQuery status'
215215 liftEither response
216216
217217 return $ toWaiResponse (ServerTiming jwtTime parseTime planTime txTime respTime) resp
@@ -230,7 +230,7 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul
230230 varyHeaderPresent :: [HTTP. Header ] -> Bool
231231 varyHeaderPresent = any (\ (h, _v) -> h == HTTP. hVary)
232232
233- withTiming :: AppConfig -> ExceptT e IO a -> ExceptT e IO (Maybe Double , a )
233+ withTiming :: ( MonadError e m , MonadIO m ) => AppConfig -> m a -> m (Maybe Double , a )
234234withTiming AppConfig {configServerTimingEnabled} f = if configServerTimingEnabled
235235 then do
236236 (t, r) <- timeItT f
0 commit comments