Fix typos and grammar (#1304)
* Fix typos and grammar * Remove redundant words, fix articles * More language fixes * More typo fixes and resolve TODO about missing links
This commit is contained in:
parent
67cb564aef
commit
b9d8fbcdc1
43 changed files with 83 additions and 80 deletions
|
@ -103,7 +103,7 @@ the `news` label if you make a new package so we can know about it!
|
||||||
|
|
||||||
## Release policy
|
## Release policy
|
||||||
|
|
||||||
We are currently moving to a more aggresive release policy, so that you can get
|
We are currently moving to a more aggressive release policy, so that you can get
|
||||||
what you contribute from Hackage fairly soon. However, note that prior to major
|
what you contribute from Hackage fairly soon. However, note that prior to major
|
||||||
releases it may take some time in between releases.
|
releases it may take some time in between releases.
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ See `CONTRIBUTING.md`
|
||||||
- It's a good idea to separate these steps, as tests often pass, if they compile :)
|
- It's a good idea to separate these steps, as tests often pass, if they compile :)
|
||||||
- See `cabal.project` to selectively `allow-newer`
|
- See `cabal.project` to selectively `allow-newer`
|
||||||
- If some packages are broken, on your discretisation there are two options:
|
- If some packages are broken, on your discretisation there are two options:
|
||||||
- Fix them and make PRs: it's good idea to test against older `servant` version too.
|
- Fix them and make PRs: it's a good idea to test against older `servant` version too.
|
||||||
- Temporarily comment out broken package
|
- Temporarily comment out broken package
|
||||||
- If you make a commit for `servant-universe`, you can use it as submodule in private projects to test even more
|
- If you make a commit for `servant-universe`, you can use it as submodule in private projects to test even more
|
||||||
- When ripples are cleared out:
|
- When ripples are cleared out:
|
||||||
|
@ -60,7 +60,7 @@ constraints:
|
||||||
troublemaker <13.37 && > 13.37
|
troublemaker <13.37 && > 13.37
|
||||||
```
|
```
|
||||||
|
|
||||||
## TechEmpower framework bechmarks
|
## TechEmpower framework benchmarks
|
||||||
|
|
||||||
We develop and maintain the servant TFB entry in https://github.com/haskell-servant/FrameworkBenchmarks/
|
We develop and maintain the servant TFB entry in https://github.com/haskell-servant/FrameworkBenchmarks/
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,10 @@ In other words, without streaming libraries.
|
||||||
- Some basic usage doesn't require usage of streaming libraries,
|
- Some basic usage doesn't require usage of streaming libraries,
|
||||||
like `conduit`, `pipes`, `machines` or `streaming`.
|
like `conduit`, `pipes`, `machines` or `streaming`.
|
||||||
We have bindings for them though.
|
We have bindings for them though.
|
||||||
- This is similar example file, which is bundled with each of the packages (TODO: links)
|
- 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
|
- `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).
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Generating mock curl calls
|
# Generating mock curl calls
|
||||||
|
|
||||||
In this example we will generate curl requests with mock post data from a servant API.
|
In this example we will generate curl requests with mock post data from a servant API.
|
||||||
This may be usefull for testing and development purposes.
|
This may be useful for testing and development purposes.
|
||||||
Especially post requests with a request body are tedious to send manually.
|
Especially post requests with a request body are tedious to send manually.
|
||||||
|
|
||||||
Also, we will learn how to use the servant-foreign library to generate stuff from servant APIs.
|
Also, we will learn how to use the servant-foreign library to generate stuff from servant APIs.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
This doc will walk through a single-module implementation of a servant API connecting to a MySQL database. It will also include some basic CRUD operations.
|
This doc will walk through a single-module implementation of a servant API connecting to a MySQL database. It will also include some basic CRUD operations.
|
||||||
|
|
||||||
Once you can wrap your head around this implemenation, understanding more complex features like resource pools would be beneficial next steps.
|
Once you can wrap your head around this implementation, understanding more complex features like resource pools would be beneficial next steps.
|
||||||
|
|
||||||
The only *prerequisite* is that you have a MySQL database open on port 3306 of your machine. Docker is an easy way to manage this.
|
The only *prerequisite* is that you have a MySQL database open on port 3306 of your machine. Docker is an easy way to manage this.
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,8 @@ startServer = run 8080 (serve api upload)
|
||||||
|
|
||||||
Finally, a main function that brings up our server and
|
Finally, a main function that brings up our server and
|
||||||
sends some test request with `http-client` (and not
|
sends some test request with `http-client` (and not
|
||||||
servant-client this time, has servant-multipart does not
|
servant-client this time, as servant-multipart does not
|
||||||
yet have support for client generation.
|
yet have support for client generation).
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
|
|
|
@ -43,13 +43,13 @@ api :: Proxy (ToServantApi Routes)
|
||||||
api = genericApi (Proxy :: Proxy Routes)
|
api = genericApi (Proxy :: Proxy Routes)
|
||||||
```
|
```
|
||||||
|
|
||||||
It's recommented to use `genericApi` function, as then you'll get
|
It's recommended to use `genericApi` function, as then you'll get
|
||||||
better error message, for example if you forget to `derive Generic`.
|
better error message, for example if you forget to `derive Generic`.
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
The clear advantage of record-based generics approach, is that
|
The clear advantage of record-based generics approach, is that
|
||||||
we can get safe links very conviently. We don't need to define endpoint types,
|
we can get safe links very conveniently. We don't need to define endpoint types,
|
||||||
as field accessors work as proxies:
|
as field accessors work as proxies:
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
|
@ -67,7 +67,7 @@ routesLinks = allFieldLinks
|
||||||
## Client
|
## Client
|
||||||
|
|
||||||
Even more power starts to show when we generate a record of client functions.
|
Even more power starts to show when we generate a record of client functions.
|
||||||
Here we use `genericClientHoist` function, which let us simultaneously
|
Here we use `genericClientHoist` function, which lets us simultaneously
|
||||||
hoist the monad, in this case from `ClientM` to `IO`.
|
hoist the monad, in this case from `ClientM` to `IO`.
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
|
|
|
@ -254,7 +254,7 @@ loginHandler cookieSettings jwtSettings form = do
|
||||||
liftIO $ pushLogStrLn logset $ toLogStr logMsg
|
liftIO $ pushLogStrLn logset $ toLogStr logMsg
|
||||||
throwError err401
|
throwError err401
|
||||||
Just applyCookies -> do
|
Just applyCookies -> do
|
||||||
let successMsg = logMsg{message = "AdminUser succesfully authenticated!"}
|
let successMsg = logMsg{message = "AdminUser successfully authenticated!"}
|
||||||
liftIO $ pushLogStrLn logset $ toLogStr successMsg
|
liftIO $ pushLogStrLn logset $ toLogStr successMsg
|
||||||
pure $ applyCookies successMsg
|
pure $ applyCookies successMsg
|
||||||
loginHandler _ _ _ = throwError err401
|
loginHandler _ _ _ = throwError err401
|
||||||
|
|
|
@ -8,7 +8,7 @@ some login token would be saved in the user agent local storage.
|
||||||
|
|
||||||
Workflow:
|
Workflow:
|
||||||
|
|
||||||
1. user is presentend 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 click 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,
|
||||||
|
@ -222,8 +222,8 @@ 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 field should be named `id_token` which should be a
|
||||||
JWT containing all the informations we need. Depending on the scopes we
|
JWT containing all the information we need. Depending on the scopes we
|
||||||
asked we might get more informations.
|
asked we might get more information.
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
-- | @AuthInfo@
|
-- | @AuthInfo@
|
||||||
|
@ -248,13 +248,13 @@ instance JSON.ToJSON AuthInfo where
|
||||||
type LoginHandler = AuthInfo -> IO (Either Text User)
|
type LoginHandler = AuthInfo -> IO (Either Text User)
|
||||||
```
|
```
|
||||||
|
|
||||||
The `handleLoggedIn` is that part that will retrieve the informations from
|
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 goes 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
|
||||||
sucessfully 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. Its a POST that should contains the code
|
||||||
as well as the client id & secret.
|
as well as the client id & secret.
|
||||||
This is the role of the `requestTokens` to make this HTTP POST.
|
This is the role of the `requestTokens` to make this HTTP POST.
|
||||||
|
@ -332,7 +332,7 @@ data Customer = Customer {
|
||||||
Here is the code that display the homepage.
|
Here is the code that display 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 will click on this link it will be redirected to Google login page
|
||||||
with some generated informations.
|
with some generated information.
|
||||||
|
|
||||||
The page also display the content of the local storage.
|
The page also display 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`.
|
||||||
|
@ -366,7 +366,7 @@ instance ToMarkup Homepage where
|
||||||
We need some helpers to generate random string for generating state and API Keys.
|
We need some helpers to generate random string for generating state and API Keys.
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
-- | generate a random Bystestring, not necessarily extremely good randomness
|
-- | generate a random ByteString, not necessarily extremely good randomness
|
||||||
-- still the password will be long enough to be very difficult to crack
|
-- still the password will be long enough to be very difficult to crack
|
||||||
genRandomBS :: IO ByteString
|
genRandomBS :: IO ByteString
|
||||||
genRandomBS = do
|
genRandomBS = do
|
||||||
|
|
|
@ -18,7 +18,7 @@ For example: `Range: createdAt 2017-01-15T23:14:67.000Z; offset 5; order desc` i
|
||||||
the client is willing to retrieve the next batch of document in descending order that were
|
the client is willing to retrieve the next batch of document in descending order that were
|
||||||
created after the fifteenth of January, skipping the first 5.
|
created after the fifteenth of January, skipping the first 5.
|
||||||
|
|
||||||
As a response, the server may return the list of corresponding document, and augment the
|
As a response, the server may return the list of corresponding documents, and augment the
|
||||||
response with 3 headers:
|
response with 3 headers:
|
||||||
|
|
||||||
- `Accept-Ranges`: A comma-separated list of fields upon which a range can be defined
|
- `Accept-Ranges`: A comma-separated list of fields upon which a range can be defined
|
||||||
|
@ -127,7 +127,7 @@ defaultRange =
|
||||||
getDefaultRange (Proxy @Color)
|
getDefaultRange (Proxy @Color)
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that `getFieldValue :: Proxy "name" -> Color -> String` is the minimal complete definintion
|
Note that `getFieldValue :: Proxy "name" -> Color -> String` is the minimal complete definition
|
||||||
of the class. Yet, you can define `getRangeOptions` to provide different parsing options (see
|
of the class. Yet, you can define `getRangeOptions` to provide different parsing options (see
|
||||||
the last section of this guide). In the meantime, we've also defined a `defaultRange` as it will
|
the last section of this guide). In the meantime, we've also defined a `defaultRange` as it will
|
||||||
come in handy when defining our handler.
|
come in handy when defining our handler.
|
||||||
|
@ -148,7 +148,7 @@ type MyHeaders =
|
||||||
```
|
```
|
||||||
|
|
||||||
`PageHeaders` is a type alias provided by the library to declare the necessary response headers
|
`PageHeaders` is a type alias provided by the library to declare the necessary response headers
|
||||||
we mentionned in introduction. Expanding the alias boils down to the following:
|
we mentioned in introduction. Expanding the alias boils down to the following:
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
-- type MyHeaders =
|
-- type MyHeaders =
|
||||||
|
@ -165,7 +165,7 @@ not, _servant-pagination_ provides an easy way to lift a collection of resources
|
||||||
#### Server
|
#### Server
|
||||||
|
|
||||||
Time to connect the last bits by defining the server implementation of our colorful API. The `Ranges`
|
Time to connect the last bits by defining the server implementation of our colorful API. The `Ranges`
|
||||||
type we've defined above (tight to the `Range` HTTP header) indicates the server to parse any `Range`
|
type we've defined above (tied to the `Range` HTTP header) indicates the server to parse any `Range`
|
||||||
header, looking for the format defined in introduction with fields and target types we have just declared.
|
header, looking for the format defined in introduction with fields and target types we have just declared.
|
||||||
If no such header is provided, we will end up receiving `Nothing`. Otherwise, it will be possible
|
If no such header is provided, we will end up receiving `Nothing`. Otherwise, it will be possible
|
||||||
to _extract_ a `Range` from our `Ranges`.
|
to _extract_ a `Range` from our `Ranges`.
|
||||||
|
@ -192,7 +192,7 @@ the format we defined, where `<field>` here can only be `name` and `<value>` mus
|
||||||
- `Range: <field> [<value>][; offset <o>][; limit <l>][; order <asc|desc>]`
|
- `Range: <field> [<value>][; offset <o>][; limit <l>][; order <asc|desc>]`
|
||||||
|
|
||||||
Beside the target field, everything is pretty much optional in the `Range` HTTP header. Missing parts
|
Beside the target field, everything is pretty much optional in the `Range` HTTP header. Missing parts
|
||||||
are deducted from the `RangeOptions` that are part of the `HasPagination` instance. Therefore, all
|
are deduced from the `RangeOptions` that are part of the `HasPagination` instance. Therefore, all
|
||||||
following examples are valid requests to send to our server:
|
following examples are valid requests to send to our server:
|
||||||
|
|
||||||
- 1 - `curl http://localhost:1442/colors -vH 'Range: name'`
|
- 1 - `curl http://localhost:1442/colors -vH 'Range: name'`
|
||||||
|
@ -219,7 +219,7 @@ The previous ranges reads as follows:
|
||||||
Note that in the simple above scenario, there's no ambiguity with `extractRange` and `returnRange`
|
Note that in the simple above scenario, there's no ambiguity with `extractRange` and `returnRange`
|
||||||
because there's only one possible `Range` defined on our resource. Yet, as you've most probably
|
because there's only one possible `Range` defined on our resource. Yet, as you've most probably
|
||||||
noticed, the `Ranges` combinator accepts a list of fields, each of which must declare a `HasPagination`
|
noticed, the `Ranges` combinator accepts a list of fields, each of which must declare a `HasPagination`
|
||||||
instance. Doing so will make the other helper functions more ambiguous and type annotation are
|
instance. Doing so will make the other helper functions more ambiguous and type annotations are
|
||||||
highly likely to be needed.
|
highly likely to be needed.
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,8 +235,8 @@ instance HasPagination Color "hex" where
|
||||||
#### Parsing Options
|
#### Parsing Options
|
||||||
|
|
||||||
By default, `servant-pagination` provides an implementation of `getRangeOptions` for each
|
By default, `servant-pagination` provides an implementation of `getRangeOptions` for each
|
||||||
`HasPagination` instance. However, this can be overwritten when defining the instance to provide
|
`HasPagination` instance. However, this can be overridden when defining the instance to provide
|
||||||
your own options. This options come into play when a `Range` header is received and isn't fully
|
your own options. These options come into play when a `Range` header is received and isn't fully
|
||||||
specified (`limit`, `offset`, `order` are all optional) to provide default fallback values for those.
|
specified (`limit`, `offset`, `order` are all optional) to provide default fallback values for those.
|
||||||
|
|
||||||
For instance, let's say we wanted to change the default limit to `5` in a new range on
|
For instance, let's say we wanted to change the default limit to `5` in a new range on
|
||||||
|
|
|
@ -79,7 +79,7 @@ It does three things. First it initializes the service which will communicate wi
|
||||||
|
|
||||||
- the Sentry `DSN`, which is obtained when creating a new project on Sentry
|
- the Sentry `DSN`, which is obtained when creating a new project on Sentry
|
||||||
- a default way to update sentry fields, where we use the identity function
|
- a default way to update sentry fields, where we use the identity function
|
||||||
- an event trasport, which generally would be `sendRecord`, an HTTPS capable trasport which uses http-conduit
|
- an event transport, which generally would be `sendRecord`, an HTTPS capable transport which uses http-conduit
|
||||||
- a fallback handler, which we choose to be `silentFallback` since later we are logging to the console anyway.
|
- a fallback handler, which we choose to be `silentFallback` since later we are logging to the console anyway.
|
||||||
|
|
||||||
In the second step it actually sends our message to Sentry with the `register` function. Its arguments are:
|
In the second step it actually sends our message to Sentry with the `register` function. Its arguments are:
|
||||||
|
|
|
@ -144,7 +144,7 @@ simpleAPIServer
|
||||||
:: m [a]
|
:: m [a]
|
||||||
-> (i -> m a)
|
-> (i -> m a)
|
||||||
-> (a -> m NoContent)
|
-> (a -> m NoContent)
|
||||||
-> Server (SimpleAPI name a i) m
|
-> ServerT (SimpleAPI name a i) m
|
||||||
simpleAPIServer listAs getA postA =
|
simpleAPIServer listAs getA postA =
|
||||||
listAs :<|> getA :<|> postA
|
listAs :<|> getA :<|> postA
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Using a custom monad
|
# Using a custom monad
|
||||||
|
|
||||||
In this section we will create and API for a book shelf without any backing DB storage.
|
In this section we will create an API for a book shelf without any backing DB storage.
|
||||||
We will keep state in memory and share it between requests using `Reader` monad and `STM`.
|
We will keep state in memory and share it between requests using `Reader` monad and `STM`.
|
||||||
|
|
||||||
We start with a pretty standard set of imports and definition of the model:
|
We start with a pretty standard set of imports and definition of the model:
|
||||||
|
|
|
@ -141,7 +141,7 @@ and calling the continuation. We should get a `Pure` value.
|
||||||
Pure n ->
|
Pure n ->
|
||||||
putStrLn $ "Expected 1764, got " ++ show n
|
putStrLn $ "Expected 1764, got " ++ show n
|
||||||
_ ->
|
_ ->
|
||||||
putStrLn "ERROR: didn't got a response"
|
putStrLn "ERROR: didn't get a response"
|
||||||
```
|
```
|
||||||
|
|
||||||
So that's it. Using `Free` we can evaluate servant clients step-by-step, and
|
So that's it. Using `Free` we can evaluate servant clients step-by-step, and
|
||||||
|
|
|
@ -108,7 +108,7 @@ API with "private." Additionally, the private parts of our API use the
|
||||||
realm for this authentication is `"foo-realm"`).
|
realm for this authentication is `"foo-realm"`).
|
||||||
|
|
||||||
Unfortunately we're not done. When someone makes a request to our `"private"`
|
Unfortunately we're not done. When someone makes a request to our `"private"`
|
||||||
API, we're going to need to provide to servant the logic for validifying
|
API, we're going to need to provide to servant the logic for validating
|
||||||
usernames and passwords. This adds a certain conceptual wrinkle in servant's
|
usernames and passwords. This adds a certain conceptual wrinkle in servant's
|
||||||
design that we'll briefly discuss. If you want the **TL;DR**: we supply a lookup
|
design that we'll briefly discuss. If you want the **TL;DR**: we supply a lookup
|
||||||
function to servant's new `Context` primitive.
|
function to servant's new `Context` primitive.
|
||||||
|
@ -260,7 +260,7 @@ this.
|
||||||
|
|
||||||
Let's implement a trivial authentication scheme. We will protect our API by
|
Let's implement a trivial authentication scheme. We will protect our API by
|
||||||
looking for a cookie named `"servant-auth-cookie"`. This cookie's value will
|
looking for a cookie named `"servant-auth-cookie"`. This cookie's value will
|
||||||
contain a key from which we can lookup a `Account`.
|
contain a key from which we can lookup an `Account`.
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
-- | An account type that we "fetch from the database" after
|
-- | An account type that we "fetch from the database" after
|
||||||
|
@ -274,7 +274,7 @@ database = fromList [ ("key1", Account "Anne Briggs")
|
||||||
, ("key3", Account "Ghédalia Tazartès")
|
, ("key3", Account "Ghédalia Tazartès")
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | A method that, when given a password, will return a Account.
|
-- | A method that, when given a password, will return an Account.
|
||||||
-- This is our bespoke (and bad) authentication logic.
|
-- This is our bespoke (and bad) authentication logic.
|
||||||
lookupAccount :: ByteString -> Handler Account
|
lookupAccount :: ByteString -> Handler Account
|
||||||
lookupAccount key = case Map.lookup key database of
|
lookupAccount key = case Map.lookup key database of
|
||||||
|
@ -346,7 +346,7 @@ genAuthServerContext = authHandler :. EmptyContext
|
||||||
|
|
||||||
-- | Our API, where we provide all the author-supplied handlers for each end
|
-- | Our API, where we provide all the author-supplied handlers for each end
|
||||||
-- point. Note that 'privateDataFunc' is a function that takes 'Account' as an
|
-- point. Note that 'privateDataFunc' is a function that takes 'Account' as an
|
||||||
-- argument. We dont' worry about the authentication instrumentation here,
|
-- argument. We don't worry about the authentication instrumentation here,
|
||||||
-- that is taken care of by supplying context
|
-- that is taken care of by supplying context
|
||||||
genAuthServer :: Server AuthGenAPI
|
genAuthServer :: Server AuthGenAPI
|
||||||
genAuthServer =
|
genAuthServer =
|
||||||
|
@ -385,11 +385,11 @@ Creating a generalized, ad-hoc authentication scheme was fairly straight
|
||||||
forward:
|
forward:
|
||||||
|
|
||||||
1. use the `AuthProtect` combinator to protect your API.
|
1. use the `AuthProtect` combinator to protect your API.
|
||||||
2. choose a application-specific data type used by your server when
|
2. choose an application-specific data type used by your server when
|
||||||
authentication is successful (in our case this was `Account`).
|
authentication is successful (in our case this was `Account`).
|
||||||
3. Create a value of `AuthHandler Request Account` which encapsulates the
|
3. Create a value of `AuthHandler Request Account` which encapsulates the
|
||||||
authentication logic (`Request -> Handler Account`). This function
|
authentication logic (`Request -> Handler Account`). This function
|
||||||
will be executed everytime a request matches a protected route.
|
will be executed every time a request matches a protected route.
|
||||||
4. Provide an instance of the `AuthServerData` type family, specifying your
|
4. Provide an instance of the `AuthServerData` type family, specifying your
|
||||||
application-specific data type returned when authentication is successful (in
|
application-specific data type returned when authentication is successful (in
|
||||||
our case this was `Account`).
|
our case this was `Account`).
|
||||||
|
|
|
@ -161,7 +161,7 @@ The types of the arguments for the functions are the same as for (server-side) r
|
||||||
## Changing the monad the client functions live in
|
## Changing the monad the client functions live in
|
||||||
|
|
||||||
Just like `hoistServer` allows us to change the monad in which request handlers
|
Just like `hoistServer` allows us to change the monad in which request handlers
|
||||||
of a web application live in, we also have `hoistClient` for changing the monad
|
of a web application live, we also have `hoistClient` for changing the monad
|
||||||
in which _client functions_ live. Consider the following trivial API:
|
in which _client functions_ live. Consider the following trivial API:
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
|
@ -173,7 +173,7 @@ hoistClientAPI = Proxy
|
||||||
|
|
||||||
We already know how to derive client functions for this API, and as we have
|
We already know how to derive client functions for this API, and as we have
|
||||||
seen above they all return results in the `ClientM` monad when using `servant-client`.
|
seen above they all return results in the `ClientM` monad when using `servant-client`.
|
||||||
However, `ClientM` rarely (or never) is the actual monad we need to use the client
|
However, `ClientM` is rarely (or never) the actual monad we need to use the client
|
||||||
functions in. Sometimes we need to run them in IO, sometimes in a custom monad
|
functions in. Sometimes we need to run them in IO, sometimes in a custom monad
|
||||||
stack. `hoistClient` is a very simple solution to the problem of "changing" the monad
|
stack. `hoistClient` is a very simple solution to the problem of "changing" the monad
|
||||||
the clients run in.
|
the clients run in.
|
||||||
|
|
|
@ -77,7 +77,7 @@ instance ToSample HelloMessage where
|
||||||
[ ("When a value is provided for 'name'", HelloMessage "Hello, Alp")
|
[ ("When a value is provided for 'name'", HelloMessage "Hello, Alp")
|
||||||
, ("When 'name' is not specified", HelloMessage "Hello, anonymous coward")
|
, ("When 'name' is not specified", HelloMessage "Hello, anonymous coward")
|
||||||
]
|
]
|
||||||
-- mutliple examples to display this time
|
-- multiple examples to display this time
|
||||||
|
|
||||||
ci :: ClientInfo
|
ci :: ClientInfo
|
||||||
ci = ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"]
|
ci = ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"]
|
||||||
|
@ -108,7 +108,7 @@ apiDocs = docs exampleAPI
|
||||||
markdown :: API -> String
|
markdown :: API -> String
|
||||||
```
|
```
|
||||||
|
|
||||||
That lets us see what our API docs look down in markdown, by looking at `markdown apiDocs`.
|
That lets us see what our API docs look like in markdown, by looking at `markdown apiDocs`.
|
||||||
|
|
||||||
````````` text
|
````````` text
|
||||||
## GET /hello
|
## GET /hello
|
||||||
|
|
|
@ -228,13 +228,13 @@ data CommonGeneratorOptions = CommonGeneratorOptions
|
||||||
{
|
{
|
||||||
-- | function generating function names
|
-- | function generating function names
|
||||||
functionNameBuilder :: FunctionName -> Text
|
functionNameBuilder :: FunctionName -> Text
|
||||||
-- | name used when a user want to send the request body (to let you redefine it)
|
-- | name used when a user wants to send the request body (to let you redefine it)
|
||||||
, requestBody :: Text
|
, requestBody :: Text
|
||||||
-- | name of the callback parameter when the request was successful
|
-- | name of the callback parameter when the request was successful
|
||||||
, successCallback :: Text
|
, successCallback :: Text
|
||||||
-- | name of the callback parameter when the request reported an error
|
-- | name of the callback parameter when the request reported an error
|
||||||
, errorCallback :: Text
|
, errorCallback :: Text
|
||||||
-- | namespace on which we define the js function (empty mean local var)
|
-- | namespace on which we define the js function (empty means local var)
|
||||||
, moduleName :: Text
|
, moduleName :: Text
|
||||||
-- | a prefix that should be prepended to the URL in the generated JS
|
-- | a prefix that should be prepended to the URL in the generated JS
|
||||||
, urlPrefix :: Text
|
, urlPrefix :: Text
|
||||||
|
|
|
@ -183,7 +183,7 @@ users2 = [isaac, albert]
|
||||||
|
|
||||||
Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
|
Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
|
||||||
are going to separate the handlers with `:<|>` too! They must be provided in
|
are going to separate the handlers with `:<|>` too! They must be provided in
|
||||||
the same order as in in the API type.
|
the same order as in the API type.
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
server2 :: Server UserAPI2
|
server2 :: Server UserAPI2
|
||||||
|
@ -716,7 +716,7 @@ $ curl --verbose http://localhost:8081/myfile.txt
|
||||||
>
|
>
|
||||||
< HTTP/1.1 404 Not Found
|
< HTTP/1.1 404 Not Found
|
||||||
[snip]
|
[snip]
|
||||||
myfile.txt just isnt there, please leave this server alone.
|
myfile.txt just isn't there, please leave this server alone.
|
||||||
|
|
||||||
$ echo Hello > myfile.txt
|
$ echo Hello > myfile.txt
|
||||||
|
|
||||||
|
@ -818,7 +818,7 @@ If it doesn't exist, the handler will fail with a `404` status code.
|
||||||
|
|
||||||
`serveDirectoryWebApp` uses some standard settings that fit the use case of
|
`serveDirectoryWebApp` uses some standard settings that fit the use case of
|
||||||
serving static files for most web apps. You can find out about the other
|
serving static files for most web apps. You can find out about the other
|
||||||
options in the documentation of the `Servant.Utils.StaticFiles` module.
|
options in the documentation of the `Servant.Server.StaticFiles` module.
|
||||||
|
|
||||||
## Nested APIs
|
## Nested APIs
|
||||||
|
|
||||||
|
@ -1135,7 +1135,7 @@ true
|
||||||
### An arrow is a reader too.
|
### An arrow is a reader too.
|
||||||
|
|
||||||
In previous versions of `servant` we had an `enter` to do what `hoistServer`
|
In previous versions of `servant` we had an `enter` to do what `hoistServer`
|
||||||
does now. `enter` had a ambitious design goals, but was problematic in practice.
|
does now. `enter` had an ambitious design goals, but was problematic in practice.
|
||||||
|
|
||||||
One problematic situation was when the source monad was `(->) r`, yet it's
|
One problematic situation was when the source monad was `(->) r`, yet it's
|
||||||
handy in practice, because `(->) r` is isomorphic to `Reader r`.
|
handy in practice, because `(->) r` is isomorphic to `Reader r`.
|
||||||
|
@ -1166,7 +1166,7 @@ back a *stream* of results, served one at a time. Stream endpoints only provide
|
||||||
a single content type, and also specify what framing strategy is used to
|
a single content type, and also specify what framing strategy is used to
|
||||||
delineate the results. To serve these results, we need to give back a stream
|
delineate the results. To serve these results, we need to give back a stream
|
||||||
producer. Adapters can be written to *Pipes*, *Conduit* and the like, or
|
producer. Adapters can be written to *Pipes*, *Conduit* and the like, or
|
||||||
written directly as `SourceIO`s. SourceIO builts upon servant's own `SourceT`
|
written directly as `SourceIO`s. SourceIO builds upon servant's own `SourceT`
|
||||||
stream type (it's simpler than *Pipes* or *Conduit*).
|
stream type (it's simpler than *Pipes* or *Conduit*).
|
||||||
The API of a streaming endpoint needs to explicitly specify which sort of
|
The API of a streaming endpoint needs to explicitly specify which sort of
|
||||||
generator it produces. Note that the generator itself is returned by a
|
generator it produces. Note that the generator itself is returned by a
|
||||||
|
|
|
@ -205,7 +205,7 @@
|
||||||
|
|
||||||
- *servant-client-core* Add `hoistClient` to `HasClient`.
|
- *servant-client-core* Add `hoistClient` to `HasClient`.
|
||||||
Just like `hoistServer` allows us to change the monad in which request handlers
|
Just like `hoistServer` allows us to change the monad in which request handlers
|
||||||
of a web application live in, we also have `hoistClient` for changing the monad
|
of a web application live, we also have `hoistClient` for changing the monad
|
||||||
in which *client functions* live.
|
in which *client functions* live.
|
||||||
Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
|
Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
|
||||||
([#936](https://github.com/haskell-servant/servant/pull/936))
|
([#936](https://github.com/haskell-servant/servant/pull/936))
|
||||||
|
|
|
@ -104,7 +104,7 @@ test-suite spec
|
||||||
, base-compat
|
, base-compat
|
||||||
, servant-client-core
|
, servant-client-core
|
||||||
|
|
||||||
-- Additonal dependencies
|
-- Additional dependencies
|
||||||
build-depends:
|
build-depends:
|
||||||
deepseq >= 1.4.2.0 && < 1.5
|
deepseq >= 1.4.2.0 && < 1.5
|
||||||
, hspec >= 2.6.0 && < 2.8
|
, hspec >= 2.6.0 && < 2.8
|
||||||
|
|
|
@ -198,7 +198,7 @@
|
||||||
|
|
||||||
- *servant-client-core* Add `hoistClient` to `HasClient`.
|
- *servant-client-core* Add `hoistClient` to `HasClient`.
|
||||||
Just like `hoistServer` allows us to change the monad in which request handlers
|
Just like `hoistServer` allows us to change the monad in which request handlers
|
||||||
of a web application live in, we also have `hoistClient` for changing the monad
|
of a web application live, we also have `hoistClient` for changing the monad
|
||||||
in which *client functions* live.
|
in which *client functions* live.
|
||||||
Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
|
Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
|
||||||
([#936](https://github.com/haskell-servant/servant/pull/936))
|
([#936](https://github.com/haskell-servant/servant/pull/936))
|
||||||
|
|
|
@ -120,7 +120,7 @@ test-suite spec
|
||||||
, wai
|
, wai
|
||||||
, warp
|
, warp
|
||||||
|
|
||||||
-- Additonal dependencies
|
-- Additional dependencies
|
||||||
build-depends:
|
build-depends:
|
||||||
entropy >= 0.4.1.3 && < 0.5
|
entropy >= 0.4.1.3 && < 0.5
|
||||||
, hspec >= 2.6.0 && < 2.8
|
, hspec >= 2.6.0 && < 2.8
|
||||||
|
|
|
@ -130,7 +130,7 @@ withClientM cm env k =
|
||||||
-- streaming response types ('SourceT', 'Conduit', pipes 'Proxy' or 'Machine').
|
-- streaming response types ('SourceT', 'Conduit', pipes 'Proxy' or 'Machine').
|
||||||
-- For those you have to use 'withClientM'.
|
-- For those you have to use 'withClientM'.
|
||||||
--
|
--
|
||||||
-- /Note:/ we 'force' the result, so the likehood of accidentally leaking a
|
-- /Note:/ we 'force' the result, so the likelihood of accidentally leaking a
|
||||||
-- connection is smaller. Use with care.
|
-- connection is smaller. Use with care.
|
||||||
--
|
--
|
||||||
runClientM :: NFData a => ClientM a -> ClientEnv -> IO (Either ClientError a)
|
runClientM :: NFData a => ClientM a -> ClientEnv -> IO (Either ClientError a)
|
||||||
|
|
|
@ -57,7 +57,7 @@ You'll also note that multiple intros are possible.
|
||||||
|
|
||||||
This is some text
|
This is some text
|
||||||
|
|
||||||
### Second secton
|
### Second section
|
||||||
|
|
||||||
And some more
|
And some more
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ test-suite spec
|
||||||
, servant-docs
|
, servant-docs
|
||||||
, string-conversions
|
, string-conversions
|
||||||
|
|
||||||
-- Additonal dependencies
|
-- Additional dependencies
|
||||||
build-depends:
|
build-depends:
|
||||||
tasty >= 1.1.0.4 && < 1.3,
|
tasty >= 1.1.0.4 && < 1.3,
|
||||||
tasty-golden >= 2.3.2 && < 2.4,
|
tasty-golden >= 2.3.2 && < 2.4,
|
||||||
|
|
|
@ -396,7 +396,7 @@ docsWithOptions p = docsFor p (defEndpoint, defAction)
|
||||||
-- > extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete)) $
|
-- > extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete)) $
|
||||||
-- > defAction & headers <>~ ["unicorns"]
|
-- > defAction & headers <>~ ["unicorns"]
|
||||||
-- > & notes <>~ [ DocNote "Title" ["This is some text"]
|
-- > & notes <>~ [ DocNote "Title" ["This is some text"]
|
||||||
-- > , DocNote "Second secton" ["And some more"]
|
-- > , DocNote "Second section" ["And some more"]
|
||||||
-- > ]
|
-- > ]
|
||||||
|
|
||||||
extraInfo :: (IsIn endpoint api, HasLink endpoint, HasDocs endpoint)
|
extraInfo :: (IsIn endpoint api, HasLink endpoint, HasDocs endpoint)
|
||||||
|
|
|
@ -77,7 +77,7 @@ test-suite spec
|
||||||
, servant
|
, servant
|
||||||
, servant-foreign
|
, servant-foreign
|
||||||
|
|
||||||
-- Additonal dependencies
|
-- Additional dependencies
|
||||||
build-depends:
|
build-depends:
|
||||||
hspec >= 2.6.0 && <2.8
|
hspec >= 2.6.0 && <2.8
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ listFromAPISpec = describe "listFromAPI" $ do
|
||||||
shouldBe putReq $ defReq
|
shouldBe putReq $ defReq
|
||||||
{ _reqUrl = Url
|
{ _reqUrl = Url
|
||||||
[ Segment $ Static "test" ]
|
[ Segment $ Static "test" ]
|
||||||
-- Shoud this be |intX| or |listX of intX| ?
|
-- Should this be |intX| or |listX of intX| ?
|
||||||
[ QueryArg (Arg "params" "listX of intX") List ]
|
[ QueryArg (Arg "params" "listX of intX") List ]
|
||||||
, _reqMethod = "PUT"
|
, _reqMethod = "PUT"
|
||||||
, _reqHeaders = []
|
, _reqHeaders = []
|
||||||
|
|
|
@ -114,7 +114,7 @@ test-suite spec
|
||||||
, wai
|
, wai
|
||||||
, warp
|
, warp
|
||||||
|
|
||||||
-- Additonal dependencies
|
-- Additional dependencies
|
||||||
build-depends:
|
build-depends:
|
||||||
entropy >= 0.4.1.3 && < 0.5
|
entropy >= 0.4.1.3 && < 0.5
|
||||||
, hspec >= 2.6.0 && < 2.8
|
, hspec >= 2.6.0 && < 2.8
|
||||||
|
|
|
@ -77,7 +77,7 @@ _ = client comprehensiveAPIWithoutStreaming
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = describe "Servant.HttpStreams" $ do
|
spec = describe "Servant.HttpStreams" $ do
|
||||||
sucessSpec
|
successSpec
|
||||||
failSpec
|
failSpec
|
||||||
wrappedApiSpec
|
wrappedApiSpec
|
||||||
basicAuthSpec
|
basicAuthSpec
|
||||||
|
@ -262,8 +262,8 @@ runClientUnsafe x burl = withClientEnvIO burl (runClientMUnsafe x)
|
||||||
where
|
where
|
||||||
runClientMUnsafe x env = withClientM x env return
|
runClientMUnsafe x env = withClientM x env return
|
||||||
|
|
||||||
sucessSpec :: Spec
|
successSpec :: Spec
|
||||||
sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
successSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
||||||
it "Servant.API.Get root" $ \(_, baseUrl) -> do
|
it "Servant.API.Get root" $ \(_, baseUrl) -> do
|
||||||
left show <$> runClient getRoot baseUrl `shouldReturn` Right carol
|
left show <$> runClient getRoot baseUrl `shouldReturn` Right carol
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
Some APIs need query parameters rewriting, e.g. in order to support
|
Some APIs need query parameters rewriting, e.g. in order to support
|
||||||
for multiple casing (camel, snake, etc) or something to that effect.
|
for multiple casing (camel, snake, etc) or something to that effect.
|
||||||
|
|
||||||
This could be easily achieved by using WAI Middleware and modyfing
|
This could be easily achieved by using WAI Middleware and modifying
|
||||||
request's `Query`. But QueryParam, QueryParams and QueryFlag use
|
request's `Query`. But QueryParam, QueryParams and QueryFlag use
|
||||||
`rawQueryString`. By using `queryString` rather then `rawQueryString`
|
`rawQueryString`. By using `queryString` rather then `rawQueryString`
|
||||||
we can enable such rewritings.
|
we can enable such rewritings.
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
We used `build-type: Custom`, but it's problematic e.g.
|
We used `build-type: Custom`, but it's problematic e.g.
|
||||||
for cross-compiling. The benefit is small, as the doctests
|
for cross-compiling. The benefit is small, as the doctests
|
||||||
can be run other ways too (though not so conviniently).
|
can be run other ways too (though not so conveniently).
|
||||||
|
|
||||||
0.16.2
|
0.16.2
|
||||||
------
|
------
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
- `greet.hs` shows how to write a simple webservice, run it, query it with automatically-derived haskell functions and print the (generated) markdown documentation for the API.
|
- `greet.hs` shows how to write a simple webservice, run it, query it with automatically-derived haskell functions and print the (generated) markdown documentation for the API.
|
||||||
- `greet.md` contains the aforementionned generated documentation.
|
- `greet.md` contains the aforementioned generated documentation.
|
|
@ -155,7 +155,7 @@ test-suite spec
|
||||||
, transformers-compat
|
, transformers-compat
|
||||||
, wai
|
, wai
|
||||||
|
|
||||||
-- Additonal dependencies
|
-- Additional dependencies
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson >= 1.4.1.0 && < 1.5
|
aeson >= 1.4.1.0 && < 1.5
|
||||||
, directory >= 1.3.0.0 && < 1.4
|
, directory >= 1.3.0.0 && < 1.4
|
||||||
|
|
|
@ -808,7 +808,7 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
|
||||||
instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context
|
instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context
|
||||||
where
|
where
|
||||||
type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr)
|
type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr)
|
||||||
-- it doens't really matter what sub route we peak
|
-- it doesn't really matter what sub route we peak
|
||||||
route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)"
|
route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)"
|
||||||
hoistServerWithContext _ _ _ = id
|
hoistServerWithContext _ _ _ = id
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import Servant.Server.Internal.ServerError
|
||||||
-- * Basic Auth
|
-- * Basic Auth
|
||||||
|
|
||||||
-- | servant-server's current implementation of basic authentication is not
|
-- | servant-server's current implementation of basic authentication is not
|
||||||
-- immune to certian kinds of timing attacks. Decoding payloads does not take
|
-- immune to certain kinds of timing attacks. Decoding payloads does not take
|
||||||
-- a fixed amount of time.
|
-- a fixed amount of time.
|
||||||
|
|
||||||
-- | The result of authentication/authorization
|
-- | The result of authentication/authorization
|
||||||
|
|
|
@ -20,7 +20,7 @@ import GHC.TypeLits
|
||||||
--
|
--
|
||||||
-- If you are using combinators that require a non-empty 'Context' you have to
|
-- If you are using combinators that require a non-empty 'Context' you have to
|
||||||
-- use 'Servant.Server.serveWithContext' and pass it a 'Context' that contains all
|
-- use 'Servant.Server.serveWithContext' and pass it a 'Context' that contains all
|
||||||
-- the values your combinators need. A 'Context' is essentially a heterogenous
|
-- the values your combinators need. A 'Context' is essentially a heterogeneous
|
||||||
-- list and accessing the elements is being done by type (see 'getContextEntry').
|
-- list and accessing the elements is being done by type (see 'getContextEntry').
|
||||||
-- The parameter of the type 'Context' is a type-level list reflecting the types
|
-- The parameter of the type 'Context' is a type-level list reflecting the types
|
||||||
-- of the contained context entries. To create a 'Context' with entries, use the
|
-- of the contained context entries. To create a 'Context' with entries, use the
|
||||||
|
|
|
@ -268,5 +268,5 @@ runAction action env req respond k = runResourceT $
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Due to GHC issue <https://ghc.haskell.org/trac/ghc/ticket/2595 2595>, we cannot
|
Due to GHC issue <https://ghc.haskell.org/trac/ghc/ticket/2595 2595>, we cannot
|
||||||
do the more succint thing - just update the records we actually change.
|
do the more succinct thing - just update the records we actually change.
|
||||||
-}
|
-}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import Servant.Server.Internal.ServerError
|
||||||
|
|
||||||
-- | Computations used in a 'Delayed' can depend on the
|
-- | Computations used in a 'Delayed' can depend on the
|
||||||
-- incoming 'Request', may perform 'IO', and result in a
|
-- incoming 'Request', may perform 'IO', and result in a
|
||||||
-- 'RouteResult', meaning they can either suceed, fail
|
-- 'RouteResult', meaning they can either succeed, fail
|
||||||
-- (with the possibility to recover), or fail fatally.
|
-- (with the possibility to recover), or fail fatally.
|
||||||
--
|
--
|
||||||
newtype DelayedIO a = DelayedIO { runDelayedIO' :: ReaderT Request (ResourceT (RouteResultT IO)) a }
|
newtype DelayedIO a = DelayedIO { runDelayedIO' :: ReaderT Request (ResourceT (RouteResultT IO)) a }
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
Some APIs need query parameters rewriting, e.g. in order to support
|
Some APIs need query parameters rewriting, e.g. in order to support
|
||||||
for multiple casing (camel, snake, etc) or something to that effect.
|
for multiple casing (camel, snake, etc) or something to that effect.
|
||||||
|
|
||||||
This could be easily achieved by using WAI Middleware and modyfing
|
This could be easily achieved by using WAI Middleware and modifying
|
||||||
request's `Query`. But QueryParam, QueryParams and QueryFlag use
|
request's `Query`. But QueryParam, QueryParams and QueryFlag use
|
||||||
`rawQueryString`. By using `queryString` rather then `rawQueryString`
|
`rawQueryString`. By using `queryString` rather then `rawQueryString`
|
||||||
we can enable such rewritings.
|
we can enable such rewritings.
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
|
|
||||||
We used `build-type: Custom`, but it's problematic e.g.
|
We used `build-type: Custom`, but it's problematic e.g.
|
||||||
for cross-compiling. The benefit is small, as the doctests
|
for cross-compiling. The benefit is small, as the doctests
|
||||||
can be run other ways too (though not so conviniently).
|
can be run other ways too (though not so conveniently).
|
||||||
|
|
||||||
- *servant* Remove deprecated modules [1268#](https://github.com/haskell-servant/servant/pull/1268)
|
- *servant* Remove deprecated modules [1268#](https://github.com/haskell-servant/servant/pull/1268)
|
||||||
|
|
||||||
|
@ -405,7 +405,7 @@
|
||||||
|
|
||||||
- *servant-client-core* Add `hoistClient` to `HasClient`.
|
- *servant-client-core* Add `hoistClient` to `HasClient`.
|
||||||
Just like `hoistServer` allows us to change the monad in which request handlers
|
Just like `hoistServer` allows us to change the monad in which request handlers
|
||||||
of a web application live in, we also have `hoistClient` for changing the monad
|
of a web application live, we also have `hoistClient` for changing the monad
|
||||||
in which *client functions* live.
|
in which *client functions* live.
|
||||||
Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
|
Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
|
||||||
([#936](https://github.com/haskell-servant/servant/pull/936))
|
([#936](https://github.com/haskell-servant/servant/pull/936))
|
||||||
|
@ -612,7 +612,7 @@
|
||||||
|
|
||||||
`enter` isn't exported from `Servant` module anymore. You can change
|
`enter` isn't exported from `Servant` module anymore. You can change
|
||||||
`enter` to `hoistServer` in a straight forward way.
|
`enter` to `hoistServer` in a straight forward way.
|
||||||
Unwrap natural transformation and add a api type `Proxy`:
|
Unwrap natural transformation and add an api type `Proxy`:
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
-server = enter (NT nt) impl
|
-server = enter (NT nt) impl
|
||||||
|
|
|
@ -157,7 +157,7 @@ test-suite spec
|
||||||
, text
|
, text
|
||||||
, transformers
|
, transformers
|
||||||
|
|
||||||
-- Additonal dependencies
|
-- Additional dependencies
|
||||||
build-depends:
|
build-depends:
|
||||||
hspec >= 2.6.0 && < 2.8
|
hspec >= 2.6.0 && < 2.8
|
||||||
, QuickCheck >= 2.12.6.1 && < 2.15
|
, QuickCheck >= 2.12.6.1 && < 2.15
|
||||||
|
|
|
@ -95,7 +95,7 @@ type family HeaderValMap (f :: * -> *) (xs :: [*]) where
|
||||||
|
|
||||||
class BuildHeadersTo hs where
|
class BuildHeadersTo hs where
|
||||||
buildHeadersTo :: [HTTP.Header] -> HList hs
|
buildHeadersTo :: [HTTP.Header] -> HList hs
|
||||||
-- ^ Note: if there are multiple occurences of a header in the argument,
|
-- ^ Note: if there are multiple occurrences of a header in the argument,
|
||||||
-- the values are interspersed with commas before deserialization (see
|
-- the values are interspersed with commas before deserialization (see
|
||||||
-- <http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 RFC2616 Sec 4.2>)
|
-- <http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 RFC2616 Sec 4.2>)
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
-- >>> toUrlPiece $ safeLink api without
|
-- >>> toUrlPiece $ safeLink api without
|
||||||
-- "bye"
|
-- "bye"
|
||||||
--
|
--
|
||||||
-- If you would like create a helper for generating links only within that API,
|
-- If you would like to create a helper for generating links only within that API,
|
||||||
-- you can partially apply safeLink if you specify a correct type signature
|
-- you can partially apply safeLink if you specify a correct type signature
|
||||||
-- like so:
|
-- like so:
|
||||||
--
|
--
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
-- >>> apiLink = safeLink api
|
-- >>> apiLink = safeLink api
|
||||||
-- >>> :}
|
-- >>> :}
|
||||||
--
|
--
|
||||||
-- `safeLink'` allows to make specialise the output:
|
-- `safeLink'` allows you to specialise the output:
|
||||||
--
|
--
|
||||||
-- >>> safeLink' toUrlPiece api without
|
-- >>> safeLink' toUrlPiece api without
|
||||||
-- "bye"
|
-- "bye"
|
||||||
|
@ -563,7 +563,7 @@ instance HasLink sub => HasLink (AuthProtect tag :> sub) where
|
||||||
type MkLink (AuthProtect tag :> sub) a = MkLink sub a
|
type MkLink (AuthProtect tag :> sub) a = MkLink sub a
|
||||||
toLink = simpleToLink (Proxy :: Proxy sub)
|
toLink = simpleToLink (Proxy :: Proxy sub)
|
||||||
|
|
||||||
-- | Helper for implemneting 'toLink' for combinators not affecting link
|
-- | Helper for implementing 'toLink' for combinators not affecting link
|
||||||
-- structure.
|
-- structure.
|
||||||
simpleToLink
|
simpleToLink
|
||||||
:: forall sub a combinator.
|
:: forall sub a combinator.
|
||||||
|
|
Loading…
Reference in a new issue