Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .github/workflows/code-style.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Code style check
name: Code style checks

concurrency:
group: formatting-${{ github.ref_name }}
Expand All @@ -20,4 +20,17 @@ jobs:
uses: cachix/install-nix-action@v31
- name: Check code formatting
run: |
nix develop '#haskellFormatter' --command fourmolu --mode=check --check-idempotence servant servant-*
nix develop '#haskellFormatter' --command fourmolu --mode=check --check-idempotence servant servant-*

lint:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install Nix
uses: cachix/install-nix-action@v31
- name: Run hlint check
run: |
nix develop '#haskellLinter' --command hlint servant servant-*
9 changes: 3 additions & 6 deletions hlint.yaml → .hlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,12 @@
#
# Generalise map to fmap, ++ to <>
# - group: {name: generalise, enabled: true}
#
# Replace return with pure
- group: {name: future, enabled: true}


# Ignore some builtin hints
- ignore: {name: Redundant do}
- ignore: {name: Parse error}
- ignore: {name: Use fmap}
- ignore: {name: Use list comprehension}
- ignore: {name: Use lambda-case}
- ignore: {name: Eta reduce}
# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules


Expand Down
8 changes: 4 additions & 4 deletions doc/cookbook/basic-auth/BasicAuth.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ api :: Proxy API
api = Proxy

server :: Server API
server usr = return (site usr)
server usr = pure (site usr)
```

In order to protect our endpoint (`"mysite" :> Get '[JSON] Website`), we simply
Expand Down Expand Up @@ -105,10 +105,10 @@ checkBasicAuth db = BasicAuthCheck $ \basicAuthData ->
password = decodeUtf8 (basicAuthPassword basicAuthData)
in
case Map.lookup username db of
Nothing -> return NoSuchUser
Nothing -> pure NoSuchUser
Just u -> if pass u == password
then return (Authorized u)
else return BadPassword
then pure (Authorized u)
else pure BadPassword
```

This check simply looks up the user in the "database" and makes sure the
Expand Down
16 changes: 8 additions & 8 deletions doc/cookbook/basic-streaming/Streaming.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ In other words, without streaming libraries.
- Some basic usage doesn't require usage of streaming libraries,
like `conduit`, `pipes`, `machines` or `streaming`.
We have bindings for them though.
- Similar example is bundled with each of our streaming library interop packages (see
- Similar example is bundled with each of our streaming library interop packages (see
[servant-pipes](https://github.com/haskell-servant/servant/blob/master/servant-pipes/example/Main.hs),
[servant-conduit](https://github.com/haskell-servant/servant/blob/master/servant-conduit/example/Main.hs) and
[servant-machines](https://github.com/haskell-servant/servant/blob/master/servant-machines/example/Main.hs))
- `SourceT` doesn't have *Prelude* with handy combinators, so we have to write
things ourselves. (Note to self: `mapM` and `foldM` would be handy to have).
things ourselves. (Note to self: `mapM` and `foldM` would be handy to have).

## Code

Expand Down Expand Up @@ -64,19 +64,19 @@ server :: Server API
server = fast :<|> slow :<|> readme :<|> proxy where
fast n = liftIO $ do
putStrLn $ "/get/" ++ show n
return $ fastSource n
pure $ fastSource n

slow n = liftIO $ do
putStrLn $ "/slow/" ++ show n
return $ slowSource n
pure $ slowSource n

readme = liftIO $ do
putStrLn "/proxy"
return (S.readFile "README.md")
pure (S.readFile "README.md")

proxy c = liftIO $ do
putStrLn "/proxy"
return c
pure c

-- for some reason unfold leaks?
fastSource = S.fromStepT . mk where
Expand Down Expand Up @@ -116,8 +116,8 @@ main = do
x <- S.unSourceT src (go (0 :: Int))
print x
where
go !acc S.Stop = return acc
go !acc (S.Error err) = print err >> return acc
go !acc S.Stop = pure acc
go !acc (S.Error err) = print err >> pure acc
go !acc (S.Skip s) = go acc s
go !acc (S.Effect ms) = ms >>= go acc
go !acc (S.Yield _ s) = go (acc + 1) s
Expand Down
4 changes: 2 additions & 2 deletions doc/cookbook/curl-mock/CurlMock.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,10 @@ generateEndpoint :: Text -> Req Mocked -> IO Text
generateEndpoint host req =
case maybeBody of
Just body ->
body >>= \b -> return $ T.intercalate " " [ "curl", "-X", method, "-d", "'" <> b <> "'"
body >>= \b -> pure $ T.intercalate " " [ "curl", "-X", method, "-d", "'" <> b <> "'"
, "-H 'Content-Type: application/json'", host <> "/" <> url ]
Nothing ->
return $ T.intercalate " " [ "curl", "-X", method, host <> "/" <> url ]
pure $ T.intercalate " " [ "curl", "-X", method, host <> "/" <> url ]
where
method = decodeUtf8 $ req ^. reqMethod

Expand Down
8 changes: 4 additions & 4 deletions doc/cookbook/custom-errors/CustomErrors.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ server :: Server TestApi
server = helloH :<|> postGreetH :<|> deleteGreetH

where helloH name Nothing = helloH name (Just False)
helloH name (Just False) = return . Greet $ "Hello, " <> name
helloH name (Just True) = return . Greet . Text.toUpper $ "Hello, " <> name
helloH name (Just False) = pure . Greet $ "Hello, " <> name
helloH name (Just True) = pure . Greet . Text.toUpper $ "Hello, " <> name

postGreetH greet = return greet
postGreetH greet = pure greet

deleteGreetH _ = return NoContent
deleteGreetH _ = pure NoContent
```

