Prefer ServantT for server to consume App context
Long story -> short: I'd like to access my App monad from within my Servant handlers. While this code type-checks, I'm not sure it's working as intended. Needing to change throwError to throwIO fails the "smell test". I expect to refactor this code, but I'm calling it a night for now.
This commit is contained in:
		
							parent
							
								
									bbcd0bf27d
								
							
						
					
					
						commit
						f61ed25755
					
				
					 2 changed files with 33 additions and 24 deletions
				
			
		| 
						 | 
					@ -6,20 +6,21 @@ import Servant
 | 
				
			||||||
import API
 | 
					import API
 | 
				
			||||||
import Data.String.Conversions (cs)
 | 
					import Data.String.Conversions (cs)
 | 
				
			||||||
import Control.Monad.IO.Class (liftIO)
 | 
					import Control.Monad.IO.Class (liftIO)
 | 
				
			||||||
 | 
					import Network.Wai.Middleware.Cors
 | 
				
			||||||
import GoogleSignIn (EncodedJWT(..), ValidationResult(..))
 | 
					import GoogleSignIn (EncodedJWT(..), ValidationResult(..))
 | 
				
			||||||
import Utils
 | 
					import Utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import qualified Network.Wai.Handler.Warp as Warp
 | 
					import qualified Network.Wai.Handler.Warp as Warp
 | 
				
			||||||
import qualified Network.Wai.Middleware.Cors as Cors
 | 
					 | 
				
			||||||
import qualified GoogleSignIn
 | 
					import qualified GoogleSignIn
 | 
				
			||||||
import qualified Types as T
 | 
					import qualified Types as T
 | 
				
			||||||
--------------------------------------------------------------------------------
 | 
					--------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
server :: Server API
 | 
					server :: ServerT API T.App
 | 
				
			||||||
server = verifyGoogleSignIn
 | 
					server = verifyGoogleSignIn
 | 
				
			||||||
  where
 | 
					  where
 | 
				
			||||||
    verifyGoogleSignIn :: T.VerifyGoogleSignInRequest -> Handler NoContent
 | 
					    verifyGoogleSignIn :: T.VerifyGoogleSignInRequest -> T.App NoContent
 | 
				
			||||||
    verifyGoogleSignIn T.VerifyGoogleSignInRequest{..} = do
 | 
					    verifyGoogleSignIn T.VerifyGoogleSignInRequest{..} = do
 | 
				
			||||||
 | 
					      T.Context{..} <- ask
 | 
				
			||||||
      validationResult <- liftIO $ GoogleSignIn.validateJWT False (EncodedJWT idToken)
 | 
					      validationResult <- liftIO $ GoogleSignIn.validateJWT False (EncodedJWT idToken)
 | 
				
			||||||
      case validationResult of
 | 
					      case validationResult of
 | 
				
			||||||
        Valid _ -> do
 | 
					        Valid _ -> do
 | 
				
			||||||
| 
						 | 
					@ -29,18 +30,23 @@ server = verifyGoogleSignIn
 | 
				
			||||||
          --   Redirect the SPA to the sign-up / payment page
 | 
					          --   Redirect the SPA to the sign-up / payment page
 | 
				
			||||||
          pure NoContent
 | 
					          pure NoContent
 | 
				
			||||||
        err -> do
 | 
					        err -> do
 | 
				
			||||||
        throwError err401 { errBody = err |> GoogleSignIn.explainResult |> cs }
 | 
					          -- TODO: I would prefer to use `throwError` here, but after changing
 | 
				
			||||||
 | 
					          -- to ServerT, I couldn't get the code to compile.
 | 
				
			||||||
 | 
					          throwIO err401 { errBody = err |> GoogleSignIn.explainResult |> cs }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
run :: RIO T.Context ()
 | 
					run :: T.App ()
 | 
				
			||||||
run = do
 | 
					run = do
 | 
				
			||||||
  T.Context{..} <- ask
 | 
					  ctx@T.Context{..} <- ask
 | 
				
			||||||
  liftIO $ Warp.run contextServerPort (enforceCors $ serve (Proxy @ API) $ server)
 | 
					  server
 | 
				
			||||||
 | 
					    |> hoistServer (Proxy @ API) (runRIO ctx)
 | 
				
			||||||
 | 
					    |> serve (Proxy @ API)
 | 
				
			||||||
 | 
					    |> cors (const $ Just corsPolicy)
 | 
				
			||||||
 | 
					    |> Warp.run contextServerPort
 | 
				
			||||||
 | 
					    |> liftIO
 | 
				
			||||||
  where
 | 
					  where
 | 
				
			||||||
    enforceCors = Cors.cors (const $ Just corsPolicy)
 | 
					    corsPolicy :: CorsResourcePolicy
 | 
				
			||||||
    corsPolicy :: Cors.CorsResourcePolicy
 | 
					    corsPolicy = simpleCorsResourcePolicy
 | 
				
			||||||
    corsPolicy =
 | 
					      { corsOrigins = Just (["http://localhost:8000"], True)
 | 
				
			||||||
      Cors.simpleCorsResourcePolicy
 | 
					      , corsMethods = simpleMethods ++ ["PUT", "PATCH", "DELETE", "OPTIONS"]
 | 
				
			||||||
        { Cors.corsOrigins = Just (["http://localhost:8000"], True)
 | 
					      , corsRequestHeaders = simpleHeaders ++ ["Content-Type", "Authorization"]
 | 
				
			||||||
        , Cors.corsMethods = Cors.simpleMethods ++ ["PUT", "PATCH", "DELETE", "OPTIONS"]
 | 
					 | 
				
			||||||
        , Cors.corsRequestHeaders = Cors.simpleHeaders ++ ["Content-Type", "Authorization"]
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,9 @@ data Context = Context
 | 
				
			||||||
  , contextClientPort :: !Int
 | 
					  , contextClientPort :: !Int
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- | Type synonym for my application monad.
 | 
				
			||||||
 | 
					type App = RIO Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
data VerifyGoogleSignInRequest = VerifyGoogleSignInRequest
 | 
					data VerifyGoogleSignInRequest = VerifyGoogleSignInRequest
 | 
				
			||||||
  { idToken :: !Text
 | 
					  { idToken :: !Text
 | 
				
			||||||
  } deriving (Eq, Show)
 | 
					  } deriving (Eq, Show)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue