From 8e7b538921bf32bef2f83bf15e4c229dd317a975 Mon Sep 17 00:00:00 2001 From: Jan Hrcek <2716069+jhrcek@users.noreply.github.com> Date: Wed, 10 Jun 2020 18:36:23 +0200 Subject: [PATCH] More cookbook improvements (#1305) * Simplify code in CurlMock cookbook recipe * Link to latest versions of packages on hackage * Fix grammar in the OpenIdConnect recipe * HasForeignType -> HasForeign --- doc/cookbook/curl-mock/CurlMock.lhs | 26 +++++-------------- doc/cookbook/https/Https.lhs | 10 +++---- .../open-id-connect/OpenIdConnect.lhs | 18 ++++++------- doc/cookbook/sentry/Sentry.lhs | 2 +- doc/tutorial/Server.lhs | 4 +-- 5 files changed, 24 insertions(+), 36 deletions(-) diff --git a/doc/cookbook/curl-mock/CurlMock.lhs b/doc/cookbook/curl-mock/CurlMock.lhs index e573993d..126349bf 100644 --- a/doc/cookbook/curl-mock/CurlMock.lhs +++ b/doc/cookbook/curl-mock/CurlMock.lhs @@ -86,7 +86,7 @@ listFromAPI :: (HasForeign lang ftype api, GenerateList ftype (Foreign ftype api ``` This looks a bit confusing... -[Here](https://hackage.haskell.org/package/servant-foreign-0.11.1/docs/Servant-Foreign.html#t:HasForeignType) is the documentation for the `HasForeign` typeclass. +[Here](https://hackage.haskell.org/package/servant-foreign/docs/Servant-Foreign.html#t:HasForeign) is the documentation for the `HasForeign` typeclass. We will not go into details here, but this allows us to create a value of type `ftype` for any type `a` in our API. In our case we want to create a mock of every type `a`. @@ -130,24 +130,12 @@ generateCurl :: (GenerateList Mocked (Foreign Mocked api), HasForeign NoLang Moc generateCurl p host = fmap T.unlines body where - body = foldr (\endp curlCalls -> mCons (generateEndpoint host endp) curlCalls) (return []) + body = mapM (generateEndpoint host) $ listFromAPI (Proxy :: Proxy NoLang) (Proxy :: Proxy Mocked) p ``` -To understand this function, better start at the end: - -`listFromAPI` gives us a list of endpoints. We iterate over them (`foldr`) and call `generateEndpoint` for every endpoint. - -As generate endpoint will not return `Text` but `IO Text` (remember we need some random bits to mock), we cannot just use the cons operator but need to build `IO [Text]` from `IO Text`s. - -``` haskell -mCons :: IO a -> IO [a] -> IO [a] -mCons ele list = - ele >>= \e -> list >>= \l -> return ( e : l ) -``` - - -Now comes the juicy part; accessing the endpoints data: +First, `listFromAPI` gives us a list of `Req Mocked`. Each `Req` describes one endpoint from the API type. +We generate a curl call for each of them using the following helper. ``` haskell generateEndpoint :: Text -> Req Mocked -> IO Text @@ -169,7 +157,7 @@ generateEndpoint host req = `servant-foreign` offers a multitude of lenses to be used with `Req`-values. `reqMethod` gives us a straigthforward `Network.HTTP.Types.Method`, `reqUrl` the url part and so on. -Just take a look at [the docs](https://hackage.haskell.org/package/servant-foreign-0.11.1/docs/Servant-Foreign.html). +Just take a look at [the docs](https://hackage.haskell.org/package/servant-foreign/docs/Servant-Foreign.html). But how do we get our mocked json string? This seems to be a bit to short to be true: @@ -201,7 +189,7 @@ And now, lets hook it all up in our main function: ``` haskell main :: IO () main = - generateCurl api "localhost:8081" >>= (\v -> T.IO.putStrLn v) + generateCurl api "localhost:8081" >>= T.IO.putStrLn ``` Done: @@ -213,6 +201,6 @@ curl -X POST -d '{"email":"wV򝣀_b򆎘:z񁊞򲙲!(3DM V","age":10,"name":"=|W"} ``` This is of course no complete curl call mock generator, many things including path arguments are missing. -But it correctly generate mock calls for simple POST requests. +But it correctly generates mock calls for simple POST requests. Also, we now know how to use `HasForeignType` and `listFromAPI` to generate anything we want. diff --git a/doc/cookbook/https/Https.lhs b/doc/cookbook/https/Https.lhs index de188463..6a95824d 100644 --- a/doc/cookbook/https/Https.lhs +++ b/doc/cookbook/https/Https.lhs @@ -34,16 +34,16 @@ app = serve api server ``` It's now time to actually run the `Application`. -The [`warp-tls`](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html) +The [`warp-tls`](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html) package provides two functions for running an `Application`, called -[`runTLS`](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html#v:runTLS) -and [`runTLSSocket`](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html#v:runTLSSocket). +[`runTLS`](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html#v:runTLS) +and [`runTLSSocket`](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html#v:runTLSSocket). We will be using the first one. It takes two arguments, -[the TLS settings](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html#t:TLSSettings) +[the TLS settings](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html#t:TLSSettings) (certificates, keys, ciphers, etc) -and [the warp settings](https://hackage.haskell.org/package/warp-3.2.12/docs/Network-Wai-Handler-Warp-Internal.html#t:Settings) +and [the warp settings](https://hackage.haskell.org/package/warp/docs/Network-Wai-Handler-Warp-Internal.html#t:Settings) (port, logger, etc). We will be using very simple settings for this example but you are of diff --git a/doc/cookbook/open-id-connect/OpenIdConnect.lhs b/doc/cookbook/open-id-connect/OpenIdConnect.lhs index 7db40280..6b4d0ca2 100644 --- a/doc/cookbook/open-id-connect/OpenIdConnect.lhs +++ b/doc/cookbook/open-id-connect/OpenIdConnect.lhs @@ -9,7 +9,7 @@ some login token would be saved in the user agent local storage. Workflow: 1. user is presented with a login button, -2. when the user click on the button it is redirected to the OIDC +2. when the user clicks on the button it is redirected to the OIDC provider, 3. the user login in the OIDC provider, 4. the OIDC provider will redirect the user and provide a `code`, @@ -221,7 +221,7 @@ The `AuthInfo` is about the infos we can grab from OIDC provider. To be more precise, the user should come with a `code` (a token) and POSTing that code to the correct OIDC provider endpoint should return a JSON -object. One of the field should be named `id_token` which should be a +object. One of the fields should be named `id_token` which should be a JWT containing all the information we need. Depending on the scopes we asked we might get more information. @@ -252,12 +252,12 @@ The `handleLoggedIn` is that part that will retrieve the information from the user once he is redirected from the OIDC Provider after login. If the user is redirected to the `redirect_uri` but with an `error` query -parameter then it means something goes wrong. +parameter then it means something went wrong. If there is no error query param but a `code` query param it means the user successfully logged in. From there we need to make a request to the token -endpoint of the OIDC provider. Its a POST that should contains the code -as well as the client id & secret. -This is the role of the `requestTokens` to make this HTTP POST. +endpoint of the OIDC provider. It's a POST that should contain the code +as well as the client id and secret. +Making this HTTP POST is the responsibility of `requestTokens`. From there we extract the `claims` of the JWT contained in one of the value of the JSON returned by the POST HTTP Request. @@ -329,12 +329,12 @@ data Customer = Customer { } ``` -Here is the code that display the homepage. +Here is the code that displays the homepage. It should contain a link to the the `/login` URL. -When the user will click on this link it will be redirected to Google login page +When the user clicks on this link it will be redirected to Google login page with some generated information. -The page also display the content of the local storage. +The page also displays the content of the local storage. And in particular the items `api-key` and `user-id`. Those items should be set after a successful login when the user is redirected to `/login/cb`. diff --git a/doc/cookbook/sentry/Sentry.lhs b/doc/cookbook/sentry/Sentry.lhs index ca221e7d..e2e41416 100644 --- a/doc/cookbook/sentry/Sentry.lhs +++ b/doc/cookbook/sentry/Sentry.lhs @@ -86,7 +86,7 @@ In the second step it actually sends our message to Sentry with the `register` f - the configured Sentry service which we just created - the name of the logger -- the error level (see [SentryLevel](https://hackage.haskell.org/package/raven-haskell-0.1.2.0/docs/System-Log-Raven-Types.html#t:SentryLevel) for the possible options) +- the error level (see [SentryLevel](https://hackage.haskell.org/package/raven-haskell/docs/System-Log-Raven-Types.html#t:SentryLevel) for the possible options) - the message we want to send - an update function to handle the specific `SentryRecord` diff --git a/doc/tutorial/Server.lhs b/doc/tutorial/Server.lhs index e7879172..61769a30 100644 --- a/doc/tutorial/Server.lhs +++ b/doc/tutorial/Server.lhs @@ -620,7 +620,7 @@ In short, this means that a handler of type `Handler a` is simply equivalent to a computation of type `IO (Either ServerError a)`, that is, an IO action that either returns an error or a result. -The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT) +The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl/docs/Control-Monad-Except.html#t:ExceptT) from which `ExceptT` comes is worth looking at. Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so `throwError` can be used to return an error from your handler (whereas `return` @@ -634,7 +634,7 @@ kind and abort early. The next two sections cover how to do just that. Other important instances from the list above are `MonadIO m => MonadIO (ExceptT e m)`, and therefore also `MonadIO Handler` as there is a `MonadIO IO` instance. -[`MonadIO`](http://hackage.haskell.org/package/transformers-0.4.3.0/docs/Control-Monad-IO-Class.html) +[`MonadIO`](http://hackage.haskell.org/package/base/docs/Control-Monad-IO-Class.html#t:MonadIO) is a class from the **transformers** package defined as: ``` haskell ignore