## Error formatters
Expand Down
12 changes: 6 additions & 6 deletions doc/cookbook/db-mysql-basics/MysqlBasics.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ doMigration :: IO ()
doMigration = runNoLoggingT $ runResourceT $ withMySQLConn connInfo $ runReaderT $ runMigration migrateAll

server :: Server Api
server =
personGET :<|>
personGETById :<|>
server =
personGET :<|>
personGETById :<|>
personDELETE :<|>
personPOST
where
Expand All @@ -155,20 +155,20 @@ server =
selectPersons :: Handler [Person]
selectPersons = do
personList <- runDB $ selectList [] []
return $ map (\(Entity _ u) -> u) personList
pure $ map (\(Entity _ u) -> u) personList

selectPersonById :: Int -> Handler Person
selectPersonById id = do
sqlResult <- runDB $ get $ PersonKey id
case sqlResult of
Just person -> return person
Just person -> pure person
Nothing -> throwError err404 { errBody = JSON.encode "Person with ID not found." }

createPerson :: Person -> Handler Person
createPerson person = do
attemptCreate <- runDB $ insert person
case attemptCreate of
PersonKey k -> return person
PersonKey k -> pure person
_ -> throwError err503 { errBody = JSON.encode "Could not create Person." }

deletePerson :: Int -> Handler ()
Expand Down
4 changes: 2 additions & 2 deletions doc/cookbook/db-postgres-pool/PostgresPool.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ initDB :: DBConnectionString -> IO ()
initDB connstr = bracket (connectPostgreSQL connstr) close $ \conn -> do
execute_ conn
"CREATE TABLE IF NOT EXISTS messages (msg text not null)"
return ()
pure ()
```

Next, our server implementation. It will be parametrised (take as
Expand All @@ -76,7 +76,7 @@ server conns = postMessage :<|> getMessages
execute conn
"INSERT INTO messages VALUES (?)"
(Only msg)
return NoContent
pure NoContent

getMessages :: Handler [Message]
getMessages = fmap (map fromOnly) . liftIO $
Expand Down
2 changes: 1 addition & 1 deletion doc/cookbook/db-sqlite-simple/DBConnection.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ server dbfile = postMessage :<|> getMessages
execute conn
"INSERT INTO messages VALUES (?)"
(Only msg)
return NoContent
pure NoContent

getMessages :: Handler [Message]
getMessages = fmap (map fromOnly) . liftIO $
Expand Down
2 changes: 1 addition & 1 deletion doc/cookbook/file-upload/FileUpload.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ upload multipartData = do
let content = fdPayload file
putStrLn $ "Content of " ++ show (fdFileName file)
LBS.putStr content
return 0
pure 0

startServer :: IO ()
startServer = run 8080 (serve api upload)
Expand Down
2 changes: 1 addition & 1 deletion doc/cookbook/https/Https.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ api :: Proxy API
api = Proxy

server :: Server API
server = return 10
server = pure 10

app :: Application
app = serve api server
Expand Down
4 changes: 2 additions & 2 deletions doc/cookbook/managed-resource/ManagedResource.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ newHandle = do
putStrLn "opening file"
h <- openFile "test.txt" AppendMode
putStrLn "opened file"
return h
pure h

closeHandle :: Handle -> IO ()
closeHandle h = do
Expand All @@ -65,7 +65,7 @@ server = writeToFile
putStrLn "writing file"
hPutStrLn h legalMsg
putStrLn "wrote file"
return NoContent
pure NoContent
```

Finally we run the server in the background while we post messages to it.
Expand Down
20 changes: 10 additions & 10 deletions doc/cookbook/open-id-connect/OpenIdConnect.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ api = Proxy

server :: OIDCEnv -> Server API
server oidcEnv = serveOIDC oidcEnv handleOIDCLogin
:<|> return Homepage
:<|> pure Homepage

