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
This commit is contained in:
parent
b9d8fbcdc1
commit
8e7b538921
5 changed files with 24 additions and 36 deletions
|
@ -86,7 +86,7 @@ listFromAPI :: (HasForeign lang ftype api, GenerateList ftype (Foreign ftype api
|
||||||
```
|
```
|
||||||
|
|
||||||
This looks a bit confusing...
|
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.
|
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`.
|
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 =
|
generateCurl p host =
|
||||||
fmap T.unlines body
|
fmap T.unlines body
|
||||||
where
|
where
|
||||||
body = foldr (\endp curlCalls -> mCons (generateEndpoint host endp) curlCalls) (return [])
|
body = mapM (generateEndpoint host)
|
||||||
$ listFromAPI (Proxy :: Proxy NoLang) (Proxy :: Proxy Mocked) p
|
$ listFromAPI (Proxy :: Proxy NoLang) (Proxy :: Proxy Mocked) p
|
||||||
```
|
```
|
||||||
|
|
||||||
To understand this function, better start at the end:
|
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.
|
||||||
`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:
|
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
generateEndpoint :: Text -> Req Mocked -> IO Text
|
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.
|
`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.
|
`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:
|
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
|
``` haskell
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main =
|
main =
|
||||||
generateCurl api "localhost:8081" >>= (\v -> T.IO.putStrLn v)
|
generateCurl api "localhost:8081" >>= T.IO.putStrLn
|
||||||
```
|
```
|
||||||
|
|
||||||
Done:
|
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.
|
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.
|
Also, we now know how to use `HasForeignType` and `listFromAPI` to generate anything we want.
|
||||||
|
|
|
@ -34,16 +34,16 @@ app = serve api server
|
||||||
```
|
```
|
||||||
|
|
||||||
It's now time to actually run the `Application`.
|
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
|
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)
|
[`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-3.2.4/docs/Network-Wai-Handler-WarpTLS.html#v:runTLSSocket).
|
and [`runTLSSocket`](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html#v:runTLSSocket).
|
||||||
We will be using the first one.
|
We will be using the first one.
|
||||||
|
|
||||||
It takes two arguments,
|
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)
|
(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).
|
(port, logger, etc).
|
||||||
|
|
||||||
We will be using very simple settings for this example but you are of
|
We will be using very simple settings for this example but you are of
|
||||||
|
|
|
@ -9,7 +9,7 @@ some login token would be saved in the user agent local storage.
|
||||||
Workflow:
|
Workflow:
|
||||||
|
|
||||||
1. user is presented with a login button,
|
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,
|
provider,
|
||||||
3. the user login in the OIDC provider,
|
3. the user login in the OIDC provider,
|
||||||
4. the OIDC provider will redirect the user and provide a `code`,
|
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
|
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
|
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
|
JWT containing all the information we need. Depending on the scopes we
|
||||||
asked we might get more information.
|
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.
|
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
|
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
|
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
|
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
|
endpoint of the OIDC provider. It's a POST that should contain the code
|
||||||
as well as the client id & secret.
|
as well as the client id and secret.
|
||||||
This is the role of the `requestTokens` to make this HTTP POST.
|
Making this HTTP POST is the responsibility of `requestTokens`.
|
||||||
|
|
||||||
From there we extract the `claims` of the JWT contained in one of the value
|
From there we extract the `claims` of the JWT contained in one of the value
|
||||||
of the JSON returned by the POST HTTP Request.
|
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.
|
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.
|
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`.
|
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
|
Those items should be set after a successful login when the user is redirected to
|
||||||
`/login/cb`.
|
`/login/cb`.
|
||||||
|
|
|
@ -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 configured Sentry service which we just created
|
||||||
- the name of the logger
|
- 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
|
- the message we want to send
|
||||||
- an update function to handle the specific `SentryRecord`
|
- an update function to handle the specific `SentryRecord`
|
||||||
|
|
||||||
|
|
|
@ -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
|
equivalent to a computation of type `IO (Either ServerError a)`, that is, an IO
|
||||||
action that either returns an error or a result.
|
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.
|
from which `ExceptT` comes is worth looking at.
|
||||||
Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so
|
Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so
|
||||||
`throwError` can be used to return an error from your handler (whereas `return`
|
`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
|
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.
|
(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:
|
is a class from the **transformers** package defined as:
|
||||||
|
|
||||||
``` haskell ignore
|
``` haskell ignore
|
||||||
|
|
Loading…
Reference in a new issue