@@ -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,23 @@ 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+ let handleErrors = fmap (either (Error. errorResponseFor configClientErrorVerbosity) identity)
132133
133- response <- do
134- authResultE <- runExceptT $ withTiming appConf $
135- liftIO (Auth. getAuthResult appState bearerAuth) >>= liftEither
134+ (response, authRole) <-
135+ -- writer to save authRole
136+ -- has to be before runExceptT to make sure role is not lost on error
137+ runWriterT $
138+ handleErrors $
139+ runExceptT $
140+ do
141+ (jwtTime, authResult@ AuthResult {.. }) <- withTiming appConf $
142+ Auth. getAuthResult appState $ ApiRequest. userBearerAuth req
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+ tell $ pure authRole
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+ postgrestResponse appState appConf maybeSchemaCache jwtTime authResult req
147+
148+ AppState. getObserver appState $ genResponseObs (getLast authRole) req response
148149
149150 -- Launch the connWorker when the connection is down. The postgrest
150151 -- function can respond successfully (with a stale schema cache) before
@@ -164,13 +165,14 @@ postgrest appState connWorker =
164165 ResponseObs user req (Wai. responseStatus resp) (WaiHeader. contentLength $ Wai. responseHeaders resp)
165166
166167postgrestResponse
167- :: AppState. AppState
168+ :: (MonadError Error m , MonadIO m )
169+ => AppState. AppState
168170 -> AppConfig
169171 -> Maybe SchemaCache
170172 -> Maybe Double
171173 -> AuthResult
172174 -> Wai. Request
173- -> ExceptT Error IO Wai. Response
175+ -> m Wai. Response
174176postgrestResponse appState conf@ AppConfig {.. } maybeSchemaCache jwtTime authResult@ AuthResult {.. } req = do
175177 let observer = AppState. getObserver appState
176178
@@ -179,12 +181,12 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul
179181 Just sCache ->
180182 return sCache
181183 Nothing -> do
182- lift $ observer SchemaCacheEmptyObs
184+ liftIO $ observer SchemaCacheEmptyObs
183185 throwError Error. NoSchemaCacheError
184186
185187 let prefs = ApiRequest. userPreferences conf req (dbTimezones sCache)
186188
187- body <- lift $ Wai. strictRequestBody req
189+ body <- liftIO $ Wai. strictRequestBody req
188190
189191 (parseTime, apiReq@ ApiRequest {.. }) <- withTiming conf $ liftEither . mapLeft Error. ApiRequestErr $ ApiRequest. userApiRequest conf prefs req body
190192 (planTime, plan) <- withTiming conf $ liftEither $ Plan. actionPlan iAction conf apiReq sCache
@@ -197,21 +199,21 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul
197199 case tx of
198200 MainTx. NoDbTx r -> pure r
199201 MainTx. DbTx dbSession -> do
200- dbRes <- lift $ AppState. usePool appState dbSession
202+ dbRes <- liftIO $ AppState. usePool appState dbSession
201203 let eitherResp = join $ mapLeft (Error. PgErr . Error. PgError (Just authRole /= configDbAnonRole)) dbRes
202204
203205 -- 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.
204206 -- This is because of a combination of ExceptT + our Error module which has Wai.responseLBS.
205207 -- This needs refactoring so only the below obsQuery is used.
206- lift $ whenLeft eitherResp $ obsQuery . Error. status
208+ liftIO $ whenLeft eitherResp $ obsQuery . Error. status
207209 liftEither eitherResp
208210
209211 (respTime, resp) <- withTiming conf $ do
210212 let response = Response. actionResponse txResult apiReq (T. decodeUtf8 prettyVersion, docsVersion) conf sCache
211213 status' = either Error. status Response. pgrstStatus response
212214
213215 -- TODO: see above obsQuery, only this obsQuery should remain after refactoring (because the QueryObs depends on the status)
214- lift $ obsQuery status'
216+ liftIO $ obsQuery status'
215217 liftEither response
216218
217219 return $ toWaiResponse (ServerTiming jwtTime parseTime planTime txTime respTime) resp
@@ -230,7 +232,7 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul
230232 varyHeaderPresent :: [HTTP. Header ] -> Bool
231233 varyHeaderPresent = any (\ (h, _v) -> h == HTTP. hVary)
232234
233- withTiming :: AppConfig -> ExceptT e IO a -> ExceptT e IO (Maybe Double , a )
235+ withTiming :: ( MonadError e m , MonadIO m ) => AppConfig -> m a -> m (Maybe Double , a )
234236withTiming AppConfig {configServerTimingEnabled} f = if configServerTimingEnabled
235237 then do
236238 (t, r) <- timeItT f
0 commit comments