-- | Then main app
app :: OIDCEnv -> Application
Expand Down Expand Up @@ -161,7 +161,7 @@ initOIDC OIDCConf{..} = do
mgr <- newManager tlsManagerSettings
prov <- O.discover "https://accounts.google.com" mgr
let oidc = O.setCredentials clientId clientPassword redirectUri (O.newOIDC prov)
return OIDCEnv { oidc = oidc
pure OIDCEnv { oidc = oidc
, mgr = mgr
, genState = genRandomBS
, prov = prov
Expand Down Expand Up @@ -208,13 +208,13 @@ genOIDCURL OIDCEnv{..} = do
st <- genState -- generate a random string
let oidcCreds = O.setCredentials clientId clientPassword redirectUri (O.newOIDC prov)
loc <- O.getAuthenticationRequestUrl oidcCreds [O.openId, O.email, O.profile] (Just st) []
return (show loc)
pure (show loc)

handleLogin :: OIDCEnv -> Handler NoContent
handleLogin oidcenv = do
loc <- liftIO (genOIDCURL oidcenv)
redirects loc
return NoContent
pure NoContent
```

The `AuthInfo` is about the infos we can grab from OIDC provider.
Expand All @@ -236,7 +236,7 @@ instance FromJSON AuthInfo where
email :: Text <- v .: "email"
email_verified :: Bool <- v .: "email_verified"
name :: Text <- v .: "name"
return $ AuthInfo (toS email) email_verified (toS name)
pure $ AuthInfo (toS email) email_verified (toS name)
parseJSON invalid = AeT.typeMismatch "Coord" invalid
instance JSON.ToJSON AuthInfo where
toJSON (AuthInfo e ev n) =
Expand Down Expand Up @@ -289,7 +289,7 @@ handleLoggedIn oidcenv handleSuccessfulId err mcode =
if emailVerified authInfo
then do
user <- liftIO $ handleSuccessfulId authInfo
either forbidden return user
either forbidden pure user
else forbidden "Please verify your email"
Nothing -> do
liftIO $ putText "No code param"
Expand Down Expand Up @@ -371,7 +371,7 @@ We need some helpers to generate random string for generating state and API Keys
genRandomBS :: IO ByteString
genRandomBS = do
g <- Random.newStdGen
Random.randomRs (0, n) g & take 42 & fmap toChar & readable 0 & toS & return
Random.randomRs (0, n) g & take 42 & fmap toChar & readable 0 & toS & pure
where
n = length letters - 1
toChar i = letters List.!! i
Expand All @@ -394,7 +394,7 @@ genRandomBS = do
customerFromAuthInfo :: AuthInfo -> IO Customer
customerFromAuthInfo authinfo = do
apikey <- genRandomBS
return Customer { account = toS (email authinfo)
pure Customer { account = toS (email authinfo)
, apiKey = apikey
, mail = Just (toS (email authinfo))
, fullname = Just (toS (name authinfo))
Expand All @@ -404,8 +404,8 @@ handleOIDCLogin :: LoginHandler
handleOIDCLogin authInfo = do
custInfo <- customerFromAuthInfo authInfo
if emailVerified authInfo
then return . Right . customerToUser $ custInfo
else return (Left "You emails is not verified by your provider. Please verify your email.")
then pure . Right . customerToUser $ custInfo
else pure (Left "You emails is not verified by your provider. Please verify your email.")
where
customerToUser :: Customer -> User
customerToUser c =
Expand Down
2 changes: 1 addition & 1 deletion doc/cookbook/sentry/Sentry.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ server = breakHandler
where breakHandler :: Handler ()
breakHandler = do
throw MyException
return ()
pure ()
```

First thing we need to do if we want to intercept and log this exception, we need to look in the section of our code where we run the `warp` application, and instead of using the simple `run` function from `warp`, we use the `runSettings` functions which allows to customise the handling of requests
Expand Down
18 changes: 9 additions & 9 deletions doc/cookbook/structuring-apis/StructuringApis.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ type that `Server FactoringAPI` "resolves to" by typing
factoringServer :: Server FactoringAPI
factoringServer x = getXY :<|> postX

where getXY Nothing = return x
getXY (Just y) = return (x + y)
where getXY Nothing = pure x
getXY (Just y) = pure (x + y)

postX = return (x - 1)
postX = pure (x - 1)
```

If you want to avoid the "nested types" and the need to manually
Expand Down Expand Up @@ -164,19 +164,19 @@ related sections of the API.
``` haskell
userServer :: Server (SimpleAPI "users" User UserId)
userServer = simpleServer
(return [])
(\userid -> return $
(pure [])
(\userid -> pure $
if userid == 0
then User "john" 64
else User "everybody else" 10
)
(\_user -> return NoContent)
(\_user -> pure NoContent)

productServer :: Server (SimpleAPI "products" Product ProductId)
productServer = simpleServer
(return [])
(\_productid -> return $ Product "Great stuff")
(\_product -> return NoContent)
(pure [])
(\_productid -> pure $ Product "Great stuff")
(\_product -> pure NoContent)
```

Finally, some dummy types and the serving part.
Expand Down
Loading
Loading