Compare commits

..

328 commits

Author SHA1 Message Date
Intolerable
a2e003367d
Add HasStatus instance for Headers (that defers StatusOf to underlying value) (#1649)
* Add HasStatus instance for Headers (that defers StatusOf to underlying value)

* changelog.d/1649
2023-02-14 23:28:57 +01:00
ˌbodʲɪˈɡrʲim
b3214eac38
Require wai >= 3.2.2.1 (#1644) 2023-01-28 13:02:36 +01:00
Jan Hrcek
f71953e63d
Fix haddock code examples in HasClient (#1640) 2023-01-28 13:02:11 +01:00
Théophile Choutri
c382a1f34e
Allow resourcet-1.3 in servant-server and servant-conduit (#1632) 2023-01-18 09:44:11 +01:00
Daan Rijks
2daae80ea8
Add (basic) API docs for ServerT (#1573) 2023-01-09 17:05:08 +01:00
Torgeir Strand Henriksen
a22600979a
Add Functor instance to AuthHandler. (#1638) 2022-12-30 12:56:52 +01:00
Théophile Choutri
b8675c0924
Provisionally disable the Stack CI, it's too flaky. (#1639) 2022-12-29 19:25:58 +01:00
andremarianiello
751350ba9e
WithResource combinator for Servant-managed resources (#1630) 2022-12-29 19:00:47 +01:00
Guillaume Bouchard
a4194dc490
feat: Polymorphic Elem for Union (#1637)
Close https://github.com/haskell-servant/servant/issues/1590
2022-12-23 09:42:52 +01:00
nbacquey
6392dce4bf
Document CaptureHint in Capture[All]Router (#1634)
Co-authored-by: Nicolas BACQUEY <nicolas.bacquey@tweag.io>
2022-12-08 09:20:53 +01:00
Janus Troelsen
8f081bd9ad
Allow mtl-2.3, require jose-0.10 (#1627) 2022-11-17 16:58:52 +01:00
romes
ad25e98e19
Handle Cookies correctly for RunStreamingClient (#1606) 2022-11-03 09:46:49 +01:00
Maxim Koltsov
0fc6e395cb
Remove allow-newer for postgresql-simple (#1625)
Upstream has released updated versions.
2022-10-31 23:59:35 +03:00
Maxim Koltsov
58aa0d1c0c
Merge pull request #1621 from haskell-servant/maksbotan/version-up
Version up for servant, servant-server
2022-10-28 01:26:00 +03:00
Maxim Koltsov
18bc2cf314
Version up for servant, servant-server 2022-10-27 21:26:36 +02:00
Maxim Koltsov
d5b9cbf634
Merge pull request #1592 from TeofilC/ghc-9.4
Support GHC-9.4
2022-10-27 22:14:26 +03:00
Teo Camarasu
ff135e868b Add flags to cabal.project to allow building with GHC-9.4 2022-10-27 13:05:51 +01:00
Teo Camarasu
86c61c6dbd Update doctest to be compatible with newer GHC 2022-10-27 13:05:51 +01:00
Teo Camarasu
3f6886ad2d Bump depedency bounds 2022-10-27 13:05:38 +01:00
Teo Camarasu
53c132173c Bump http-api-data bounds 2022-10-27 13:05:05 +01:00
Teo Camarasu
a445fbafd6 Use CPP to avoid errors with old GHC from TypeApplications in class instance 2022-10-27 13:05:05 +01:00
Teo Camarasu
52f76ea722 Add GHC-9.4 to workflow 2022-10-27 13:05:05 +01:00
Teo Camarasu
4627683a64 Fix TypeError for GHC-9.4
In GHC-9.4 the typechecker changed requiring more annotations in positions like this. See https://gitlab.haskell.org/ghc/ghc/-/wikis/migration/9.4#ambiguous-types-containing-a-typeerror and https://gitlab.haskell.org/ghc/ghc/-/issues/21149
2022-10-18 10:45:21 +01:00
l-epple
e4650de303
Allow lens 5.2 (#1607) 2022-10-02 17:21:43 +02:00
Felix Yan
2323906080
Allow hspec 2.10 (#1609)
Builds fine and all tests pass.
2022-09-07 07:31:58 +02:00
Maxim Koltsov
f0e2316895
Merge pull request #1596 from haskell-servant/maksbotan/servant-auth-ghc9.2
servant-auth-swagger: allow base-4.16
2022-07-17 21:11:45 +03:00
Maxim Koltsov
43c57332dd
servant-auth-swagger: buildable on GHC 9 2022-07-17 20:48:52 +03:00
Maxim Koltsov
1833ef0d6e
servant-auth-swagger: allow base-4.16 2022-07-17 20:01:25 +03:00
Bart Schuurmans
489cbd59f4
servant-client: Run ClientEnv's makeClientRequest in IO (#1595)
* servant-client: Run ClientEnv's makeClientRequest in IO

* Add changelog.d entry for #1595
2022-07-01 13:25:13 +02:00
Ian Shipman
1fba9dc604
Only add a ? when query string is nonempty (#1589)
* Only add a ? when query string is nonempty

* Adds changelog entry
2022-05-16 16:50:10 +02:00
Gaël Deest
8ef5021a5f
Merge pull request #1588 from LightAndLight/master
Add HasSwagger instance for NamedRoutes
2022-05-13 07:41:12 +02:00
Tom Sydney Kerckhove
036102af58
Evaluate NoContent before (not) rendering it. (#1587)
* Evaluate NoContent before rendering it, so it shows up as covered in coverage reports

* failing test as well

* test that NoContent gets rendered if it is not an exception

Co-authored-by: Tom Sydney Kerckhove <syd@cs-syd.eu>
2022-05-04 14:40:26 +02:00
Isaac Elliott
59b5fe67cd servant-swagger: clean up imports 2022-05-03 11:43:30 +10:00
Isaac Elliott
ae8e1e6003 servant-swagger: tag NamedRoutes endpoints with datatype name 2022-05-03 11:43:27 +10:00
Isaac Elliott
cb310b8294 servant-swagger: add HasSwagger instance for NamedRoutes 2022-05-03 11:43:16 +10:00
Julian Arni
5e1569e9e2
Merge pull request #1580 from haskell-servant/jkarni/servant-auth-io-keyset
Allow IO in JWTSettings' validationKeys
2022-04-23 18:17:00 -03:00
Julian K. Arni
4e8fb045e2 Review fix 2022-04-20 21:07:08 +02:00
Julian K. Arni
4cc714d654 Changelog entry 2022-04-20 21:07:08 +02:00
Julian K. Arni
3006e90126 Allow IO in JWTSettings' validationKeys 2022-04-20 21:07:08 +02:00
Gaël Deest
c48a6702b7
Merge pull request #1582 from haskell-servant/named-routes-servant-docs
Add support for NamedRoutes in servant-docs
2022-04-19 13:13:50 +02:00
Gaël Deest
9c81b4927a Add support for NamedRoutes in servant-docs 2022-04-19 12:51:31 +02:00
Gaël Deest
117a2cc5e1
Merge pull request #1583 from haskell-servant/hspec-no-color
Disable hspec colored output in servant-swagger doctests
2022-04-19 12:41:15 +02:00
Gaël Deest
78280dc267 Disable hspec colored output in servant-swagger doctests
Colored output is the default since hspec 2.9.5.

This causes CI failures due to terminal escaping characters when running
the doctests on GitHub Actions.
2022-04-19 11:16:03 +02:00
Alp
c19ed0fb92
Major bound for servant-server's dependency on servant (#1574)
Reflecting a revision made on hackage for servant-server 0.19.1
2022-03-30 02:10:54 +02:00
Shea Levy
658585a7cd
Derive MonadMask for ClientM (#1572) 2022-03-26 17:03:01 +01:00
Gaël Deest
65de6f701c
Merge pull request #1556 from nbacquey/router_layout_captures
Display capture hints in router layout
2022-03-25 10:42:33 +01:00
Nicolas BACQUEY
a19cb84a0e Update changelog 2022-03-24 16:43:27 +01:00
Nicolas BACQUEY
9d66e16706 Add spec for serverLayout 2022-03-23 14:30:45 +01:00
Nicolas BACQUEY
77b92d0d7d Display capture hints in router layout
This commit introduces a `CaptureHint` type, which is passed as an extra
argument to the `CaptureRouter` and `CaptureAllRouter` constructors for
the `Router'` type.
`CaptureHint` values are then used in `routerLayout`, to display the
name and "type" of captured values (single or list), instead of just
"<capture>" previously.

N.B.:
Because the `choice` smart constructor for routers can aggregate
`Capture` combinators with different capture hints, the `Capture*Router`
constructors actually take a *list* of `CaptureHint`, instead of a
single one.
2022-03-23 14:30:45 +01:00
Maxim Koltsov
f5a91d20e1
Merge pull request #1568 from haskell-servant/maksbotan/stackage-deps
Allow hspec-2.9, lens-aeson-1.2
2022-03-22 23:43:22 +01:00
Maxim Koltsov
dd29f25f77
Allow lens-aeson 1.2 2022-03-22 23:22:02 +01:00
Maxim Koltsov
04f59c012b
Require servant-0.18.2 in servant-swagger
This version of servant adds Fragment, which servant-swagger adds
instance for.
2022-03-22 23:10:05 +01:00
Maxim Koltsov
256cec566f
Support hspec >= 2.9 in servant-swagger tests 2022-03-22 22:54:58 +01:00
Gaël Deest
276ca2ed01
Merge pull request #1569 from haskell-servant/url-encoding
Use toEncodedUrlPiece directly when encoding captures
2022-03-22 14:19:07 +01:00
Gaël Deest
c1c631eaff Add changelog entry 2022-03-22 11:56:18 +01:00
Gaël Deest
0e051ccfdf
Merge pull request #1557 from ysangkok/janus/newer-stack
Use Stack 2.7.5, cleanup allow-newer/CI
2022-03-22 11:36:17 +01:00
Gaël Deest
658217b021 Use toEncodedUrlPiece directly when encoding captures
Current implementation of captures uses the `toUrlPiece` method from the
`ToHttpApiData` typeclass, and encodes the resulting `Text` using `toEncodedUrlPiece`
when appending to the request path.

The problem with this approach is that the instance for `Text` percent-encodes
characters that are perfectly valid in URLs, such as `*`.

This patch makes direct use of `toEncodedUrlPiece`, which lets users implement
encoding according to their needs.

Closes #1511
2022-03-21 17:29:23 +01:00
Gaël Deest
af3dde1b1d
Merge pull request #1566 from haskell-servant/fix-operator-doc
Fix haddock documentation for (//) and (/:)
2022-03-21 16:14:47 +01:00
Maxim Koltsov
ced5f1a655
Allow hspec-2.9 2022-03-21 15:44:10 +01:00
Maxim Koltsov
626e1c3a7c
Relax more deps for Stackage (#1567) 2022-03-21 17:18:08 +03:00
Gaël Deest
0c80bc8f8e Fix haddock documentation for (//) and (/:)
The examples for these two operators weren't displayed properly due to invalid Haddock markup.
2022-03-21 14:18:49 +01:00
Maxim Koltsov
d52c5d08a0
servant-server 0.19.1 2022-03-21 14:13:52 +01:00
Maxim Koltsov
89b66a3634
Merge pull request #1555 from ysangkok/janus/ghc-92
Allow GHC 9.2 for all packages
2022-03-21 13:58:48 +01:00
Gaël Deest
3370b75622
Merge pull request #1565 from haskell-servant/re-export
Re-export Servant.API.Generic in Servant.API
2022-03-21 13:57:40 +01:00
Gaël Deest
9a99ef9a0b Re-export Servant.API.Generic in Servant.API 2022-03-21 13:31:33 +01:00
Maxim Koltsov
408352320e
Remove obsolete allow-newer 2022-03-21 11:45:49 +01:00
Janus Troelsen
010e6a72af Disable curl-mock for 9.2 because of generic-arbitrary 2022-03-13 20:35:40 -06:00
Janus Troelsen
39898676a8 Enable all packages on GHC 9.2 2022-03-13 19:58:24 -06:00
Janus Troelsen
bbd82a736f Use Stack 2.7.5, cleanup 2022-03-09 12:58:50 -06:00
Janus Troelsen
17e3eb1041 Allow GHC 9.2 for compatible packages 2022-03-08 08:59:35 -06:00
Gaël Deest
de923fc887
Merge pull request #1554 from ysangkok/repl-doctest
Use cabal-install to invoke doctest
2022-03-08 09:16:19 +01:00
Janus Troelsen
222ccf107c Use cabal-install to invoke doctest 2022-03-08 01:01:37 -06:00
Giorgio Marinelli
d05da71f09
Export encoding function for a query parameter value (#1549) 2022-03-01 15:22:25 +01:00
Marco Perone
cedab6572d
fix broken links (#1548) 2022-03-01 09:34:45 +01:00
Gaël Deest
15b364ae93
Merge pull request #1541 from mjdominus/master
Update documentation
2022-02-28 09:50:17 +01:00
Gaël Deest
8fccfccae0
Merge pull request #1546 from hasufell/PR/hasufell/issue-1545/monad-fail
Add `MonadFail` instance for `Handler` wrt #1545
2022-02-28 09:47:33 +01:00
Julian Ospald
181e51db8a
Add MonadFail instance for Handler wrt #1545 2022-02-26 22:31:56 +01:00
Mark Jason Dominus (陶敏修)
0e4d02ae75 Update copyright notice from 2018 to 2022 2022-02-25 14:44:13 -05:00
Mark Jason Dominus (陶敏修)
b4c4131778 Update error message in Makefile
The file this message refers to was renamed in
commit 53b3b939e4.
2022-02-25 14:44:13 -05:00
Mark Jason Dominus (陶敏修)
6d5c3023ce Discuss ghcup in tutorial installation instructions
The instructions as written will not work on Ubuntu systems,
which provide an extremely out-of-date Haskell toolchain.

Addresses issue https://github.com/haskell-servant/servant/issues/1540

https://github.com/haskell-servant/servant/issues/1540
2022-02-25 14:44:12 -05:00
Caroline GAUDREAU
7ef9730f77
Merge pull request #1538 from akhesaCaro/reverting
Reverting NamedRoutes cookbook
2022-02-18 12:37:05 +01:00
akhesaCaro
6da8488f9b Revert "removing Generic cookbook in favour of NamedRoutes"
This reverts commit 34aed1d289.
2022-02-18 12:14:28 +01:00
akhesaCaro
f4cd56446b Revert "introducing NamedRoutes cookbook"
This reverts commit 5c80214351.
2022-02-18 12:13:09 +01:00
Caroline GAUDREAU
50355d0125
Merge pull request #1534 from akhesaCaro/cookbook_namedRoutes
Cookbook named routes
2022-02-18 11:41:19 +01:00
akhesaCaro
34aed1d289 removing Generic cookbook in favour of NamedRoutes 2022-02-18 11:08:43 +01:00
akhesaCaro
5c80214351 introducing NamedRoutes cookbook 2022-02-18 11:08:36 +01:00
Gaël Deest
009dc06e76
Merge pull request #1535 from ysangkok/remove-unnecessary-constraints-and-allow-newer
Remove unnecessary constraint/allow-newer
2022-02-16 22:51:59 +01:00
Janus Troelsen
e2a9165229 Remove unnecessary constraint/allow-newer 2022-02-15 09:54:17 -06:00
Gaël Deest
d35b3e9b70
Merge pull request #1529 from purefunsolutions/fix-servant-client-ghcjs-for-servant-0.19
Fix servant-client-ghcjs for servant 0.19
2022-02-14 16:39:00 +01:00
Gaël Deest
002fa2107a
Merge pull request #1531 from gdeest/servant-auth-named-routes
servant-auth-server: Support NamedRoutes
2022-02-14 14:57:19 +01:00
Gaël Deest
bd9151b9de servant-auth-server: Support NamedRoutes
Trying to use `NamedRoutes` with `servant-auth-server` currently results
in hideous error messages such as:

```
app/Main.hs:50:7: error:
    • No instance for (Servant.Auth.Server.Internal.AddSetCookie.AddSetCookies
                         ('Servant.Auth.Server.Internal.AddSetCookie.S
                            ('Servant.Auth.Server.Internal.AddSetCookie.S
                               'Servant.Auth.Server.Internal.AddSetCookie.Z))
                         (AdminRoutes (Servant.Server.Internal.AsServerT Handler))
                         (ServerT
                            (Servant.Auth.Server.Internal.AddSetCookie.AddSetCookieApi
                               (Servant.Auth.Server.Internal.AddSetCookie.AddSetCookieApi
                                  (NamedRoutes AdminRoutes)))
                            Handler))
        arising from a use of 'serveWithContext'
    • In the expression: serveWithContext (Proxy @API) ctx RootAPI {..}
```

This is because we didn't teach it how to recurse along `NamedRoutes`
trees and sprinkle headers at the tip of each branch.

This commit adds a test case and fixes the issue. In the process, it
also implements `ThrowAll` for `NamedRoutes`, which was necessary for
the test to run, and should also prove convenient for users.
2022-02-14 14:28:46 +01:00
Mika Tammi
17b55634b3
servant-client-ghcjs: Fix performRequest function
Fix performRequest function to be compatible with the latest
servant-client-core RunClient typeclass
2022-02-11 20:55:34 +02:00
Mika Tammi
3158809631
servant-client-ghcjs: Bump base max-bound 2022-02-11 20:22:57 +02:00
Gaël Deest
cdd7c34add
Merge pull request #1526 from ysangkok/master
Allow newer hashable, lens, text
2022-02-07 10:00:58 +01:00
Gaël Deest
67322d8ab8
Merge pull request #1525 from k0001/fix-9.2.1
servant-server: Fix build on GHC 9.2.1
2022-02-07 09:57:23 +01:00
Janus Troelsen
67da8514a0 Allow newer hashable, lens, text 2022-02-06 16:12:25 -06:00
Renzo Carbonara
61d0d14b5c servant-server: Fix build on GHC 9.2.1
The issue is similar to the one in #1513:

```
src/Servant/Server/Internal.hs:824:10: error:
    • Uninferrable type variable k0 in
      type family equation right-hand side: (TypeError ...)
    • In the type instance declaration for ‘ServerT’
      In the instance declaration for
        ‘HasServer ((arr :: a -> b) :> sub) context’
    |
824 |     type ServerT (arr :> sub) _ = TypeError (PartialApplication HasServer arr)
    |
```

This fix is similar to the one in #1514.
2022-02-04 14:34:12 +02:00
Sven Tennie
a8f1a7603f
Update docs: #haskell-servant is now on libera.chat (#1503) 2022-02-03 12:40:00 +01:00
Gaël Deest
78034cd2b3
Merge pull request #1522 from peterbecich/github-actions-updates
minor updates to GitHub Actions
2022-02-03 10:20:23 +01:00
Clément Delafargue
6f12e38698
Fix NamedRoutes example in 0.19 changelog (#1523) 2022-02-03 09:56:19 +01:00
Peter Becich
9a3fd77a3a
minor updates to GitHub Actions 2022-02-02 23:50:10 -08:00
Gaël Deest
e14f445e2a
Merge pull request #1521 from gdeest/minor-releases
servant-auth 0.4.0.0 -> 0.4.1.0, servant-auth-server 0.4.6.0 -> 0.4.7.0
2022-02-02 16:14:21 +01:00
Gaël Deest
4caa1f563b servant-auth 0.4.0.0 -> 0.4.1.0, servant-auth-server 0.4.6.0 -> 0.4.7.0 2022-02-02 15:54:31 +01:00
Gaël Deest
e1b59dbb31
Merge pull request #1519 from haskell-servant/prepare-0.19
Changelog tweaks + servant-http-streams / servant-docs bump
2022-02-01 12:42:34 +01:00
Gaël Deest
b17d018d3f Changelog tweaks + servant-http-streams / servant-docs bump 2022-02-01 12:29:31 +01:00
Gaël Deest
e98ae8adba
Merge pull request #1517 from haskell-servant/prepare-0.19
Prepare 0.19 release
2022-02-01 10:28:15 +01:00
Gaël Deest
e4945740aa Prepare 0.19 release 2022-02-01 10:17:03 +01:00
Gaël Deest
7a770b5a1e
Merge pull request #1514 from guibou/fix_ghc92_build
Fix GHC 9.2 build
2022-01-25 11:42:10 +01:00
Guillaume Bouchard
22d5790e73 Fix GHC 9.2 build
Close #1513.

GHC 9.2 needs explicit kind signature here, I don't really understand
why.

This kind signature is correct and not too restritive, because `HasLink`
is technically defined `class HasLink endpoint` which means that it is
infered as `k -> Constraint`. In the instance signature, we have
`HasLink ((arr :: a -> b) :> sub)`, so here the `k` is the same kind as
the one of `:>` which is not polykinded.
2022-01-24 17:14:44 +01:00
Gaël Deest
75db4a5327
Merge pull request #1486 from haskell-servant/type-errors
Custom errors for HasClient, HasServer
2022-01-18 17:16:37 +01:00
Gaël Deest
75cb9ac246 Add comment about slightly incorrect error message 2022-01-18 16:25:11 +01:00
Gaël Deest
aab7e0d5dd Custom errors for HasClient, HasServer 2022-01-18 16:25:05 +01:00
Gaël Deest
3493d135f0
Merge pull request #1508 from haskell-servant/fix-servant-swagger-build
Fix servant-swagger Cabal
2022-01-18 11:15:10 +01:00
Gaël Deest
e8c301afc9 Add servant-swagger to stack.yaml 2022-01-18 11:07:38 +01:00
Gaël Deest
b56d681fde Relax doctest lower bound 2022-01-18 11:07:38 +01:00
Gaël Deest
b33442423e Re-adding Cabal-the-library as a dep
Fixes #1507.
2022-01-18 11:07:17 +01:00
Sven Tennie
c388c5e82c
Add HeadNoContent to Servant.API.Verbs (#1502)
As the head method isn't allowed to contain any response body, no
general Head Verb is added. (This may easily lead to wrong usages...)

(https://httpwg.org/specs/rfc7231.html#HEAD)
2022-01-06 13:02:57 +01:00
Matthieu Coudron
73c87bc2bc
bumped cabal-version field (#1498)
* bumped cabal-version field

Cabal supports two types of licenses, native and SPDX, which can be seen here hackage.haskell.org/package/Cabal-3.6.2.0/docs/Distribution-Types-PackageDescription.html#v:licenseRaw

Several packages use BSD-3-Clause as a license, in conjonction with cabal-version: >=1.10 which cabal parses as Right (UnknownLicense "BSD-3").
If I change teh cabal-version to cabal-version: 2.2 , cabal correctly identifdies the license License (ELicense (ELicenseId BSD_3_Clause)).

* changed license from cabal to spdx format

aka BSD3 -> BSD-3-Clause: next cabal may deprecate the old format
2022-01-04 22:06:23 +01:00
Giorgio Marinelli
29d2553e74
Derive HasClient good response status from Verb status (#1469) 2021-12-09 10:09:18 +01:00
antoine-fl
cb294aa2b3
Fix Request's Show instance (#1492) 2021-12-01 19:16:59 +01:00
Théophile Choutri
a975cfc361
Add details about AddHeaders instances (#1490)
* Add details about the instances of AddHeader

Also:

* Cleanup of extensions and imports
2021-11-30 23:52:06 +01:00
Caroline GAUDREAU
9a3979926d
Merge pull request #1475 from akhesaCaro/aeson_2
support Aeson 2
2021-11-26 17:25:56 +01:00
akhesacaro
05ef0dd1d3 Allow using aeson 1 (lax with min-bounds) 2021-11-26 17:14:31 +01:00
akhesacaro
62033db535 servant-auth-swagger: bump servant-swagger and swagger2 2021-11-18 11:56:38 +01:00
akhesacaro
d9d8fa7525 servant-swagger: remove obsolete files 2021-11-18 11:56:38 +01:00
akhesacaro
42ceb3916d changing servant-swagger info 2021-11-18 11:56:38 +01:00
akhesacaro
bcb484774e servant-swagger: bump aeson and cabal (aeson > 2) 2021-11-18 11:56:38 +01:00
akhesacaro
39fb875951 moving servant-swagger into the main servant repo 2021-11-18 11:56:38 +01:00
akhesacaro
efffc70919 fixing servant-auth (aeson 2.0 bump) 2021-11-18 11:56:38 +01:00
akhesacaro
8af80d35a0 bump jose min and max-bound (aeson 2.0 bump) 2021-11-18 11:56:38 +01:00
akhesacaro
e01188aaad min bound aeson 2 2021-11-18 11:56:32 +01:00
Gaël Deest
3ed24fdd90
Merge pull request #1289 from acondolu/master
Better errors for partially applied combinators
2021-11-18 10:51:30 +01:00
Gaël Deest
0e41e37c93
Merge pull request #1485 from haskell-servant/rename-proof
Rename proof to g{Client,Server,Link}Proof
2021-11-18 10:43:35 +01:00
Gaël Deest
f2bd982eaf Rename proof to g{Client,Server,Link}Proof 2021-11-18 10:25:36 +01:00
Gaël Deest
1bb0282abc
Merge pull request #1388 from gdeest/generic-apis
Improve API for composing generic routes
2021-11-18 10:21:59 +01:00
Gaël Deest
575aa70eca Cleanup 2021-11-18 10:11:45 +01:00
Gaël Deest
d81c8d9911 Add parameter-supplying operator
Renamed `(/:)` to `(//)`, and used `(/:)` for supplying parameters to
client functions.

Should close #1442.
2021-11-18 10:11:45 +01:00
Gaël Deest
6718752b4a Add (/:) operator 2021-11-18 10:11:31 +01:00
Gaël Deest
5f8aaec146 Fix client tests 2021-11-18 10:11:31 +01:00
Gaël Deest
fca59556dd Code reorganization
Move `HasServer (NamedRoutes routes)` instance

The instance has been moved to `Servant.Server.Internal`, as the
instances for other combinators. It is necessary so that the instance
can be re-exported from `Servant.Server` without circular imports.

Otherwise, users have to import `Servant.Server.Generic` manually ;
forgetting to do so will produce confusing error messages about the
missing instance.

Move `HasClient (NamedRoutes routes)` instance

Moved so that the instance is made available when importing
`Servant.Client`, avoiding possibly confusing errors when
`Servant.Client.Generic` isn't imported.
2021-11-18 10:09:58 +01:00
Gaël Deest
b033871dfc Implement HasLink instance for NamedRoutes 2021-11-18 10:09:58 +01:00
Gaël Deest
861cd4f997 Exclude quantified constraints code for GHCJS
QuantifiedConstraints isn't available for GHC 8.4 (where our GHCJS
version is still stuck).

We may need to take a drastic decision for GHCJS at some point.
2021-11-18 10:09:58 +01:00
Gaël Deest
5ead291f8d Implementation of HasClient
Follows the same design as `HasServer` in the previous commit.

A test has been added (which incidentally acts as a test for the
HasServer instance).
2021-11-18 10:09:58 +01:00
Gaël Deest
b0b02f1948 Implement HasServer (NamedRoutes routes)
We define `ServerT (NamedRoutes api) m` as `api (AsServerT m)`, so that
the server of an record-defined API is a record of handlers.

The implementation piggy backs on the instance for “vanilla” servant
types with `(:<|>)`, using the `GServantProduct` for converting backd
and forth between the record / vanilla servers.

The main difficulty is that GHC needs to know that this operation is
legit, which can be expressed as the fact that:

```
GToServant (Rep (ServerT (NamedRoutes api))) m ~
ServerT (GToServant (Rep (api AsApi))) m
```

plus a few additional constraints.

This is easy enough for `route`, as we know that `m ~ Handler`. But in
the case of `hoistServerWithContext`, the two involved monads are
unknown ; in other words, this constraint needs to hold `forall m.`

Switching `-XQuantifiedConstraints` on is not sufficient, as our
constraints involve type families (`Rep` and `ServerT`). Our trick is to
use an intermediary typeclass, `GServer`, as a provider of evidence (in
the form of a `Dict`) that our constraints are indeed satisfied for a
particular monad.

The only instance of `GServer` is defined along with it, so it is
practically invisible to users.
2021-11-18 10:09:58 +01:00
Gaël Deest
65e3070cac Add NamedRoutes combinator
Allows users to directly embed APIs defined as records of routes into
vanilla Servant API types.

E.g.:

```haskell
data MyRoutes mode = MyRoutes
  { version :: mode :- Get '[JSON] Int
  , …
  }

type API = "prefix" :> NamedRoutes MyRoutes :<|> …
```

APIs can thus be recursively defined directly with Generic record types.
2021-11-18 10:09:58 +01:00
Gaël Deest
67a37dc3f6 Fix build error on GHC 8.6 2021-11-17 15:29:22 +01:00
Caroline GAUDREAU
04e4de5260
Merge pull request #1357 from SupercedeTech/master
servant-docs: Add support of Pretty modifier for all verbs aliases
2021-11-17 14:42:24 +01:00
Andrea Condoluci
42b7d0eb9b Type-level errors for HasLink for invalid combinators 2021-11-15 21:40:36 +01:00
Théophile Choutri
f3d25bfdb3
Merge pull request #1479 from tchoutri/update-cabal-spdx-identifiers
Change the license value to a valid SPDX identifier
2021-11-01 10:39:54 +01:00
Théophile Choutri
4e4ad495ef Change the license value to a valid SPDX identifier 2021-10-31 22:37:56 +01:00
Maxim Koltsov
043d5a0e90
Merge pull request #1476 from haskell-servant/maksbotan/fix-servant-auth-tests
Fix tests for some servant-auth pkgs on GHC 9
2021-10-31 22:04:49 +01:00
Maxim Koltsov
70f6c49524
Get rid of Unicode in err404 example (#1478)
ServerError field errBody uses ByteString, whose IsString instance kills
Unicode, thus turning example into garbage. Changed it to simple ASCII
string, since Unicode art did not exactly correspond to 404 error
anyway.

Fixes #1371
2021-10-31 14:36:57 +03:00
Théophile Choutri
70b3721537
Merge pull request #1477 from josephcsible/ghc92
Enable FlexibleContexts in Servant.API.ContentTypes
2021-10-31 12:09:04 +01:00
Joseph C. Sible
fea40bd0fc Enable FlexibleContexts in Servant.API.ContentTypes
Starting with GHC 9.2, UndecidableInstances no longer implies FlexibleContexts.
Add this extension where it's needed to make compilation succeed.
2021-10-30 23:26:21 -04:00
Maxim Koltsov
ca6774d797 Update servant-auth cookbook deps 2021-10-30 21:29:17 +02:00
Maxim Koltsov
e2e9ce0596 Enable servant-auth cookbook 2021-10-30 21:26:44 +02:00
Maxim Koltsov
53b1d9d2b6 Enable tests for servant-auth-client
Fixes #1474
2021-10-30 21:01:01 +02:00
Maxim Koltsov
551d4936af Fix tests for some servant-auth pkgs on GHC 9
Turns out the tests broke because of base64-bytestring issue specific to
GHC-9 that was fixed in 1.2.1.0.

Fixes #1474
2021-10-30 20:43:35 +02:00
Caroline GAUDREAU
bd9e4b1090
Merge pull request #1471 from akhesaCaro/monorepo_servant_auth
repatriation of servant-auth in the main servant repo
2021-10-29 15:03:24 +02:00
akhesacaro
e05826a799 servant-auth-swagger: Excluding building against GHC 9.0
(need base > 4.15 but swagger exclude it)
2021-10-27 18:32:46 +02:00
akhesacaro
95033be30f server-auth-server: Excluding tests against GHC 9 2021-10-27 18:32:46 +02:00
akhesacaro
7c012d70d3 servant-auth-client: Excluding tests against GHC 9 2021-10-27 18:32:46 +02:00
akhesacaro
48d22a35b8 servant-auth: removing CI status in README, Servant attribution now 2021-10-27 18:32:38 +02:00
akhesacaro
8e7a775cdd servant-auth: removing unused files from former repo 2021-10-27 18:31:20 +02:00
akhesacaro
05674e4870 change servant-auth repo url in cabal files 2021-10-26 22:31:40 +02:00
akhesacaro
119e54a800 repatriation of servant-auth in the main servant repo 2021-10-26 16:27:09 +02:00
Caroline GAUDREAU
26b01f03f2
Merge pull request #1432 from GambolingPangolin/fixes-1418
Addresses problem with URL encodings
2021-10-24 09:24:57 +02:00
Gaël Deest
abc53b54e3
Merge pull request #1462 from haskell-servant/upgrade-ghcjs
Upgrade GHCJS
2021-10-11 13:40:39 +02:00
Gaël Deest
b0f8c89472
Merge pull request #1465 from haskell-servant/eyeinsky-master
Fix documentation hierarchy
2021-10-11 13:27:34 +02:00
Markus Läll
f92d2c7ad6 Fix typo 2021-10-11 13:02:06 +02:00
Markus Läll
43760caf97 Fix documentation hierarchy 2021-10-11 13:02:06 +02:00
Théophile Choutri
9df5195710
Merge pull request #1463 from sorki/srk/foreignTypo
servant-foreign: fix haddock/example typo
2021-10-11 11:32:29 +02:00
Gaël Deest
b7c6a95929 Fix tested-with fields in Cabal files
Also re-added `servant-client` to `cabal.ghcjs.project`, setting
`buildable: False` on tests as they don't run with GHCJS.
2021-10-11 10:35:40 +02:00
Richard Marko
51c8edb74d servant-foreign: fix haddock/example typo 2021-10-11 10:29:06 +02:00
Gaël Deest
e9ae1eeed8 Remove the old Github action 2021-10-10 22:59:36 +02:00
Gaël Deest
910a3ae7ec Upgrade GHCJS to 8.6
This allows us to deprecate GHCJS 8.4 (which makes sense, as vanilla GHC
< 8.6 is already deprecated).

We re-use GHCJS from reflex-platform, which unfortunately isn't
up-to-date with latest GHC and is only 8.6. The benefit of using
reflex-platform is that it provides nix expressions for GHCJS + a binary
nix cache.

reflex-platform patches text to use a JS-String based internal
representation for performance reasons, so we provide a few haskell
dependencies from reflex-platform as well:

- hashable
- attoparsec

As those rely on text's internal representation but have been patched
for reflex-platform.
2021-10-10 22:53:26 +02:00
Ian Shipman
d5e439e56b Updates changelog 2021-10-03 09:57:55 -05:00
Ian Shipman
9666f1956b Addresses problems with URL encodings
This changes the way URL encoding for query parameters is handled,
making it possible to correctly encode arbitrary binary data into query
parameter values.

Closes #1418
2021-10-03 09:57:55 -05:00
Théophile Choutri
48bc24768e
Merge pull request #1458 from tchoutri/master
Use GHC 8.10.7 for CI & Cabal metadata
2021-10-03 12:00:59 +02:00
Théophile Choutri
c011f12d24 Use GHC 8.10.7 for CI and in Cabal metadata
Sponsored by: Scrive AB
2021-10-02 23:51:25 +02:00
Caroline GAUDREAU
5115c41617
Merge pull request #1457 from akhesaCaro/nixshell_ghc9
Update to GHC 9 (sub tasks)
2021-10-02 19:29:28 +02:00
akhesacaro
9be55b3ba3 uncomment db-sqlite-simple cookbook to add it to the building plan 2021-10-02 18:13:24 +02:00
akhesacaro
61d097db44 uncomment uverb coobook and include it from building against GHC >= 9 2021-10-02 18:09:51 +02:00
akhesaCaro
2ea6664124 GHC9 mention in Nix README. 2021-10-02 17:48:45 +02:00
Caroline GAUDREAU
0b706aa6d1
Merge pull request #1452 from akhesaCaro/unsupport_old_ghc
Unsupport GHC < 8.6.5
2021-10-02 13:41:42 +02:00
akhesacaro
a4aacc9475 A new version of hashable isn't compitable with our ghcjs. A max bound version is needed in the Cabal file 2021-10-02 13:32:15 +02:00
akhesacaro
e5f1604a9d removing Makefile deprecated with its GHC version 2021-10-02 13:13:33 +02:00
akhesacaro
e56f0092d7 remove tested-with (GHC < 8.6.5) from cabal 2021-10-02 13:13:33 +02:00
akhesaCaro
6e5dffbb91 unsupporting GHC < 8.6.5, removing unecessary imports 2021-10-02 13:13:24 +02:00
akhesaCaro
1fa1878180 unsupporting GHC < 8.6.5 in the CI 2021-10-02 13:10:20 +02:00
akhesacaro
af7d281ef0 add missing dependencies into shell.nix 2021-10-02 13:10:20 +02:00
akhesaCaro
b1a9876dc9 unsupporting GHC < 8.6.5 in the nix-shell 2021-10-02 13:08:54 +02:00
Caroline GAUDREAU
8da966f057
Merge pull request #1455 from bChiquet/Document-Raw's-behaviour
Document Raw's behaviour when composing APIs
2021-10-01 17:32:01 +02:00
bChiquet
8b93af3d12 factor in @alp's feedbacks on PR #1455 2021-10-01 16:33:16 +02:00
bChiquet
29aa10176d Document Raw's behaviour when composing APIs 2021-10-01 16:33:16 +02:00
Caroline GAUDREAU
bf160cc1ad
Merge pull request #1456 from akhesaCaro/http_common_constraint
Fix CI by adding a constraint to http-common to make it build against ghc 8.2.2
2021-10-01 15:56:53 +02:00
akhesaCaro
993277e8f4 add a coinstraint to http-common to make it build against ghc 8.2.2 2021-10-01 15:15:59 +02:00
Felix Yan
3af3129f75
Allow transformers-compat 0.7 (#1436)
Builds fine and all tests pass.
2021-08-29 15:06:09 -05:00
Brandon Chinn
799537f82d
Add serveWithContextT, ServerContext (#1441)
servant-server: add serveWithContexT and ServerContext
2021-08-21 19:15:02 +02:00
Dan Fithian
47bd25266f
Servant docs curl (#1401)
servant-dosc: generate sample curl request
2021-08-19 13:11:00 +02:00
Paolo Capriotti
19ec395e66
Avoid using SOP constructors directly (#1434)
This is a followup to #1420. It uses `respond` and `matchUnion`, with
the help of some type annotations, instead of the NS constructors from
SOP.
2021-07-13 10:10:30 -05:00
Maxim Koltsov
21682f6b72
servant-foreign 0.15.4 2021-06-23 23:47:01 +02:00
Maxim Koltsov
e2b897d3c0
Prepare 0.18.3 release (#1430) 2021-06-24 00:38:46 +03:00
Maxim Koltsov
3e29b5194e
Merge pull request #1409 from haskell-servant/maksbotan/ghc-9
Update for GHC 9.0.1
2021-06-24 00:12:26 +03:00
Maxim Koltsov
f527f09ac3
continue-on-error for doctest on GHC 9 2021-06-23 23:06:15 +02:00
Maxim Koltsov
2eba8866b7
Fix doctest running in CI 2021-06-23 23:06:15 +02:00
Maxim Koltsov
6cf2da8b64
Update GHC 8.10 to 8.10.4 in GitHub actions 2021-06-23 23:06:15 +02:00
Maxim Koltsov
4c05338876
doctest 0.18 2021-06-23 23:06:15 +02:00
Maxim Koltsov
61111178f0
Support GHC-9.0.1 2021-06-23 23:06:07 +02:00
Felix Yan
cc67b9ec6e
Allow attoparsec 0.14 (#1408)
Builds fine and all tests pass.
2021-06-21 22:54:50 -05:00
Alp
0c961f6ebb
Fix #1405 (#1429)
Request bodies are not really supposed to be used in GET requests.
2021-06-21 22:54:29 -05:00
Alp
ba30dd1700
Merge pull request #1424 from haskell-servant/update-irc-link
Update IRC link in readme to point at libera
2021-06-16 11:44:52 +02:00
Paolo Capriotti
0f9cc7eeec
Add response header support to UVerb (#1420)
* Use type wrapped in Headers h to generate response

This avoids having to define MimeRender instances for Headers.
2021-06-10 17:10:50 +02:00
Samuel Gélineau
0cb2d603c4
use Capture Description if available (#1423)
* use Capture Description if available

* update golden/comprehensive.md

This is technically a breaking change, because if a Capture has both a
Description and a ToCapture instance, the Description now takes
precedence. Since this Description wasn't doing anything before, I am
guessing that most projects currently only use Description to describe
their endpoints and not their Captures, and thus that few people will be
affected by this breaking change.

* test the "no ToCapture instance" case

The case in which there is both a Description and a ToCapture instance
seems like a corner case. The more interesting cases are the one in
which there is a Description but no ToCapture instance, and the case in
which there is a ToCapture instance but no description.
2021-06-08 13:28:19 -05:00
Maxim Koltsov
da8e64b534
Allow lens-5.0 (#1426) 2021-06-06 00:37:35 +03:00
Nathan van Doorn
26f0f93874
Update IRC link in readme to point at libera 2021-05-26 09:05:50 +01:00
Maxim Koltsov
4016aafe66
CI: add ppa:hvr/ghc in ghcjs build (#1421) 2021-05-14 16:34:16 +03:00
Maxim Koltsov
507f0a4671
Allow hspec < 2.9
https://github.com/commercialhaskell/stackage/issues/6010
2021-05-14 12:34:04 +03:00
Gaël Deest
4a79cea3ff
Merge pull request #1415 from felixonmars/patch-1
Allow singleton-bool 0.1.6
2021-04-29 15:29:02 +02:00
fisx
448c444db6
Typo (#1416) 2021-04-23 10:37:48 +02:00
Felix Yan
3c520683ce
Allow singleton-bool 0.1.6
Builds fine and all tests pass.
2021-04-21 06:16:17 +08:00
Caroline GAUDREAU
ad76c47c2f
Merge pull request #1413 from akhesaCaro/cookbook_hoist_server_with_context
enabling hoist-server-with-context cookbook
2021-04-16 13:49:57 +02:00
akhesaCaro
97967d87d1 enabling hoist-server-with-context cookbook 2021-04-16 13:39:09 +02:00
Gaël Deest
4fe6997659
Merge pull request #1412 from akhesaCaro/cookbook_https
enabling https cookbook
2021-04-10 15:47:38 +02:00
akhesaCaro
bbd016df09 enabling https cookbook 2021-04-10 15:23:57 +02:00
Caroline GAUDREAU
486f89da04
Merge pull request #1410 from akhesaCaro/improve_nix_shell
pinning nixpkgs and adding missing dependencies to shell.nix file
2021-04-10 11:59:17 +02:00
akhesaCaro
4a7a1080a0 pinning nixpkgs and adding missing dependencies 2021-04-10 11:30:37 +02:00
Caroline GAUDREAU
bc6144716b
Merge pull request #1407 from akhesaCaro/using_derivingvia_uverb
Using DerivingVia in UVerb's cookbook
2021-04-09 16:43:12 +02:00
Gaël Deest
f30b72cc90 Add DeriveAnyClass to UVerb.lhs (not implied by DerivingVia on 8.6.5) 2021-04-09 16:19:51 +02:00
Gaël Deest
81a73dfcda Try excluding uverb cookbook from pre 8.6.1 builds 2021-04-09 15:59:02 +02:00
Caroline GAUDREAU
d06b65c4e6
Merge pull request #1390 from Profpatsch/document-servant-foreign
Document servant-foreign
2021-03-25 12:21:20 +01:00
Philip Patsch
e4865644c1 doc(servant-foreign): Document module
I spend some considerable time reverse engineering the module, so I
thought I’d write the documentation I would have liked to see.

The strategy here is that a user not necessarily has insight into how
servant works internally, or even how to write complex servant routes,
they just want to generate a list of endpoints and convert the `Req`
type into e.g. generated code in $language. Thus, they need to know
the semantics of all fields of Req, how they interact and how they
relate to a plain http route.

I made sure every `f` is replaced with `ftype`, so we have one
conventional way of referring to the foreign type argument everywhere.

Some enums are not set at all, they are marked as such.

`_reqBodyContentType` introduces a major restriction of the module, so
that is mentioned in the documentation for now, until the time it will
be fixed.

A few TODO’s describe places where types don’t make sense but would
introduce API-breaking changes, so these should probably be
simplified,
but bundled in one go.
2021-03-25 11:26:53 +01:00
Philip Patsch
07f7954cc6 chore(servant-foreign): remove dead type Frag
It is not used anywhere and also not exported.
2021-03-25 11:26:53 +01:00
Philip Patsch
a0265097e8 doc(servant-foreign): reorder imports
The imports were ordered in the worst possible way, with all
undocumented small type definitions coming first and the actual meat
of the module coming at the very end, mixed in with irrelevant
functions.

This inverses that toxic ordering, showing the main function
first (`listFromAPI`) and then the main data type (`Req`) and the main
class (`HasForeignType`).
2021-03-25 11:26:53 +01:00
Philip Patsch
c3a517cb4f doc(servant-foreign): Inflection docs & module docs 2021-03-25 11:26:53 +01:00
akhesaCaro
d4f7b0397d adding a compatibility warning in the cookbook 2021-03-23 14:10:14 +01:00
akhesaCaro
ba379287c8 reverting : removing DerivingVia extension (not compatible ghc < 8.6.1) 2021-03-23 14:09:49 +01:00
Gaël Deest
0743ca724d
Merge pull request #1403 from haskell-servant/ci-fix-followups
Try and improve caching on CI
2021-03-18 12:17:58 +01:00
Gaël Deest
a28856a11a Rename GH action file to match branch name 2021-03-18 12:10:10 +01:00
Gaël Deest
d6fb3826c8 Try and improve caching 2021-03-18 11:48:50 +01:00
Gaël Deest
53e943b5bb
Merge pull request #1397 from akhesaCaro/compiling_sqlite_simple_cookbook
Fixing Servant cookbooks part 1 (-testing + sqlite-simple)
2021-03-18 11:09:21 +01:00
akhesaCaro
269e546a6a sqlite-simple cookbook is working with sqlite-simple >= 0.4.5.0 2021-03-18 10:50:17 +01:00
akhesaCaro
dd1ab6dd36 fixing DocSpec tests to make them compile 2021-03-18 07:43:09 +01:00
Gaël Deest
a74d9d911e
Merge pull request #1402 from haskell-servant/fix-ci
Basic GitHub actions-based CI
2021-03-17 23:50:13 +01:00
Gaël Deest
507990cafe Switch to actively maintained haskell/actions/setup for CI 2021-03-17 23:22:20 +01:00
Gaël Deest
6452942a69 Cleanup 2021-03-17 23:22:20 +01:00
Gaël Deest
95d4f5030f Build / tests with GHCJS 2021-03-17 23:22:20 +01:00
Gaël Deest
579a372eb9 Enable all tested-with GHC versions 2021-03-17 23:22:20 +01:00
Gaël Deest
f9dd1f691f Try installing GHCJS via HVR's PPA 2021-03-17 23:22:20 +01:00
akhesacaro
9357583459 removing DerivingStrategies extension (not compatible ghc < 8.2.1) 2021-03-17 23:22:20 +01:00
akhesacaro
08b5e86536 (<>) needs the import of Data.Semigroup (ghc 8.2.2) 2021-03-17 23:22:20 +01:00
akhesacaro
86eb25018e removing DerivingVia extension (not compatible ghc < 8.6.1) 2021-03-17 23:22:20 +01:00
Gaël Deest
133ed94442 Re-enable doctests on CI 2021-03-17 23:22:20 +01:00
Gaël Deest
613dcf9ed5 Basic GitHub actions-based CI
- Setup a basic CI based on GitHub actions, with a somewhat limited build matrix.
- Disable cookbook/testing, because servant-quickcheck doesn't build anymore.
- Disable servant-docs on Cabal build, because of some test failures
  - The order of some JSON fields seems to be reversed in the output, need investigation.
- Fix test failures in servant-http-streams when `localhost` points to an IPv6 address rather than 127.0.0.1.
2021-03-17 23:22:20 +01:00
Gaël Deest
f1b5a64466 Remove Travis / haskell-ci configuration
Also remove mention about re-generating .travis.yml in README
2021-03-17 23:22:20 +01:00
Bodigrim
27173c9223
Allow bytestring-0.11 (#1386) 2020-12-16 11:04:49 +01:00
Ondřej Súkup
1f701aa97d
Update upper bound limit for http-client (#1384) 2020-12-11 22:52:32 +01:00
Ondřej Súkup
7412ac3472
Update upper bound limit of base64-bytestring (#1383) 2020-12-11 21:15:27 +01:00
Philipp Balzarek
7675e725d2
Bump base64-bytestring limit to 1.3 (#1382) 2020-12-11 00:32:16 +01:00
fisx
6ebb9e419e
Fix overlapping MimeRender instances (#1376) 2020-12-09 23:08:54 +01:00
fisx
505e6d346b
Merge pull request #1377 from haskell-servant/housekeeping
Housekeeping
2020-12-09 23:07:47 +01:00
Matthias Fischmann
fe849b27bf
bump stack.yaml resolver. 2020-12-06 16:04:02 +01:00
Matthias Fischmann
2f20c32704
Don't warn about necessary, expected type errors. 2020-12-06 16:03:19 +01:00
Intolerable
a8f584f80b
add HasLink instance for UVerb (#1370) 2020-12-06 14:19:35 +01:00
Alexey Kuleshevich
08579ca003
Update upper bounds for QuickCheck (#1375) 2020-12-05 20:49:11 +01:00
Domen Kožar
0bda65e315
links: import toUrlPiece to make it clear where it comes from 2020-12-05 17:00:03 +01:00
Arian van Putten
f7dc40ca8d
servant-client-core: depend on 0.18.2 (#1366) 2020-11-25 15:06:45 +03:00
Maxim Koltsov
57badc7c74
Add UVerb cookbook to cookbook build (#1365) 2020-11-22 18:08:01 +03:00
Maxim Koltsov
0ad2bd221a
Prepare 0.18.2 release (#1364) 2020-11-22 17:51:32 +03:00
Andrey Prokopenko
ce638027a8
Remove extra parameter from haddock section of Fragment instances (#1362) 2020-11-22 11:08:32 +01:00
Felix Yan
aa4f54e92e
Correct a typo in UVerb.hs (#1363) 2020-11-22 11:08:11 +01:00
Felix Yan
1d0b34df50
Allow QuickCheck 2.14 (#1359)
Builds fine and all tests pass.
2020-11-22 11:07:54 +01:00
Andrey Prokopenko
da0c83d318
Add URI fragment as a separate combinator (#1324) 2020-11-18 21:57:20 +03:00
Arian van Putten
339eec6a90
Fix overlapping instance for WithStatus (#1361)
We do not need the `ToJSON` instance for `WithStatus`
it would cause an overlap between:

```
ToJSON a => MimeRender JSON a
```

and

```
forall cty a.  MimeRendercty  a =>  MimeRender cty (WithStatus a)
```
and Servant just needs the `MimeRender` typeclass for it to work

* Add some more docs to the UVerb module

* cookbook/uverb: Change GHC versions

CI was complaining some version did not exist. Trying to bump
Also added 8.10.1

* doc/cookbook/uverb: Remove 8.4.4 from tested versions

CI was running into a cabal bug for some reason
2020-11-18 17:33:03 +01:00
Никита Размахнин
0ea692bb64 Add support of Pretty modifier for all verbs aliases
Minor import warning fix
2020-11-11 10:22:55 +03:00
Maxim Koltsov
8e2a654e0e
Merge pull request #1355 from haskell-servant/fix-constraints
Update inter-library version constraints
2020-11-05 11:16:14 +03:00
Maxim Koltsov
4c72c08830
Update inter-library version constraints 2020-11-05 10:48:38 +03:00
Maxim Koltsov
c95faa53fe
Merge pull request #1354 from haskell-servant/bump-versions
Bump versions
2020-11-04 22:28:15 +03:00
Maxim Koltsov
bd698cad3b
Bump version in preparation for new release 2020-11-04 17:06:51 +03:00
Maxim Koltsov
9e4a97eb78
Loosen upper bound on wai-extra 2020-11-04 15:11:15 +03:00
fisx
c1105899f4
union verbs (#1314) 2020-10-31 20:45:46 +01:00
fisx
64f3543034
bump "tested-with" ghc versions. (#1350) 2020-10-25 14:24:06 +01:00
Domen Kožar
81ce30302c
Merge pull request #1351 from haskell-servant/arianvp-patch-1
Update FUNDING.yml
2020-10-20 11:56:43 +02:00
Arian van Putten
6a66ca6d65
Update FUNDING.yml
I have a Github Sponsors account now. so people can sponsor me directly on Github.
2020-10-14 10:43:57 +02:00
Alejandro Serrano
0c0fe5b9d3
Loosen bound on base16-bytestring (#1348) 2020-10-12 13:28:35 +02:00
Cary Robbins
83bbc6d520
Add instance for ToSample NonEmpty (#1330) 2020-10-01 14:02:43 +02:00
Felix Yan
264846a61f
Allow hspec-wai 0.11 (#1343)
Builds fine and all tests pass.
2020-10-01 10:16:27 +02:00
Brandon Chinn
e364470dd9
Fix docs: emptyAPIServer -> emptyServer (#1344) 2020-10-01 09:58:50 +02:00
Szabo Gergely
e3a29addf4
Fix servant-docs code sample in README (#1335) 2020-09-03 06:43:10 +02:00
Matthias Fischmann
1760cc8527
Bump more package versions. 2020-09-01 14:21:26 +02:00
Matthias Fischmann
27f9662830
Remove x-revision in servant-conduit. (Oops.) 2020-09-01 12:24:38 +02:00
Matthias Fischmann
2906f0137c
Bump minor version of servant-conduit. 2020-09-01 12:21:57 +02:00
fisx
0d97d76c3b
Merge pull request #1332 from felixonmars/http-api-data-0.4.2
Allow http-api-data 0.4.2
2020-08-31 12:10:37 +02:00
Felix Yan
b4b649c8f4
Allow http-api-data 0.4.2
Builds fine and all tests pass here.
2020-08-30 15:26:48 +08:00
fisx
1e4872c8b6
Merge pull request #1328 from jkaye2012/patch-1
Minor rewording in Server tutorial
2020-08-02 14:57:42 +02:00
Jordan Kaye
9f8127ed54
Minor rewording in Server tutorial
Fixed an awkward wording in the Server tutorial.
2020-08-02 05:51:08 -06:00
Matthias Fischmann
858fb6cce5
Fix: remove x-revision from servant-server.cabal. 2020-07-31 20:25:31 +02:00
Matthias Fischmann
6dcb29bada
Update changelogs. 2020-07-31 20:19:07 +02:00
fisx
be679589bd
Merge pull request #1327 from maksbotan/maksbotan/bump-version
Reenable cookbook-testing
2020-07-31 13:24:40 +02:00
Maxim Koltsov
e93376939c
Reenable cookbook-testing 2020-07-31 13:32:17 +03:00
fisx
067ab350ef
Merge pull request #1326 from maksbotan/maksbotan/bump-version
Bump version to 0.18
2020-07-31 09:24:09 +02:00
Maxim Koltsov
d740c18992
Explicit export list in ErrorFormatter.hs 2020-07-30 19:05:46 +03:00
Maxim Koltsov
43cf589e0e
Bump version to 0.18 2020-07-30 19:03:58 +03:00
fisx
c5717a61a3
Merge pull request #1312 from maksbotan/maksbotan/configurable-combinator-errors
Configurable combinator errors
2020-07-30 17:15:59 +02:00
fisx
4a6db6e5ff
Merge pull request #1321 from andys8/patch-1
Docs: Hoist Server "Footnote"
2020-07-21 09:28:53 +02:00
Andy
55f5a78b1b
Docs: Hoist Server "Footnote" 2020-07-21 01:02:05 +02:00
Maxim Koltsov
cb0224d063
Add 8.10.1 to tested-with, haskell-ci regenerate 2020-07-17 17:17:45 +03:00
Maxim Koltsov
d94ad9df9b
Add cookbook entry for custom error formatters 2020-07-17 17:11:46 +03:00
Maxim Koltsov
bd2a813c1a
TEMP disable cookbook/testing 2020-07-17 17:11:46 +03:00
Maxim Koltsov
7218c66fd0
haskell-ci regenerate 2020-07-17 17:11:45 +03:00
Maxim Koltsov
1a09b1d3a4
Update GHC 8.8.x versions to 8.8.3 2020-07-17 17:10:31 +03:00
Maxim Koltsov
cb80fa6263
Add tests for custom error formatters 2020-07-17 17:10:31 +03:00
Maxim Koltsov
57f0b0b390
Make error messages from combinators configurable
Currently there is no way for Servant users to customize formatting of
error messages that arise when combinators can't parse URL or request
body, apart from reimplementing those combinators for themselves or
using middlewares.

This commit adds a possibility to specify custom error formatters
through Context.

Fixes #685
2020-07-17 17:10:31 +03:00
fisx
1f1f7f309a
Merge pull request #1318 from haskell-servant/ghc_810
Ghc 810
2020-07-03 08:12:40 +02:00
Matthias Fischmann
eaadc9ec1f
Relax upper bound for aeson. 2020-07-03 06:57:52 +02:00
Leif Warner
e3c4f5d85e
Bump doctest version used for ghc 8.10.1 2020-07-03 06:57:52 +02:00
Leif Warner
7ddc2e7b9e
Add GHC 8.10.1 to .travis.yml build. 2020-07-03 06:57:52 +02:00
Leif Warner
0530671ad6
Allow newer versions of base, template-haskell, lens, & unliftio-core 2020-07-03 06:57:52 +02:00
fisx
7f4ae61a01
Merge pull request #1310 from Taneb/knownstatus
Add KnownStatus typeclass
2020-06-13 17:02:41 +02:00
Nathan van Doorn
ff9da1cde4 Use GHC.TypeLits rather than TypeNats 2020-06-13 15:50:12 +01:00
Nathan van Doorn
6889d053c7 Add FlexibleInstances for earlier GHCs 2020-06-13 15:38:36 +01:00
Nathan van Doorn
a8184a2ee0 Add KnownStatus typeclass 2020-06-13 15:10:07 +01:00
Felix Yan
4b225c23d7
Allow aeson 1.5 in all components (#1309) 2020-06-12 15:02:39 -04:00
Felix Yan
c778a18372
Allow aeson 1.5 (#1302)
Builds fine and all tests pass here.
2020-06-12 02:38:09 -04:00
Jan Hrcek
8e7b538921
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
2020-06-10 12:36:23 -04:00
Jan Hrcek
b9d8fbcdc1
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
2020-06-06 00:43:51 -04:00
Teymour Aldridge
67cb564aef
Update README.md (#1300)
Grammatical fix to documentation
2020-06-06 00:30:16 -04:00
David Johnson
85599b944c
Build servant repo with nix. (#1288) 2020-06-06 00:04:32 -04:00
256 changed files with 10855 additions and 1438 deletions

2
.github/FUNDING.yml vendored
View file

@ -1,6 +1,6 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: [arianvp]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username

14
.github/run-ghcjs-tests.sh vendored Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
#
# cabal v2-test does not work with GHCJS
# See: https://github.com/haskell/cabal/issues/6175
#
# This invokes cabal-plan to figure out test binaries, and invokes them with node.
cabal-plan list-bins '*:test:*' | while read -r line
do
testpkg=$(echo "$line" | perl -pe 's/:.*//')
testexe=$(echo "$line" | awk '{ print $2 }')
echo "testing $textexe in package $textpkg"
(cd "$testpkg" && node "$testexe".jsexe/all.js)
done

148
.github/workflows/master.yml vendored Normal file
View file

@ -0,0 +1,148 @@
name: CI
# Trigger the workflow on push or pull request, but only for the master branch
on:
pull_request:
push:
branches: [master]
jobs:
cabal:
name: ${{ matrix.os }} / ghc ${{ matrix.ghc }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
cabal: ["3.6"]
ghc:
- "8.6.5"
- "8.8.4"
- "8.10.7"
- "9.0.2"
- "9.2.2"
- "9.4.2"
steps:
- uses: actions/checkout@v2
- uses: haskell/actions/setup@v1
id: setup-haskell-cabal
name: Setup Haskell
with:
ghc-version: ${{ matrix.ghc }}
cabal-version: ${{ matrix.cabal }}
- name: Freeze
run: |
cabal configure --enable-tests --enable-benchmarks --test-show-details=direct
cabal freeze
- uses: actions/cache@v2.1.3
name: Cache ~/.cabal/store and dist-newstyle
with:
path: |
${{ steps.setup-haskell-cabal.outputs.cabal-store }}
dist-newstyle
key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }}
restore-keys: |
${{ runner.os }}-${{ matrix.ghc }}-
- name: Configure
run: |
cabal install --ignore-project -j2 doctest --constraint='doctest ^>=0.20'
- name: Build
run: |
cabal build all
- name: Test
run: |
cabal test all
- name: Run doctests
run: |
# Necessary for doctest to be found in $PATH
export PATH="$HOME/.cabal/bin:$PATH"
DOCTEST="cabal repl --with-ghc=doctest --ghc-options=-w"
(cd servant && eval $DOCTEST)
(cd servant-client && eval $DOCTEST)
(cd servant-client-core && eval $DOCTEST)
(cd servant-http-streams && eval $DOCTEST)
(cd servant-docs && eval $DOCTEST)
(cd servant-foreign && eval $DOCTEST)
(cd servant-server && eval $DOCTEST)
(cd servant-machines && eval $DOCTEST)
(cd servant-conduit && eval $DOCTEST)
(cd servant-pipes && eval $DOCTEST)
# stack:
# name: stack / ghc ${{ matrix.ghc }}
# runs-on: ubuntu-latest
# strategy:
# matrix:
# stack: ["2.7.5"]
# ghc: ["8.10.7"]
# steps:
# - uses: actions/checkout@v2
# - uses: haskell/actions/setup@v1
# name: Setup Haskell Stack
# with:
# ghc-version: ${{ matrix.ghc }}
# stack-version: ${{ matrix.stack }}
# - uses: actions/cache@v2.1.3
# name: Cache ~/.stack
# with:
# path: ~/.stack
# key: ${{ runner.os }}-${{ matrix.ghc }}-stack
# - name: Install dependencies
# run: |
# stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --only-dependencies
# - name: Build
# run: |
# stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks
# - name: Test
# run: |
# stack test --system-ghc
ghcjs:
name: ubuntu-latest / ghcjs 8.6
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v2
- uses: cachix/install-nix-action@v13
with:
extra_nix_config: |
trusted-public-keys = ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=1aba6f367982bd6dd78ec2fda75ab246a62d32c5 cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
substituters = https://nixcache.reflex-frp.org https://cache.nixos.org/
- name: Setup
run: |
# Override cabal.project with the lightweight GHCJS one
cp cabal.ghcjs.project cabal.project
cat cabal.project
nix-shell ghcjs.nix --run "cabal v2-update && cabal v2-freeze"
- uses: actions/cache@v2.1.3
name: Cache ~/.cabal/store and dist-newstyle
with:
path: |
~/.cabal/store
dist-newstyle
key: ${{ runner.os }}-ghcjs8.6-${{ hashFiles('cabal.project.freeze') }}
restore-keys: |
${{ runner.os }}-ghcjs8.6-
- name: Build
run: |
nix-shell ghcjs.nix --run "cabal v2-build --ghcjs --enable-tests --enable-benchmarks all"
- name: Tests
run: |
nix-shell ghcjs.nix --run ".github/run-ghcjs-tests.sh"

6
.gitignore vendored
View file

@ -1,4 +1,5 @@
**/*/dist
*~
dist-*
.ghc.environment.*
/bin
@ -29,6 +30,11 @@ doc/_build
doc/venv
doc/tutorial/static/api.js
doc/tutorial/static/jq.js
shell.nix
.hspec-failures
# nix
result*
# local versions of things
servant-multipart

View file

@ -1,304 +0,0 @@
# This Travis job script has been generated by a script via
#
# haskell-ci '--config=cabal.haskell-ci' '--output=.travis.yml' 'cabal.project'
#
# For more information, see https://github.com/haskell-CI/haskell-ci
#
# version: 0.9.20200121
#
version: ~> 1.0
language: c
os: linux
dist: bionic
git:
# whether to recursively clone submodules
submodules: false
branches:
only:
- master
addons:
google: stable
cache:
directories:
- $HOME/.cabal/packages
- $HOME/.cabal/store
- $HOME/.hlint
before_cache:
- rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
# remove files that are regenerated by 'cabal update'
- rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
- rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
- rm -rfv $CABALHOME/packages/head.hackage
jobs:
include:
- compiler: ghcjs-8.4
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu bionic main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"},{"sourceline":"deb http://ppa.launchpad.net/hvr/ghcjs/ubuntu bionic main"},{"sourceline":"deb https://deb.nodesource.com/node_10.x bionic main","key_url":"https://deb.nodesource.com/gpgkey/nodesource.gpg.key"}],"packages":["ghcjs-8.4","cabal-install-3.0","ghc-8.4.4","nodejs"]}}
os: linux
- compiler: ghc-8.8.2
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu bionic main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.8.2","cabal-install-3.0"]}}
os: linux
- compiler: ghc-8.6.5
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu bionic main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.6.5","cabal-install-3.0"]}}
os: linux
- compiler: ghc-8.4.4
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu bionic main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.4.4","cabal-install-3.0"]}}
os: linux
- compiler: ghc-8.2.2
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu bionic main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.2.2","cabal-install-3.0"]}}
os: linux
- compiler: ghc-8.0.2
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu bionic main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.0.2","cabal-install-3.0"]}}
os: linux
before_install:
- |
if echo $CC | grep -q ghcjs; then
GHCJS=true;
else
GHCJS=false;
fi
- HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
- WITHCOMPILER="-w $HC"
- if $GHCJS ; then HC=${HC}js ; fi
- if $GHCJS ; then WITHCOMPILER="--ghcjs ${WITHCOMPILER}js" ; fi
- HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//')
- if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi
- HCPKG="$HC-pkg"
- unset CC
- CABAL=/opt/ghc/bin/cabal
- CABALHOME=$HOME/.cabal
- export PATH="$CABALHOME/bin:$PATH"
- TOP=$(pwd)
- "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
- echo $HCNUMVER
- CABAL="$CABAL -vnormal+nowrap"
- set -o pipefail
- TEST=--enable-tests
- BENCH=--enable-benchmarks
- HEADHACKAGE=false
- rm -f $CABALHOME/config
- |
echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
echo "remote-build-reporting: anonymous" >> $CABALHOME/config
echo "write-ghc-environment-files: always" >> $CABALHOME/config
echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
echo "world-file: $CABALHOME/world" >> $CABALHOME/config
echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
echo "install-dirs user" >> $CABALHOME/config
echo " prefix: $CABALHOME" >> $CABALHOME/config
echo "repository hackage.haskell.org" >> $CABALHOME/config
echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
install:
- ${CABAL} --version
- echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
- node --version
- echo $GHCJS
- GHCJOBS=-j2
- |
echo "program-default-options" >> $CABALHOME/config
echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config
- cat $CABALHOME/config
- rm -fv cabal.project cabal.project.local cabal.project.freeze
- travis_retry ${CABAL} v2-update -v
- if ! $GHCJS ; then (cd /tmp && ${CABAL} v2-install $WITHCOMPILER -j2 doctest --constraint='doctest ==0.16.2.*') ; fi
- if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe') ; fi
- if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 hspec-discover) ; fi
# Generate cabal.project
- rm -rf cabal.project cabal.project.local cabal.project.freeze
- touch cabal.project
- |
echo "packages: servant" >> cabal.project
if ! $GHCJS ; then echo "packages: servant-client" >> cabal.project ; fi
echo "packages: servant-client-core" >> cabal.project
if ! $GHCJS ; then echo "packages: servant-http-streams" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: servant-docs" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: servant-foreign" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: servant-server" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: doc/tutorial" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: servant-machines" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: servant-conduit" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: servant-pipes" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/basic-auth" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/curl-mock" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/basic-streaming" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/db-postgres-pool" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/file-upload" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/generic" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/pagination" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/testing" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/structuring-apis" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/using-custom-monad" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: doc/cookbook/using-free-client" >> cabal.project ; fi
- |
echo "constraints: foundation >=0.0.14" >> cabal.project
echo "constraints: memory <0.14.12 || >0.14.12" >> cabal.project
echo "constraints: base-compat ^>=0.11" >> cabal.project
echo "constraints: semigroups ^>=0.19" >> cabal.project
echo "constraints: sqlite-simple < 0" >> cabal.project
echo "allow-newer: servant-pagination-2.2.2:servant" >> cabal.project
echo "allow-newer: servant-pagination-2.2.2:servant-server" >> cabal.project
echo "optimization: False" >> cabal.project
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|servant|servant-client|servant-client-core|servant-conduit|servant-docs|servant-foreign|servant-http-streams|servant-machines|servant-pipes|servant-server|tutorial)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
- cat cabal.project || true
- cat cabal.project.local || true
- if [ -f "servant/configure.ac" ]; then (cd "servant" && autoreconf -i); fi
- if [ -f "servant-client/configure.ac" ]; then (cd "servant-client" && autoreconf -i); fi
- if [ -f "servant-client-core/configure.ac" ]; then (cd "servant-client-core" && autoreconf -i); fi
- if [ -f "servant-http-streams/configure.ac" ]; then (cd "servant-http-streams" && autoreconf -i); fi
- if [ -f "servant-docs/configure.ac" ]; then (cd "servant-docs" && autoreconf -i); fi
- if [ -f "servant-foreign/configure.ac" ]; then (cd "servant-foreign" && autoreconf -i); fi
- if [ -f "servant-server/configure.ac" ]; then (cd "servant-server" && autoreconf -i); fi
- if [ -f "doc/tutorial/configure.ac" ]; then (cd "doc/tutorial" && autoreconf -i); fi
- if [ -f "servant-machines/configure.ac" ]; then (cd "servant-machines" && autoreconf -i); fi
- if [ -f "servant-conduit/configure.ac" ]; then (cd "servant-conduit" && autoreconf -i); fi
- if [ -f "servant-pipes/configure.ac" ]; then (cd "servant-pipes" && autoreconf -i); fi
- if [ -f "doc/cookbook/basic-auth/configure.ac" ]; then (cd "doc/cookbook/basic-auth" && autoreconf -i); fi
- if [ -f "doc/cookbook/curl-mock/configure.ac" ]; then (cd "doc/cookbook/curl-mock" && autoreconf -i); fi
- if [ -f "doc/cookbook/basic-streaming/configure.ac" ]; then (cd "doc/cookbook/basic-streaming" && autoreconf -i); fi
- if [ -f "doc/cookbook/db-postgres-pool/configure.ac" ]; then (cd "doc/cookbook/db-postgres-pool" && autoreconf -i); fi
- if [ -f "doc/cookbook/file-upload/configure.ac" ]; then (cd "doc/cookbook/file-upload" && autoreconf -i); fi
- if [ -f "doc/cookbook/generic/configure.ac" ]; then (cd "doc/cookbook/generic" && autoreconf -i); fi
- if [ -f "doc/cookbook/pagination/configure.ac" ]; then (cd "doc/cookbook/pagination" && autoreconf -i); fi
- if [ -f "doc/cookbook/testing/configure.ac" ]; then (cd "doc/cookbook/testing" && autoreconf -i); fi
- if [ -f "doc/cookbook/structuring-apis/configure.ac" ]; then (cd "doc/cookbook/structuring-apis" && autoreconf -i); fi
- if [ -f "doc/cookbook/using-custom-monad/configure.ac" ]; then (cd "doc/cookbook/using-custom-monad" && autoreconf -i); fi
- if [ -f "doc/cookbook/using-free-client/configure.ac" ]; then (cd "doc/cookbook/using-free-client" && autoreconf -i); fi
- ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH}
- "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
- rm cabal.project.freeze
script:
- DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
# Packaging...
- echo 'Packaging...' && echo -en 'travis_fold:start:sdist\\r'
- ${CABAL} v2-sdist all
- echo -en 'travis_fold:end:sdist\\r'
# Unpacking...
- echo 'Unpacking...' && echo -en 'travis_fold:start:unpack\\r'
- mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
- cd ${DISTDIR} || false
- find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
- find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
- PKGDIR_servant="$(find . -maxdepth 1 -type d -regex '.*/servant-[0-9.]*')"
- PKGDIR_servant_client="$(find . -maxdepth 1 -type d -regex '.*/servant-client-[0-9.]*')"
- PKGDIR_servant_client_core="$(find . -maxdepth 1 -type d -regex '.*/servant-client-core-[0-9.]*')"
- PKGDIR_servant_http_streams="$(find . -maxdepth 1 -type d -regex '.*/servant-http-streams-[0-9.]*')"
- PKGDIR_servant_docs="$(find . -maxdepth 1 -type d -regex '.*/servant-docs-[0-9.]*')"
- PKGDIR_servant_foreign="$(find . -maxdepth 1 -type d -regex '.*/servant-foreign-[0-9.]*')"
- PKGDIR_servant_server="$(find . -maxdepth 1 -type d -regex '.*/servant-server-[0-9.]*')"
- PKGDIR_tutorial="$(find . -maxdepth 1 -type d -regex '.*/tutorial-[0-9.]*')"
- PKGDIR_servant_machines="$(find . -maxdepth 1 -type d -regex '.*/servant-machines-[0-9.]*')"
- PKGDIR_servant_conduit="$(find . -maxdepth 1 -type d -regex '.*/servant-conduit-[0-9.]*')"
- PKGDIR_servant_pipes="$(find . -maxdepth 1 -type d -regex '.*/servant-pipes-[0-9.]*')"
- PKGDIR_cookbook_basic_auth="$(find . -maxdepth 1 -type d -regex '.*/cookbook-basic-auth-[0-9.]*')"
- PKGDIR_cookbook_curl_mock="$(find . -maxdepth 1 -type d -regex '.*/cookbook-curl-mock-[0-9.]*')"
- PKGDIR_cookbook_basic_streaming="$(find . -maxdepth 1 -type d -regex '.*/cookbook-basic-streaming-[0-9.]*')"
- PKGDIR_cookbook_db_postgres_pool="$(find . -maxdepth 1 -type d -regex '.*/cookbook-db-postgres-pool-[0-9.]*')"
- PKGDIR_cookbook_file_upload="$(find . -maxdepth 1 -type d -regex '.*/cookbook-file-upload-[0-9.]*')"
- PKGDIR_cookbook_generic="$(find . -maxdepth 1 -type d -regex '.*/cookbook-generic-[0-9.]*')"
- PKGDIR_cookbook_pagination="$(find . -maxdepth 1 -type d -regex '.*/cookbook-pagination-[0-9.]*')"
- PKGDIR_cookbook_testing="$(find . -maxdepth 1 -type d -regex '.*/cookbook-testing-[0-9.]*')"
- PKGDIR_cookbook_structuring_apis="$(find . -maxdepth 1 -type d -regex '.*/cookbook-structuring-apis-[0-9.]*')"
- PKGDIR_cookbook_using_custom_monad="$(find . -maxdepth 1 -type d -regex '.*/cookbook-using-custom-monad-[0-9.]*')"
- PKGDIR_cookbook_using_free_client="$(find . -maxdepth 1 -type d -regex '.*/cookbook-using-free-client-[0-9.]*')"
# Generate cabal.project
- rm -rf cabal.project cabal.project.local cabal.project.freeze
- touch cabal.project
- |
echo "packages: ${PKGDIR_servant}" >> cabal.project
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_client}" >> cabal.project ; fi
echo "packages: ${PKGDIR_servant_client_core}" >> cabal.project
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_http_streams}" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_docs}" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_foreign}" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_server}" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: ${PKGDIR_tutorial}" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_machines}" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_conduit}" >> cabal.project ; fi
if ! $GHCJS ; then echo "packages: ${PKGDIR_servant_pipes}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_basic_auth}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_curl_mock}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_basic_streaming}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_db_postgres_pool}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_file_upload}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_generic}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_pagination}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_testing}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_structuring_apis}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_using_custom_monad}" >> cabal.project ; fi
if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo "packages: ${PKGDIR_cookbook_using_free_client}" >> cabal.project ; fi
- |
echo "constraints: foundation >=0.0.14" >> cabal.project
echo "constraints: memory <0.14.12 || >0.14.12" >> cabal.project
echo "constraints: base-compat ^>=0.11" >> cabal.project
echo "constraints: semigroups ^>=0.19" >> cabal.project
echo "constraints: sqlite-simple < 0" >> cabal.project
echo "allow-newer: servant-pagination-2.2.2:servant" >> cabal.project
echo "allow-newer: servant-pagination-2.2.2:servant-server" >> cabal.project
echo "optimization: False" >> cabal.project
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|servant|servant-client|servant-client-core|servant-conduit|servant-docs|servant-foreign|servant-http-streams|servant-machines|servant-pipes|servant-server|tutorial)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
- cat cabal.project || true
- cat cabal.project.local || true
- |
pkgdir() {
case $1 in
servant) echo ${PKGDIR_servant} ;;
servant-client) echo ${PKGDIR_servant_client} ;;
servant-client-core) echo ${PKGDIR_servant_client_core} ;;
servant-http-streams) echo ${PKGDIR_servant_http_streams} ;;
servant-docs) echo ${PKGDIR_servant_docs} ;;
servant-foreign) echo ${PKGDIR_servant_foreign} ;;
servant-server) echo ${PKGDIR_servant_server} ;;
tutorial) echo ${PKGDIR_tutorial} ;;
servant-machines) echo ${PKGDIR_servant_machines} ;;
servant-conduit) echo ${PKGDIR_servant_conduit} ;;
servant-pipes) echo ${PKGDIR_servant_pipes} ;;
cookbook-basic-auth) echo ${PKGDIR_cookbook_basic_auth} ;;
cookbook-curl-mock) echo ${PKGDIR_cookbook_curl_mock} ;;
cookbook-basic-streaming) echo ${PKGDIR_cookbook_basic_streaming} ;;
cookbook-db-postgres-pool) echo ${PKGDIR_cookbook_db_postgres_pool} ;;
cookbook-file-upload) echo ${PKGDIR_cookbook_file_upload} ;;
cookbook-generic) echo ${PKGDIR_cookbook_generic} ;;
cookbook-pagination) echo ${PKGDIR_cookbook_pagination} ;;
cookbook-testing) echo ${PKGDIR_cookbook_testing} ;;
cookbook-structuring-apis) echo ${PKGDIR_cookbook_structuring_apis} ;;
cookbook-using-custom-monad) echo ${PKGDIR_cookbook_using_custom_monad} ;;
cookbook-using-free-client) echo ${PKGDIR_cookbook_using_free_client} ;;
esac
}
- echo -en 'travis_fold:end:unpack\\r'
# Building with tests and benchmarks...
- echo 'Building with tests and benchmarks...' && echo -en 'travis_fold:start:build-everything\\r'
# build & run tests, build benchmarks
- ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all
- echo -en 'travis_fold:end:build-everything\\r'
# Testing...
- if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all ; fi
- if $GHCJS ; then cabal-plan list-bins '*:test:*' | while read -r line; do testpkg=$(echo "$line" | perl -pe 's/:.*//'); testexe=$(echo "$line" | awk '{ print $2 }'); echo "testing $textexe in package $textpkg"; (cd "$(pkgdir $testpkg)" && nodejs "$testexe".jsexe/all.js); done ; fi
# Doctest...
- echo 'Doctest...' && echo -en 'travis_fold:start:doctest\\r'
- perl -i -e 'while (<ARGV>) { print unless /package-id\s+(base-compat-batteries)-\d+(\.\d+)*/; }' .ghc.environment.*
- if ! $GHCJS ; then (cd ${PKGDIR_servant} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_client} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_client_core} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_http_streams} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_docs} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_foreign} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_server} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_machines} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_conduit} && doctest src) ; fi
- if ! $GHCJS ; then (cd ${PKGDIR_servant_pipes} && doctest src) ; fi
- echo -en 'travis_fold:end:doctest\\r'
# haddock...
- echo 'haddock...' && echo -en 'travis_fold:start:haddock\\r'
- if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all ; fi
- echo -en 'travis_fold:end:haddock\\r'
# REGENDATA ("0.9.20200121",["--config=cabal.haskell-ci","--output=.travis.yml","cabal.project"])
# EOF

View file

@ -79,8 +79,10 @@ not been a timely response to a PR, you can ping the Maintainers group (with
We encourage people to experiment with new combinators and instances - it is
one of the most powerful ways of using `servant`, and a wonderful way of
getting to know it better. If you do write a new combinator, we would love to
know about it! Either hop on #servant on freenode and let us know, or open an
issue with the `news` tag (which we will close when we read it).
know about it! Either hop on
[#haskell-servant on libera.chat](https://web.libera.chat/#haskell-servant) and
let us know, or open an issue with the `news` tag (which we will close when we
read it).
As for adding them to the main repo: maintaining combinators can be expensive,
since official combinators must have instances for all classes (and new classes
@ -103,7 +105,7 @@ the `news` label if you make a new package so we can know about it!
## 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
releases it may take some time in between releases.

View file

@ -1,32 +0,0 @@
# With common maintenance tasks
HC ?= ghc-8.4.4
all :
@echo "Don't try to make all at once!"
really-all :
$(MAKE) build-ghc
$(MAKE) build-ghc HC=ghc-8.0.2
$(MAKE) build-ghc HC=ghc-8.2.2
$(MAKE) build-ghc HC=ghc-8.6.5
$(MAKE) build-ghcjs
build-ghc :
cabal v2-build -w $(HC) all
build-ghcjs :
cabal v2-build --builddir=dist-newstyle-ghcjs --project-file=cabal.ghcjs.project all
packdeps :
packdeps */*.cabal
doctest : doctest-servant doctest-servant-server
perl -i -e 'while (<ARGV>) { print unless /package-id\s+base-compat-\d+(\.\d+)*/; }' .ghc.environment.*
doctest-servant :
(cd servant && doctest src)
(cd servant && doctest test/Servant/LinksSpec.hs)
doctest-servant-server :
(cd servant-server && doctest src)

View file

@ -9,11 +9,12 @@ introduces the core features of servant. After this article, you should be able
to write your first servant webservices, learning the rest from the haddocks'
examples.
The central documentation can be found [here](http://docs.servant.dev/).
The core documentation can be found [here](http://docs.servant.dev/).
Other blog posts, videos and slides can be found on the
[website](http://www.servant.dev/).
If you need help, drop by the IRC channel (#servant on freenode).
If you need help, drop by the IRC channel (#haskell-servant on libera.chat) or [mailing
list](https://groups.google.com/forum/#!forum/haskell-servant).
## Contributing
@ -31,7 +32,7 @@ See `CONTRIBUTING.md`
- It's a good idea to separate these steps, as tests often pass, if they compile :)
- See `cabal.project` to selectively `allow-newer`
- 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
- 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:
@ -39,29 +40,9 @@ See `CONTRIBUTING.md`
- `git push --tags`
- `cabal sdist` and `cabal upload`
## travis
## TechEmpower framework benchmarks
`.travis.yml` is generated using `make-travis-yml` tool, in
[multi-ghc-travis](https://github.com/haskell-hvr/multi-ghc-travis) repository.
To regenerate the script use (*note:* atm you need to comment `doc/cookbook/` packages).
```sh
runghc ~/Documents/other-haskell/multi-ghc-travis/make_travis_yml_2.hs regenerate
```
In case Travis jobs fail due failing build of dependency, you can temporarily
add `constraints` to the `cabal.project`, and regenerate the `.travis.yml`.
For example, the following will disallow single `troublemaker-13.37` package version:
```
constraints:
troublemaker <13.37 && > 13.37
```
## TechEmpower framework bechmarks
We develop & 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/
To verify (i.e. compile and test that it works)
@ -81,4 +62,10 @@ To compare with `reitit` (Clojure framework)
./tfb --mode benchmark --test reitit reitit-async reitit-jdbc servant servant-beam servant-psql-simple --type json plaintext db fortune
```
And visualise the results at https://www.techempower.com/benchmarks/#section=test
You can see the visualised results at https://www.techempower.com/benchmarks/#section=test
## Nix
A developer shell.nix file is provided in the `nix` directory
See [nix/README.md](nix/README.md)

View file

@ -2,9 +2,13 @@
packages:
servant/
servant-client/
servant-client-core/
servant-jsaddle/
-- we need to tell cabal we are using GHCJS
compiler: ghcjs
tests: True
-- Constraints so that reflex-platform provided packages are selected.
constraints: attoparsec == 0.13.2.2
constraints: hashable == 1.3.0.0

View file

@ -1,25 +0,0 @@
distribution: bionic
folds: all-but-test
branches: master
jobs-selection: any
google-chrome: True
ghcjs-tests: True
doctest: True
doctest-filter-packages: base-compat-batteries
doctest-skip: tutorial
-- https://github.com/haskell/cabal/issues/6176
ghcjs-tools: hspec-discover
-- We have inplace packages (servant-js) so we skip installing dependencies in a separate step
install-dependencies: False
-- this speed-ups the build a little, but we have to check these for release
no-tests-no-benchmarks: False
unconstrained: False
-- Don't run cabal check, as cookbook examples won't pass it
cabal-check: False
-- ghc-options: -j2
jobs: :2

View file

@ -1,11 +1,18 @@
packages:
servant/
servant-auth/servant-auth
servant-auth/servant-auth-client
servant-auth/servant-auth-docs
servant-auth/servant-auth-server
servant-auth/servant-auth-swagger
servant-client/
servant-client-core/
servant-http-streams/
servant-docs/
servant-foreign/
servant-server/
servant-swagger/
doc/tutorial/
-- servant streaming
@ -22,43 +29,26 @@ packages:
packages:
doc/cookbook/basic-auth
doc/cookbook/curl-mock
doc/cookbook/custom-errors
doc/cookbook/basic-streaming
doc/cookbook/db-postgres-pool
-- doc/cookbook/db-sqlite-simple
doc/cookbook/db-sqlite-simple
doc/cookbook/file-upload
doc/cookbook/generic
-- doc/cookbook/hoist-server-with-context
-- doc/cookbook/https
-- doc/cookbook/jwt-and-basic-auth/
doc/cookbook/hoist-server-with-context
doc/cookbook/https
doc/cookbook/jwt-and-basic-auth
doc/cookbook/pagination
-- doc/cookbook/sentry
doc/cookbook/testing
-- Commented out because servant-quickcheck currently doesn't build.
-- doc/cookbook/testing
doc/cookbook/uverb
doc/cookbook/structuring-apis
doc/cookbook/using-custom-monad
doc/cookbook/using-free-client
-- doc/cookbook/open-id-connect
doc/cookbook/managed-resource
tests: True
optimization: False
-- reorder-goals: True
constraints:
-- see https://github.com/haskell-infra/hackage-trustees/issues/119
foundation >=0.0.14,
memory <0.14.12 || >0.14.12
constraints: base-compat ^>=0.11
constraints: semigroups ^>=0.19
-- MonadFail
-- https://github.com/nurpax/sqlite-simple/issues/74
constraints: sqlite-simple < 0
-- allow-newer: sqlite-simple-0.4.16.0:semigroups
-- allow-newer: direct-sqlite-2.3.24:semigroups
-- needed for doctests
write-ghc-environment-files: always
-- https://github.com/chordify/haskell-servant-pagination/pull/12
allow-newer: servant-pagination-2.2.2:servant
allow-newer: servant-pagination-2.2.2:servant-server

9
changelog.d/1432 Normal file
View file

@ -0,0 +1,9 @@
synopsis: Fixes encoding of URL parameters in servant-client
prs: #1432
issues: #1418
description: {
Some applications use query parameters to pass arbitrary (non-unicode) binary
data. This change modifies how servant-client handles query parameters, so
that application developers can use `ToHttpApiData` to marshal binary data into
query parameters.
}

11
changelog.d/1469 Normal file
View file

@ -0,0 +1,11 @@
synopsis: Derive HasClient good response status from Verb status
prs: #1469
description: {
`HasClient` instances for the `Verb` datatype use `runRequest` in
`clientWithRoute` definitions.
This means that a request performed with `runClientM` will be successful if and
only if the endpoint specify a response status code >=200 and <300.
This change replaces `runRequest` with `runRequestAcceptStatus` in `Verb`
instances for the `HasClient` class, deriving the good response status from
the `Verb` status.
}

9
changelog.d/1477 Normal file
View file

@ -0,0 +1,9 @@
synopsis: Enable FlexibleContexts in Servant.API.ContentTypes
prs: #1477
description: {
Starting with GHC 9.2, UndecidableInstances no longer implies FlexibleContexts.
Add this extension where it's needed to make compilation succeed.
}

10
changelog.d/1529 Normal file
View file

@ -0,0 +1,10 @@
synopsis: Fix performRequest in servant-client-ghcjs
prs: #1529
description: {
performRequest function in servant-client-ghcjs was not compatible with the
latest RunClient typeclass. Added the acceptStatus parameter and fixed the
functionality to match what servant-client provides.
}

81
changelog.d/1556 Normal file
View file

@ -0,0 +1,81 @@
synopsis: Display capture hints in router layout
prs: #1556
description: {
This PR enhances the `Servant.Server.layout` function, which produces a textual description of the routing layout of an API. More precisely, it changes `<capture>` blocks, so that they display the name and type of the variable being captured instead.
Example:
For the following API
```haskell
type API =
"a" :> "d" :> Get '[JSON] NoContent
:<|> "b" :> Capture "x" Int :> Get '[JSON] Bool
:<|> "a" :> "e" :> Get '[JSON] Int
```
we previously got the following output:
```
/
├─ a/
│ ├─ d/
│ │ └─•
│ └─ e/
│ └─•
└─ b/
└─ <capture>/
├─•
└─•
```
now we get:
```
/
├─ a/
│ ├─ d/
│ │ └─•
│ └─ e/
│ └─•
└─ b/
└─ <x::Int>/
├─•
└─•
```
This change is achieved by the introduction of a CaptureHint type, which is passed as an extra argument to the CaptureRouter and CaptureAllRouter constructors for the Router' type.
CaptureHint values are then used in routerLayout, to display the name and type of captured values, instead of just `<capture>` previously.
N.B.:
Because the choice smart constructor for routers can aggregate Capture combinators with different capture hints, the Capture*Router constructors actually take a list of CaptureHint, instead of a single one.
This PR also introduces Spec tests for the routerLayout function.
Warning:
This change is potentially breaking, because it adds the constraint `Typeable a` to all types that are to be captured. Because all types are typeable since GHC 7.10, this is not as bad as it sounds ; it only break expressions where `a` is quantified in an expression with `Capture a`.
In those cases, the fix is easy: it suffices to add `Typeable a` to the left-hand side of the quantification constraint.
For instance, the following code will no longer compile:
```haskell
type MyAPI a = Capture "foo" a :> Get '[JSON] ()
myServer :: forall a. Server (MyAPI a)
myServer = const $ return ()
myApi :: forall a. Proxy (MyAPI a)
myApi = Proxy
app :: forall a. (FromHttpApiData a) => Application
app = serve (myApi @a) (myServer @a)
```
Indeed, `app` should be replaced with:
```haskell
app :: forall a. (FromHttpApiData a, Typeable a) => Application
app = serve (myApi @a) (myServer @a)
```
}

13
changelog.d/1569 Normal file
View file

@ -0,0 +1,13 @@
synopsis: Encode captures using toEncodedUrlPiece
prs: #1569
issues: #1511
description: {
The `servant-client` library now makes direct use of `toEncodedUrlPiece` from `ToHttpApiData`
to encode captured values when building the request path. It gives user freedom to implement
URL-encoding however they need.
Previous behavior was to use `toUrlPiece` and URL-encode its output using `toEncodedUrlPiece`
from the `Text` instance of `ToHttpApiData`. The issue with this approach is that
`ToHttpApiData Text` is overly zealous and also encodes characters, such as `*`, which are perfectly valid in a URL.
}

2
changelog.d/1573 Normal file
View file

@ -0,0 +1,2 @@
synopsis: Add API docs for ServerT
prs: #1573

12
changelog.d/1580 Normal file
View file

@ -0,0 +1,12 @@
synopsis: Allow IO in validationKeys
prs: #1580
issues: #1579
description: {
Currently validationKeys are a fixed JWKSet. This does not work with OIDC
providers such as AWS Cognito or Okta, which regularly fetching jwks_uri to
discover new and expired keys.
This change alters the type of validationKeys from JWKSet to IO JWKSet.
}

2
changelog.d/1589 Normal file
View file

@ -0,0 +1,2 @@
synopsis: Only include question mark for nonempty query strings
prs: 1589

2
changelog.d/1595 Normal file
View file

@ -0,0 +1,2 @@
synopsis: Run ClientEnv's makeClientRequest in IO.
prs: #1595

10
changelog.d/1606 Normal file
View file

@ -0,0 +1,10 @@
synopsis: Handle Cookies correctly for RunStreamingClient
prs: #1606
issues: #1605
description: {
Makes performWithStreamingRequest take into consideration the
CookieJar, which it previously didn't.
}

2
changelog.d/1638 Normal file
View file

@ -0,0 +1,2 @@
synopsis: Add Functor instance to AuthHandler.
prs: #1638

8
changelog.d/1649 Normal file
View file

@ -0,0 +1,8 @@
synopsis: Add HasStatus instance for Headers (that defers StatusOf to underlying value)
prs: #1649
description: {
Adds a new HasStatus (Headers hs a) instance (StatusOf (Headers hs a) = StatusOf a)
}

View file

@ -0,0 +1,16 @@
synopsis: Add sample cURL requests to generated documentation
prs: #1401
description: {
Add sample cURL requests to generated documentation.
Those supplying changes to the Request `header` field manually using
lenses will need to add a sample bytestring value.
`headers <>~ ["unicorn"]`
becomes
`headers <>~ [("unicorn", "sample value")]`
}

38
default.nix Normal file
View file

@ -0,0 +1,38 @@
with (builtins.fromJSON (builtins.readFile ./nix/nixpkgs.json));
{
pkgs ? import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
inherit sha256;
}) {}
, compiler ? "ghc883"
}:
let
overrides = self: super: {
servant = self.callCabal2nix "servant" ./servant {};
servant-docs = self.callCabal2nix "servant-docs" ./servant-docs {};
servant-pipes = self.callCabal2nix "servant-pipes" ./servant-pipes {};
servant-server = self.callCabal2nix "servant-server" ./servant-server {};
servant-client = self.callCabal2nix "servant-client" ./servant-client {};
servant-foreign = self.callCabal2nix "servant-foreign" ./servant-foreign {};
servant-conduit = self.callCabal2nix "servant-conduit" ./servant-conduit {};
servant-machines = self.callCabal2nix "servant-machines" ./servant-machines {};
servant-client-core = self.callCabal2nix "servant-client-core" ./servant-client-core {};
servant-http-streams = self.callCabal2nix "servant-http-streams" ./servant-http-streams {};
};
hPkgs = pkgs.haskell.packages.${compiler}.override { inherit overrides; };
in
with hPkgs;
{
inherit
servant
servant-client
servant-client-core
servant-conduit
servant-docs
servant-foreign
servant-http-streams
servant-machines
servant-pipes
servant-server;
}

View file

@ -10,7 +10,7 @@ BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@if [ ! -d venv ]; then echo "WARNING: There is no venv directory, did you forget to 'virtualenv venv'. Check building-the-docs file."; fi
@if [ ! -d venv ]; then echo "WARNING: There is no venv directory, did you forget to 'virtualenv venv'. Check README.md."; fi
@if [ ! "z$$(which $(SPHINXBUILD))" = "z$$(pwd)/venv/bin/sphinx-build" ]; then echo "WARNING: Did you forgot to 'source venv/bin/activate'"; fi
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View file

@ -46,7 +46,7 @@ master_doc = 'index'
# General information about the project.
project = u'Servant'
copyright = u'2018, Servant Contributors'
copyright = u'2022, Servant Contributors'
author = u'Servant Contributors'
# The version info for the project you're documenting, acts as replacement for
@ -169,4 +169,3 @@ texinfo_documents = [
source_parsers = {
'.lhs': CommonMarkParser,
}

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-basic-auth
version: 0.1
synopsis: Basic Authentication cookbook example
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-basic-auth
main-is: BasicAuth.lhs

View file

@ -8,7 +8,10 @@ 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.
- 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
things ourselves. (Note to self: `mapM` and `foldM` would be handy to have).

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-basic-streaming
version: 2.1
synopsis: Streaming in servant without streaming libs
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-basic-streaming
main-is: Streaming.lhs

View file

@ -1,7 +1,7 @@
# Generating mock curl calls
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.
Also, we will learn how to use the servant-foreign library to generate stuff from servant APIs.
@ -24,7 +24,6 @@ Language extensions and imports:
import Control.Lens ((^.))
import Data.Aeson
import Data.Aeson.Text
import Data.Monoid ((<>))
import Data.Proxy (Proxy (Proxy))
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
@ -86,7 +85,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 +129,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 +156,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 +188,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 +200,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.

View file

@ -1,16 +1,19 @@
cabal-version: 2.2
name: cookbook-curl-mock
version: 0.1
synopsis: Generate curl mock requests cookbook example
homepage: http://docs.servant.dev
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbock-curl-mock
if impl(ghc >= 9.2)
-- generic-arbitrary is incompatible
buildable: False
main-is: CurlMock.lhs
build-depends: base == 4.*
, aeson

View file

@ -0,0 +1,189 @@
# Customizing errors from Servant
Servant handles a lot of parsing and validation of the input request. When it can't parse something: query
parameters, URL parts or request body, it will return appropriate HTTP codes like 400 Bad Request.
These responses will contain the error message in their body without any formatting. However, it is often
desirable to be able to provide custom formatting for these error messages, for example, to wrap them in JSON.
Recently Servant got a way to add such formatting. This Cookbook chapter demonstrates how to use it.
Extensions and imports:
```haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
import Data.Aeson
import Data.Proxy
import Data.Text
import GHC.Generics
import Network.Wai
import Network.Wai.Handler.Warp
import Servant
import Data.String.Conversions
(cs)
import Servant.API.ContentTypes
```
The API (from `greet.hs` example in Servant sources):
```haskell
-- | A greet message data type
newtype Greet = Greet { _msg :: Text }
deriving (Generic, Show)
instance FromJSON Greet
instance ToJSON Greet
-- API specification
type TestApi =
-- GET /hello/:name?capital={true, false} returns a Greet as JSON
"hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON] Greet
-- POST /greet with a Greet as JSON in the request body,
-- returns a Greet as JSON
:<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] Greet
-- DELETE /greet/:greetid
:<|> "greet" :> Capture "greetid" Text :> Delete '[JSON] NoContent
testApi :: Proxy TestApi
testApi = Proxy
-- Server-side handlers.
--
-- There's one handler per endpoint, which, just like in the type
-- that represents the API, are glued together using :<|>.
--
-- Each handler runs in the 'Handler' monad.
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 . toUpper $ "Hello, " <> name
postGreetH greet = return greet
deleteGreetH _ = return NoContent
```
## Error formatters
`servant-server` provides an `ErrorFormatter` type to specify how the error message will be
formatted. A formatter is just a function accepting three parameters:
- `TypeRep` from `Data.Typeable`: this is a runtime representation of the type of the combinator
(like `Capture` or `ReqBody`) that generated the error. It can be used to display its name (with
`show`) or even dynamically dispatch on the combinator type. See the docs for `Data.Typeable` and
`Type.Reflection` modules.
- `Request`: full information for the request that led to the error.
- `String`: specific error message from the combinator.
The formatter is expected to produce a `ServerError` which will be returned from the handler.
Additionally, there is `NotFoundErrorFormatter`, which accepts only `Request` and can customize the
error in case when no route can be matched (HTTP 404).
Let's make two formatters. First one will wrap our error in a JSON:
```json
{
"error": "ERROR MESSAGE",
"combinator": "NAME OF THE COMBINATOR"
}
```
Additionally, this formatter will examine the `Accept` header of the request and generate JSON
message only if client can accept it.
```haskell
customFormatter :: ErrorFormatter
customFormatter tr req err =
let
-- aeson Value which will be sent to the client
value = object ["combinator" .= show tr, "error" .= err]
-- Accept header of the request
accH = getAcceptHeader req
in
-- handleAcceptH is Servant's function that checks whether the client can accept a
-- certain message type.
-- In this case we call it with "Proxy '[JSON]" argument, meaning that we want to return a JSON.
case handleAcceptH (Proxy :: Proxy '[JSON]) accH value of
-- If client can't handle JSON, we just return the body the old way
Nothing -> err400 { errBody = cs err }
-- Otherwise, we return the JSON formatted body and set the "Content-Type" header.
Just (ctypeH, body) -> err400
{ errBody = body
, errHeaders = [("Content-Type", cs ctypeH)]
}
notFoundFormatter :: NotFoundErrorFormatter
notFoundFormatter req =
err404 { errBody = cs $ "Not found path: " <> rawPathInfo req }
```
If you don't need to react to the `Accept` header, you can just unconditionally return the JSON like
this (with `encode` from `Data.Aeson`):
```
err400
{ errBody = encode body
, errHeaders = [("Content-Type", "application/json")]
}
```
## Passing formatters to Servant
Servant uses the Context to configure formatters. You only need to add a value of type
`ErrorFormatters` to your context. This is a record with the following fields:
- `bodyParserErrorFormatter :: ErrorFormatter`
- `urlParseErrorFormatter :: ErrorFormatter`
- `headerParseErrorFormatter :: ErrorFormatter`
- `notFoundErrorFormatter :: NotFoundErrorFormatter`
Default formatters are exported as `defaultErrorFormatters`, so you can use record update syntax to
set the only ones you need:
```haskell
customFormatters :: ErrorFormatters
customFormatters = defaultErrorFormatters
{ bodyParserErrorFormatter = customFormatter
, notFoundErrorFormatter = notFoundFormatter
}
```
And at last, use `serveWithContext` to run your server as usual:
```haskell
app :: Application
app = serveWithContext testApi (customFormatters :. EmptyContext) server
main :: IO ()
main = run 8000 app
```
Now if we try to request something with a wrong body, we will get a nice error:
```
$ http -j POST localhost:8000/greet 'foo=bar'
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=utf-8
Date: Fri, 17 Jul 2020 13:34:18 GMT
Server: Warp/3.3.12
Transfer-Encoding: chunked
{
"combinator": "ReqBody'",
"error": "Error in $: parsing Main.Greet(Greet) failed, key \"_msg\" not found"
}
```
Notice the `Content-Type` header set by our combinator.

View file

@ -0,0 +1,25 @@
cabal-version: 2.2
name: cookbook-custom-errors
version: 0.1
synopsis: Return custom error messages from combinators
homepage: http://docs.servant.dev
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-custom-errors
main-is: CustomErrors.lhs
build-depends: base == 4.*
, aeson
, servant
, servant-server
, string-conversions
, text
, wai
, warp
default-language: Haskell2010
ghc-options: -Wall -pgmL markdown-unlit
build-tool-depends: markdown-unlit:markdown-unlit

View file

@ -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.
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.

View file

@ -1,13 +1,13 @@
cabal-version: 2.2
name: mysql-basics
version: 0.1.0.0
synopsis: Simple MySQL API cookbook example
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
executable run
hs-source-dirs: .

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-db-postgres-pool
version: 0.1
synopsis: Simple PostgreSQL connection pool cookbook example
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-db-postgres-pool
main-is: PostgresPool.lhs

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-db-sqlite-simple
version: 0.1
synopsis: Simple SQLite DB cookbook example
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-db-sqlite-simple
main-is: DBConnection.lhs
@ -23,7 +23,7 @@ executable cookbook-db-sqlite-simple
, http-types >= 0.12
, markdown-unlit >= 0.4
, http-client >= 0.5
, sqlite-simple >= 0.4
, sqlite-simple >= 0.4.5.0
, transformers
default-language: Haskell2010
ghc-options: -Wall -pgmL markdown-unlit

View file

@ -90,8 +90,8 @@ startServer = run 8080 (serve api upload)
Finally, a main function that brings up our server and
sends some test request with `http-client` (and not
servant-client this time, has servant-multipart does not
yet have support for client generation.
servant-client this time, as servant-multipart does not
yet have support for client generation).
``` haskell
main :: IO ()

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-file-upload
version: 0.1
synopsis: File upload cookbook example
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-file-upload
main-is: FileUpload.lhs

View file

@ -43,13 +43,13 @@ api :: Proxy (ToServantApi 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`.
## Links
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:
```haskell
@ -67,7 +67,7 @@ routesLinks = allFieldLinks
## Client
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`.
```haskell

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-generic
version: 0.1
synopsis: Using custom monad to pass a state between handlers
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-using-custom-monad
main-is: Generic.lhs

View file

@ -254,7 +254,7 @@ loginHandler cookieSettings jwtSettings form = do
liftIO $ pushLogStrLn logset $ toLogStr logMsg
throwError err401
Just applyCookies -> do
let successMsg = logMsg{message = "AdminUser succesfully authenticated!"}
let successMsg = logMsg{message = "AdminUser successfully authenticated!"}
liftIO $ pushLogStrLn logset $ toLogStr successMsg
pure $ applyCookies successMsg
loginHandler _ _ _ = throwError err401
@ -287,7 +287,7 @@ mkApp cfg cs jwts ctx =
(flip runReaderT ctx) (adminServer cs jwts)
```
One footenote: because we'd like our logs to be in JSON form, we'll also create a `Middleware` object
One footnote: because we'd like our logs to be in JSON form, we'll also create a `Middleware` object
so that `Warp` *also* will emit logs as JSON. This will ensure *all* logs are emitted as JSON:
```haskell

View file

@ -1,17 +1,17 @@
cabal-version: 2.2
name: cookbook-hoist-server-with-context
version: 0.0.1
synopsis: JWT and basic access authentication with a Custom Monad cookbook example
description: Using servant-auth to support both JWT-based and basic
authentication.
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
category: Servant
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-hoist-server-with-context
main-is: HoistServerWithContext.lhs
@ -24,7 +24,7 @@ executable cookbook-hoist-server-with-context
, servant
, servant-server
, servant-auth >= 0.3.2
, servant-auth-server
, servant-auth-server >= 0.4.4.0
, time
, warp >= 3.2
, wai >= 3.2

View file

@ -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

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-https
version: 0.1
synopsis: HTTPS cookbook example
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-https
main-is: Https.lhs
@ -17,7 +17,7 @@ executable cookbook-https
, servant-server
, wai >= 3.2
, warp >= 3.2
, warp-tls >= 3.2
, warp-tls >= 3.2.9
, markdown-unlit >= 0.4
default-language: Haskell2010
ghc-options: -Wall -pgmL markdown-unlit

View file

@ -6,8 +6,8 @@ how to solve many common problems with servant. If you're
interested in contributing examples of your own, feel free
to open an issue or a pull request on
`our github repository <https://github.com/haskell-servant/servant>`_
or even to just get in touch with us on the **#servant** IRC channel
on freenode or on
or even to just get in touch with us on the `**#haskell-servant** IRC channel
on libera.chat <https://web.libera.chat/#haskell-servant>_ or on
`the mailing list <https://groups.google.com/forum/#!forum/haskell-servant>`_.
The scope is very wide. Simple and fancy authentication schemes,
@ -25,6 +25,8 @@ you name it!
db-postgres-pool/PostgresPool.lhs
using-custom-monad/UsingCustomMonad.lhs
using-free-client/UsingFreeClient.lhs
custom-errors/CustomErrors.lhs
uverb/UVerb.lhs
basic-auth/BasicAuth.lhs
basic-streaming/Streaming.lhs
jwt-and-basic-auth/JWTAndBasicAuth.lhs
@ -35,3 +37,4 @@ you name it!
sentry/Sentry.lhs
testing/Testing.lhs
open-id-connect/OpenIdConnect.lhs
managed-resource/ManagedResource.lhs

View file

@ -1,17 +1,17 @@
cabal-version: 2.2
name: cookbook-jwt-and-basic-auth
version: 0.0.1
synopsis: JWT and basic access authentication cookbook example
description: Using servant-auth to support both JWT-based and basic
authentication.
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
category: Servant
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-jwt-and-basic-auth
main-is: JWTAndBasicAuth.lhs
@ -22,7 +22,7 @@ executable cookbook-jwt-and-basic-auth
, servant
, servant-client
, servant-server
, servant-auth ==0.3.*
, servant-auth == 0.4.*
, servant-auth-server >= 0.3.1.0
, warp >= 3.2
, wai >= 3.2

View file

@ -0,0 +1,114 @@
# Request-lifetime Managed Resources
Let's see how we can write a handle that uses a resource managed by Servant. The resource is created automatically by Servant when the server recieves a request, and the resource is automatically destroyed when the server is finished handling a request.
As usual, we start with a little bit of throat clearing.
``` haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
import Control.Concurrent
import Control.Exception (bracket, throwIO)
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
import Data.Acquire
import Network.HTTP.Client (newManager, defaultManagerSettings)
import Network.Wai.Handler.Warp
import Servant
import Servant.Client
import System.IO
```
Here we define an API type that uses the `WithResource` combinator. The server handler for an endpoint with a `WithResource res` component will receive a value of that type as an argument.
``` haskell
type API = WithResource Handle :> ReqBody '[PlainText] String :> Post '[JSON] NoContent
api :: Proxy API
api = Proxy
```
But this resource value has to come from somewhere. Servant obtains the value using an Acquire provided in the context. The Acquire knows how to both create and destroy resources of a particular type.
``` haskell
appContext :: Context '[Acquire Handle]
appContext = acquireHandle :. EmptyContext
acquireHandle :: Acquire Handle
acquireHandle = mkAcquire newHandle closeHandle
newHandle :: IO Handle
newHandle = do
putStrLn "opening file"
h <- openFile "test.txt" AppendMode
putStrLn "opened file"
return h
closeHandle :: Handle -> IO ()
closeHandle h = do
putStrLn "closing file"
hClose h
putStrLn "closed file"
```
Now we create the handler which will use this resource. This handler will write the request message to the System.IO.Handle which was provided to us. In some situations the handler will succeed, but in some in will fail. In either case, Servant will clean up the resource for us.
``` haskell
server :: Server API
server = writeToFile
where writeToFile :: (ReleaseKey, Handle) -> String -> Handler NoContent
writeToFile (_, h) msg = case msg of
"illegal" -> error "wait, that's illegal!"
legalMsg -> liftIO $ do
putStrLn "writing file"
hPutStrLn h legalMsg
putStrLn "wrote file"
return NoContent
```
Finally we run the server in the background while we post messages to it.
``` haskell
runApp :: IO ()
runApp = run 8080 (serveWithContext api appContext $ server)
postMsg :: String -> ClientM NoContent
postMsg = client api
main :: IO ()
main = do
mgr <- newManager defaultManagerSettings
bracket (forkIO $ runApp) killThread $ \_ -> do
ms <- flip runClientM (mkClientEnv mgr (BaseUrl Http "localhost" 8080 "")) $ do
liftIO $ putStrLn "sending hello message"
_ <- postMsg "hello"
liftIO $ putStrLn "sending illegal message"
_ <- postMsg "illegal"
liftIO $ putStrLn "done"
print ms
```
This program prints
```
sending hello message
opening file
opened file
writing file
wrote file
closing file
closed file
sending illegal message
opening file
opened file
closing file
closed file
wait, that's illegal!
CallStack (from HasCallStack):
error, called at ManagedResource.lhs:63:24 in main:Main
Left (FailureResponse (Request {requestPath = (BaseUrl {baseUrlScheme = Http, baseUrlHost = "localhost", baseUrlPort = 8080, baseUrlPath = ""},""), requestQueryString = fromList [], requestBody = Just ((),text/plain;charset=utf-8), requestAccept = fromList [], requestHeaders = fromList [], requestHttpVersion = HTTP/1.1, requestMethod = "POST"}) (Response {responseStatusCode = Status {statusCode = 500, statusMessage = "Internal Server Error"}, responseHeaders = fromList [("Transfer-Encoding","chunked"),("Date","Thu, 24 Nov 2022 21:04:47 GMT"),("Server","Warp/3.3.23"),("Content-Type","text/plain; charset=utf-8")], responseHttpVersion = HTTP/1.1, responseBody = "Something went wrong"}))
```
and appends to a file called `test.txt`. We can see from the output that when a legal message is sent, the file is opened, written to, and closed. We can also see that when an illegal message is sent, the file is opened but not written to. Crucially, it is still closed even though the handler threw an exception.

View file

@ -0,0 +1,30 @@
cabal-version: 2.2
name: cookbook-managed-resource
version: 0.1
synopsis: Simple managed resource cookbook example
homepage: http://docs.servant.dev/
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
tested-with: GHC==9.4.2
executable cookbook-managed-resource
main-is: ManagedResource.lhs
build-depends: base == 4.*
, text >= 1.2
, aeson >= 1.2
, servant
, servant-client
, servant-server
, warp >= 3.2
, wai >= 3.2
, http-types >= 0.12
, markdown-unlit >= 0.4
, http-client >= 0.5
, transformers
, resourcet
default-language: Haskell2010
ghc-options: -Wall -pgmL markdown-unlit
build-tool-depends: markdown-unlit:markdown-unlit

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: open-id-connect
version: 0.1
synopsis: OpenId Connect with Servant example
homepage: http://haskell-servant.readthedocs.org/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >= 1.10
tested-with: GHC==8.4.4, GHC==8.6.5
tested-with: GHC==8.6.5
executable cookbook-openidconnect
main-is: OpenIdConnect.lhs

View file

@ -8,8 +8,8 @@ some login token would be saved in the user agent local storage.
Workflow:
1. user is presentend with a login button,
2. when the user click on the button it is redirected to the OIDC
1. user is presented with a login button,
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,9 +221,9 @@ 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
JWT containing all the informations we need. Depending on the scopes we
asked we might get more informations.
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.
``` haskell
-- | @AuthInfo@
@ -248,16 +248,16 @@ instance JSON.ToJSON AuthInfo where
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.
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
sucessfully 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.
successfully logged in. From there we need to make a request to the token
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.
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
with some generated informations.
Here is the code that displays the homepage.
It should contain a link to the `/login` URL.
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`.
@ -366,7 +366,7 @@ instance ToMarkup Homepage where
We need some helpers to generate random string for generating state and API Keys.
``` 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
genRandomBS :: IO ByteString
genRandomBS = do

View file

@ -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
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:
- `Accept-Ranges`: A comma-separated list of fields upon which a range can be defined
@ -127,7 +127,7 @@ defaultRange =
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
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.
@ -148,7 +148,7 @@ type MyHeaders =
```
`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
-- type MyHeaders =
@ -165,7 +165,7 @@ not, _servant-pagination_ provides an easy way to lift a collection of resources
#### Server
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.
If no such header is provided, we will end up receiving `Nothing`. Otherwise, it will be possible
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>]`
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:
- 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`
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`
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.
@ -235,8 +235,8 @@ instance HasPagination Color "hex" where
#### Parsing Options
By default, `servant-pagination` provides an implementation of `getRangeOptions` for each
`HasPagination` instance. However, this can be overwritten when defining the instance to provide
your own options. This options come into play when a `Range` header is received and isn't fully
`HasPagination` instance. However, this can be overridden when defining the instance to provide
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.
For instance, let's say we wanted to change the default limit to `5` in a new range on

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-pagination
version: 2.1
synopsis: Pagination with Servant example
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-pagination
main-is: Pagination.lhs

View file

@ -79,14 +79,14 @@ 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
- 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.
In the second step it actually sends our message to Sentry with the `register` function. Its arguments are:
- 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`

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-sentry
version: 0.1
synopsis: Collecting runtime exceptions using Sentry
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-sentry
main-is: Sentry.lhs

View file

@ -144,7 +144,7 @@ simpleAPIServer
:: m [a]
-> (i -> m a)
-> (a -> m NoContent)
-> Server (SimpleAPI name a i) m
-> ServerT (SimpleAPI name a i) m
simpleAPIServer listAs getA postA =
listAs :<|> getA :<|> postA

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-structuring-apis
version: 0.1
synopsis: Example that shows how APIs can be structured
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-structuring-apis
main-is: StructuringApis.lhs

View file

@ -1,16 +1,16 @@
cabal-version: 2.2
name: cookbook-testing
version: 0.0.1
synopsis: Common testing patterns in Servant apps
description: This recipe includes various strategies for writing tests for Servant.
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
category: Servant
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-testing
main-is: Testing.lhs
@ -23,7 +23,7 @@ executable cookbook-testing
, servant
, servant-client
, servant-server
, servant-quickcheck
, servant-quickcheck >= 0.0.10
, http-client
, http-types >= 0.12
, hspec

View file

@ -1,6 +1,6 @@
# 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 start with a pretty standard set of imports and definition of the model:

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-using-custom-monad
version: 0.1
synopsis: Using custom monad to pass a state between handlers
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-using-custom-monad
main-is: UsingCustomMonad.lhs

View file

@ -119,7 +119,7 @@ Now we can use `servant-client`'s internals to convert servant's `Request`
to http-client's `Request`, and we can inspect it:
```haskell
let req' = I.defaultMakeClientRequest burl req
req' <- I.defaultMakeClientRequest burl req
putStrLn $ "Making request: " ++ show req'
```
@ -141,7 +141,7 @@ and calling the continuation. We should get a `Pure` value.
Pure 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

View file

@ -1,14 +1,14 @@
cabal-version: 2.2
name: cookbook-using-free-client
version: 0.1
synopsis: Using Free client
homepage: http://docs.servant.dev/
license: BSD3
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7
executable cookbook-using-free-client
main-is: UsingFreeClient.lhs

View file

@ -0,0 +1,223 @@
# Listing alternative responses and exceptions in your API types
Servant allows you to talk about the exceptions you throw in your API
types. This is not limited to actual exceptions, you can write
handlers that respond with arbitrary open unions of types.
## Compatibility
:warning: This cookbook is compatible with GHC 8.6.1 or higher :warning:
## Preliminaries
```haskell
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wall -Wno-orphans #-}
import Control.Concurrent (threadDelay)
import Control.Concurrent.Async (async)
import Control.Monad (when)
import Control.Monad.Except (ExceptT (..), MonadError (..), MonadTrans (..), runExceptT)
import Data.Aeson (FromJSON (..), ToJSON (..))
import Data.Aeson.Encode.Pretty (encodePretty)
import Data.String.Conversions (cs)
import Data.Swagger (ToSchema)
import Data.Typeable (Proxy (Proxy))
import qualified GHC.Generics as GHC
import qualified Network.HTTP.Client as Client
import qualified Network.Wai.Handler.Warp as Warp
import Servant.API
import Servant.Client
import Servant.Server
import Servant.Swagger
```
## The API
This looks like a `Verb`-based routing table, except that `UVerb` has
no status, and carries a list of response types rather than a single
one. Each entry in the list carries its own response code.
```haskell
type API =
"fisx" :> Capture "bool" Bool
:> UVerb 'GET '[JSON] '[FisxUser, WithStatus 303 String]
:<|> "arian"
:> UVerb 'GET '[JSON] '[WithStatus 201 ArianUser]
```
Here are the details:
```haskell
data FisxUser = FisxUser {name :: String}
deriving (Eq, Show, GHC.Generic)
instance ToJSON FisxUser
instance FromJSON FisxUser
instance ToSchema FisxUser
-- | 'HasStatus' allows us to can get around 'WithStatus' if we want
-- to, and associate the status code with our resource types directly.
--
-- (To avoid orphan instances and make it more explicit what's in the
-- API and what isn't, we could even introduce a newtype 'Resource'
-- that wraps all the types we're using in our routing table, and then
-- define lots of 'HasStatus' instances for @Resource This@ and
-- @Resource That@.)
instance HasStatus FisxUser where
type StatusOf FisxUser = 203
data ArianUser = ArianUser
deriving (Eq, Show, GHC.Generic)
instance ToJSON ArianUser
instance FromJSON ArianUser
instance ToSchema ArianUser
```
## Server, Client, Swagger
You can just respond with any of the elements of the union in handlers.
```haskell
fisx :: Bool -> Handler (Union '[FisxUser, WithStatus 303 String])
fisx True = respond (FisxUser "fisx")
fisx False = respond (WithStatus @303 ("still fisx" :: String))
arian :: Handler (Union '[WithStatus 201 ArianUser])
arian = respond (WithStatus @201 ArianUser)
```
You can create client functions like you're used to:
```
fisxClient :: Bool -> ClientM (Union '[FisxUser, WithStatus 303 String])
arianClient :: ClientM (Union '[WithStatus 201 ArianUser])
(fisxClient :<|> arianClient) = client (Proxy @API)
```
... and that's basically it! Here are a few sample commands that
show you how the swagger docs look like and how you can handle the
result unions in clients:
```
main :: IO ()
main = do
putStrLn . cs . encodePretty $ toSwagger (Proxy @API)
_ <- async . Warp.run 8080 $ serve (Proxy @API) (fisx :<|> arian)
threadDelay 50000
mgr <- Client.newManager Client.defaultManagerSettings
let cenv = mkClientEnv mgr (BaseUrl Http "localhost" 8080 "")
result <- runClientM (fisxClient True) cenv
print $ foldMapUnion (Proxy @Show) show <$> result
print $ matchUnion @FisxUser <$> result
print $ matchUnion @(WithStatus 303 String) <$> result
pure ()
```
## Idiomatic exceptions
Since `UVerb` (probably) will mostly be used for error-like responses, it may be desirable to be able to early abort handler, like with current servant one would use `throwError` with `ServerError`.
```haskell
newtype UVerbT xs m a = UVerbT { unUVerbT :: ExceptT (Union xs) m a }
deriving (Functor, Applicative, Monad, MonadTrans)
-- | Deliberately hide 'ExceptT's 'MonadError' instance to be able to use
-- underlying monad's instance.
instance MonadError e m => MonadError e (UVerbT xs m) where
throwError = lift . throwError
catchError (UVerbT act) h = UVerbT $ ExceptT $
runExceptT act `catchError` (runExceptT . unUVerbT . h)
-- | This combinator runs 'UVerbT'. It applies 'respond' internally, so the handler
-- may use the usual 'return'.
runUVerbT :: (Monad m, HasStatus x, IsMember x xs) => UVerbT xs m x -> m (Union xs)
runUVerbT (UVerbT act) = either id id <$> runExceptT (act >>= respond)
-- | Short-circuit 'UVerbT' computation returning one of the response types.
throwUVerb :: (Monad m, HasStatus x, IsMember x xs) => x -> UVerbT xs m a
throwUVerb = UVerbT . ExceptT . fmap Left . respond
```
Example usage:
```haskell
data Foo = Foo Int Int Int
deriving (Show, Eq, GHC.Generic, ToJSON)
deriving HasStatus via WithStatus 200 Foo
data Bar = Bar
deriving (Show, Eq, GHC.Generic)
instance ToJSON Bar
h :: Handler (Union '[Foo, WithStatus 400 Bar])
h = runUVerbT $ do
when ({- something bad -} True) $
throwUVerb $ WithStatus @400 Bar
when ({- really bad -} False) $
throwError $ err500
-- a lot of code here...
return $ Foo 1 2 3
```
## Related Work
There is the [issue from
2017](https://github.com/haskell-servant/servant/issues/841) that was
resolved by the introduction of `UVerb`, with a long discussion on
alternative designs.
[servant-checked-exceptions](https://hackage.haskell.org/package/servant-checked-exceptions)
is a good solution to the problem, but it restricts the user to JSON
and a very specific envelop encoding for the union type, which is
often not acceptable. (One good reason for this design choice is that
it makes writing clients easier, where you need to get to the union
type from one representative, and you don't want to run several
parsers in the hope that the ones that should will always error out so
you can try until the right one returns a value.)
[servant-exceptions](https://github.com/ch1bo/servant-exceptions) is
another shot at the problem. It is inspired by
servant-checked-exceptions, so it may be worth taking a closer look.
The README claims that
[cardano-sl](https://github.com/input-output-hk/cardano-sl) also has
some code for generalized error handling.
In an earier version of the `UVerb` implementation, we have used some
code from
[world-peace](https://hackage.haskell.org/package/world-peace), but
that package itself wasn't flexible enough, and we had to use
[sop-core](https://hackage.haskell.org/package/sop-core) to implement
the `HasServer` instance.
Here is a blog post we found on the subject:
https://lukwagoallan.com/posts/unifying-servant-server-error-responses
(If you have anything else, please add it here or let us know.)
```haskell
main :: IO ()
main = return ()
```

View file

@ -0,0 +1,35 @@
cabal-version: 2.2
name: cookbook-uverb
version: 0.0.1
synopsis: How to use the 'UVerb' type.
description: Listing alternative responses and exceptions in your API types.
homepage: http://docs.servant.dev/
license: BSD-3-Clause
license-file: ../../../servant/LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
category: Servant
build-type: Simple
tested-with: GHC==8.6.5, GHC==8.8.4, GHC==8.10.7
executable cookbook-uverb
main-is: UVerb.lhs
build-depends: base == 4.*
, aeson >= 1.2
, aeson-pretty >= 0.8.8
, async
, http-client
, mtl
, servant
, servant-client
, servant-server
, servant-swagger
, string-conversions
, swagger2
, wai
, warp
if impl(ghc >= 9)
buildable: False
default-language: Haskell2010
ghc-options: -Wall -pgmL markdown-unlit
build-tool-depends: markdown-unlit:markdown-unlit

View file

@ -12,7 +12,7 @@ Helpful Links
`https://github.com/haskell-servant/servant/issues <https://github.com/haskell-servant/servant/issues>`_
- the irc channel:
``#servant`` on freenode
`#haskell-servant on libera.chat <https://web.libera.chat/#haskell-servant>`_
- the mailing list:
`groups.google.com/forum/#!forum/haskell-servant <https://groups.google.com/forum/#!forum/haskell-servant>`_

View file

@ -1,3 +1,4 @@
recommonmark==0.5.0
Sphinx==1.8.4
sphinx_rtd_theme>=0.4.2
jinja2<3.1.0

View file

@ -389,3 +389,30 @@ One example for this is if you want to serve a directory of static files along
with the rest of your API. But you can plug in everything that is an
`Application`, e.g. a whole web application written in any of the web
frameworks that support `wai`.
Be mindful! The `servant-server`'s router works by pattern-matching the
different routes that are composed using `:<|>`. `Raw`, as an escape hatch,
matches any route that hasn't been matched by previous patterns. Therefore,
any subsequent route will be silently ignored.
``` haskell
type UserAPI14 = Raw
:<|> "users" :> Get '[JSON] [User]
-- In this situation, the /users endpoint
-- will not be reachable because the Raw
-- endpoint matches requests before
```
A simple way to avoid this pitfall is to either use `Raw` as the last
definition, or to always have it under a static path.
``` haskell
type UserAPI15 = "files" :> Raw
-- The raw endpoint is under the /files
-- static path, so it won't match /users.
:<|> "users" :> Get '[JSON] [User]
type UserAPI16 = "users" :> Get '[JSON] [User]
:<|> Raw
-- The Raw endpoint is matched last, so
-- it won't overlap another endpoint.
```

View file

@ -47,7 +47,6 @@ module Authentication where
import Data.Aeson (ToJSON)
import Data.ByteString (ByteString)
import Data.Map (Map, fromList)
import Data.Monoid ((<>))
import qualified Data.Map as Map
import Data.Proxy (Proxy (Proxy))
import Data.Text (Text)
@ -108,7 +107,7 @@ API with "private." Additionally, the private parts of our API use the
realm for this authentication is `"foo-realm"`).
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
design that we'll briefly discuss. If you want the **TL;DR**: we supply a lookup
function to servant's new `Context` primitive.
@ -133,7 +132,7 @@ combinator. Using `Context`, we can supply a function of type
handler. This will allow the handler to check authentication and return a `User`
to downstream handlers if successful.
In practice we wrap `BasicAuthData -> Handler` into a slightly
In practice we wrap `BasicAuthData -> Handler User` into a slightly
different function to better capture the semantics of basic authentication:
``` haskell ignore
@ -260,7 +259,7 @@ this.
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
contain a key from which we can lookup a `Account`.
contain a key from which we can lookup an `Account`.
```haskell
-- | An account type that we "fetch from the database" after
@ -274,7 +273,7 @@ database = fromList [ ("key1", Account "Anne Briggs")
, ("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.
lookupAccount :: ByteString -> Handler Account
lookupAccount key = case Map.lookup key database of
@ -346,7 +345,7 @@ genAuthServerContext = authHandler :. EmptyContext
-- | 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
-- 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
genAuthServer :: Server AuthGenAPI
genAuthServer =
@ -385,7 +384,7 @@ Creating a generalized, ad-hoc authentication scheme was fairly straight
forward:
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`).
3. Create a value of `AuthHandler Request Account` which encapsulates the
authentication logic (`Request -> Handler Account`). This function

View file

@ -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
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:
``` haskell
@ -173,7 +173,7 @@ hoistClientAPI = Proxy
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`.
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
stack. `hoistClient` is a very simple solution to the problem of "changing" the monad
the clients run in.

View file

@ -77,7 +77,7 @@ instance ToSample HelloMessage where
[ ("When a value is provided for 'name'", HelloMessage "Hello, Alp")
, ("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 "Alp" "alp@foo.com" 26 ["haskell", "mathematics"]
@ -108,7 +108,7 @@ apiDocs = docs exampleAPI
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
## GET /hello

View file

@ -228,13 +228,13 @@ data CommonGeneratorOptions = CommonGeneratorOptions
{
-- | function generating function names
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
-- | name of the callback parameter when the request was successful
, successCallback :: Text
-- | name of the callback parameter when the request reported an error
, 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
-- | a prefix that should be prepended to the URL in the generated JS
, urlPrefix :: Text

View file

@ -183,7 +183,7 @@ users2 = [isaac, albert]
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
the same order as in in the API type.
the same order as in the API type.
``` haskell
server2 :: Server UserAPI2
@ -313,8 +313,8 @@ For reference, here's a list of some combinators from **servant**:
## The `FromHttpApiData`/`ToHttpApiData` classes
Wait... How does **servant** know how to decode the `Int`s from the URL? Or how
to decode a `ClientInfo` value from the request body? This is what this and the
following two sections address.
to decode a `ClientInfo` value from the request body? The following three sections will
help us answer these questions.
`Capture`s and `QueryParam`s are represented by some textual value in URLs.
`Header`s are similarly represented by a pair of a header name and a
@ -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
@ -716,7 +716,7 @@ $ curl --verbose http://localhost:8081/myfile.txt
>
< HTTP/1.1 404 Not Found
[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
@ -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
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
@ -1135,7 +1135,7 @@ true
### An arrow is a reader too.
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
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
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
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*).
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

View file

@ -6,46 +6,10 @@ This is an introductory tutorial to **servant**. Whilst browsing is fine, it mak
Any comments, issues or feedback about the tutorial can be submitted
to `servant's issue tracker <http://github.com/haskell-servant/servant/issues>`_.
cabal-install
--------
The whole tutorial is a `cabal <https://cabal.readthedocs.io/en/latest/>`_
project and can be built locally as follows:
.. code-block:: bash
$ git clone https://github.com/haskell-servant/servant.git
$ cd servant
# build
$ cabal new-build tutorial
# load in ghci to play with it
$ cabal new-repl tutorial
stack
--------
The servant `stack <https://docs.haskellstack.org/en/stable/README/>`_ template includes the working tutorial. To initialize this template, run:
.. code-block:: bash
$ stack new myproj servant
$ cd myproj
# build
$ stack build
# start server
$ stack exec myproj-exe
The code can be found in the `*.lhs` files under `doc/tutorial/` in the
repository. Feel free to edit it while you're reading this documentation and
see the effect of your changes.
`Nix <https://nixos.org/nix/>`_ users should feel free to take a look at
the `nix/shell.nix` file in the repository and use it to provision a suitable
environment to build and run the examples.
.. toctree::
:maxdepth: 1
install.rst
ApiType.lhs
Server.lhs
Client.lhs

68
doc/tutorial/install.rst Normal file
View file

@ -0,0 +1,68 @@
Install
========
cabal-install
--------
The whole tutorial is a `cabal <https://cabal.readthedocs.io/en/latest/>`_
project and can be built locally as follows:
.. code-block:: bash
$ git clone https://github.com/haskell-servant/servant.git
$ cd servant
# build
$ cabal new-build tutorial
# load in ghci to play with it
$ cabal new-repl tutorial
stack
--------
The servant `stack <https://docs.haskellstack.org/en/stable/README/>`_ template includes the working tutorial. To initialize this template, run:
.. code-block:: bash
$ stack new myproj servant
$ cd myproj
# build
$ stack build
# start server
$ stack exec myproj-exe
The code can be found in the `*.lhs` files under `doc/tutorial/` in the
repository. Feel free to edit it while you're reading this documentation and
see the effect of your changes.
nix
--------
`Nix <https://nixos.org/nix/>`_ users should feel free to take a look at
the `nix/shell.nix` file in the repository and use it to provision a suitable
environment to build and run the examples.
Note for Ubuntu users
--------
Ubuntu's packages for `ghc`, `cabal`, and `stack` are years out of date.
If the instructions above fail for you,
try replacing the Ubuntu packages with up-to-date versions.
First remove the installed versions:
.. code-block:: bash
# remove the obsolete versions
$ sudo apt remove ghc haskell-stack cabal-install
Then install fresh versions of the Haskell toolchain
using the `ghcup <https://www.haskell.org/ghcup/install/>`_ installer.
As of February 2022, one easy way to do this is by running a bootstrap script:
.. code-block:: bash
$ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
The script is interactive and will prompt you for details about what
you want installed and where. To install manually,
see `the detailed instructions <https://www.haskell.org/ghcup/install/#manual-install>`_.

View file

@ -1,3 +1,4 @@
cabal-version: 2.2
name: tutorial
version: 0.10
synopsis: The servant tutorial
@ -6,18 +7,14 @@ description:
<http://docs.servant.dev/>
homepage: http://docs.servant.dev/
category: Servant, Documentation
license: BSD3
license: BSD-3-Clause
license-file: LICENSE
author: Servant Contributors
maintainer: haskell-servant-maintainers@googlegroups.com
build-type: Simple
cabal-version: >=1.10
tested-with:
GHC==8.0.2
GHC==8.2.2
GHC==8.4.4
GHC==8.6.5
GHC==8.8.2
GHC==8.8.3, GHC ==8.10.7
extra-source-files:
static/index.html
static/ui.js
@ -67,10 +64,10 @@ library
, blaze-markup >= 0.8.0.0 && < 0.9
, cookie >= 0.4.3 && < 0.5
, js-jquery >= 3.3.1 && < 3.4
, lucid >= 2.9.11 && < 2.10
, random >= 1.1 && < 1.2
, lucid >= 2.9.11 && < 2.12
, random >= 1.1 && < 1.3
, servant-js >= 0.9 && < 0.10
, time >= 1.6.0.1 && < 1.10
, time >= 1.6.0.1 && < 1.13
-- For legacy tools, we need to specify build-depends too
build-depends: markdown-unlit >= 0.5.0 && <0.6

22
ghcjs.nix Normal file
View file

@ -0,0 +1,22 @@
let reflex-platform = import (builtins.fetchTarball
{ name = "reflex-platform";
url = "https://github.com/reflex-frp/reflex-platform/archive/1aba6f367982bd6dd78ec2fda75ab246a62d32c5.tar.gz";
}) {};
pkgs = import ./nix/nixpkgs.nix; in
pkgs.stdenv.mkDerivation {
name = "ghcjs-shell";
buildInputs =
[ (reflex-platform.ghcjs.ghcWithPackages (p: with p; [
attoparsec
hashable
]))
pkgs.cabal-install
pkgs.gmp
pkgs.haskellPackages.cabal-plan
pkgs.haskellPackages.hspec-discover
pkgs.nodejs
pkgs.perl
pkgs.zlib
];
}

View file

@ -21,3 +21,21 @@ a particular ghc version, e.g:
``` sh
$ nix-shell nix/shell.nix --argstr compiler ghcHEAD
```
**Possible GHC versions**
- `ghc865Binary`
- `ghc884`
- `ghc8104` - default
- `ghc901`
### Cabal users
GHC version can be chosen via the nix-shell parameter
`cabal build all`
### Stack version
Since the ghc version is set by the LTS version, it is preferable to use the `ghc8104` version parameter for the nix-shell.
`stack --no-nix --system-ghc <command>`

4
nix/nixpkgs.json Normal file
View file

@ -0,0 +1,4 @@
{
"rev" : "05f0934825c2a0750d4888c4735f9420c906b388",
"sha256" : "1g8c2w0661qn89ajp44znmwfmghbbiygvdzq0rzlvlpdiz28v6gy"
}

4
nix/nixpkgs.nix Normal file
View file

@ -0,0 +1,4 @@
import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/refs/tags/21.05.tar.gz";
sha256 = "sha256:1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36";
}) {}

View file

@ -1,6 +1,6 @@
{ pkgs ? import <nixpkgs> {}
, compiler ? "ghc822"
{ compiler ? "ghc8104"
, tutorial ? false
, pkgs ? import ./nixpkgs.nix
}:
with pkgs;
@ -9,13 +9,12 @@ let
ghc = haskell.packages.${compiler}.ghcWithPackages (_: []);
docstuffs = python3.withPackages (ps: with ps; [ recommonmark sphinx sphinx_rtd_theme ]);
in
stdenv.mkDerivation {
name = "servant-dev";
buildInputs = [ ghc zlib python3 wget ]
buildInputs = [ ghc zlib python3 wget cabal-install postgresql openssl stack haskellPackages.hspec-discover ]
++ (if tutorial then [docstuffs postgresql] else []);
shellHook = ''
eval $(grep export ${ghc}/bin/ghc)
export LD_LIBRARY_PATH="${zlib}/lib";
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"${zlib}/lib";
'';
}

1
servant-auth/README.md Symbolic link
View file

@ -0,0 +1 @@
servant-auth-server/README.lhs

View file

@ -0,0 +1 @@
:set -isrc -itest -idoctest/ghci-wrapper/src

View file

@ -0,0 +1,26 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [PVP Versioning](https://pvp.haskell.org/).
## [Unreleased]
## [0.4.1.0] - 2020-10-06
- Support generic Bearer token auth
## [0.4.0.0] - 2019-03-08
## Changed
- #145 Support servant-0.16 in tests @domenkozar
- #145 Drop GHC 7.10 support @domenkozar
## [0.3.3.0] - 2018-06-18
### Added
- Support for GHC 8.4 by @phadej
- Support for servant-0.14 by @phadej
- Changelog by @domenkozar

View file

@ -0,0 +1,31 @@
Copyright Julian K. Arni (c) 2015
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Julian K. Arni nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

View file

@ -0,0 +1,80 @@
cabal-version: 2.2
name: servant-auth-client
version: 0.4.1.0
synopsis: servant-client/servant-auth compatibility
description: This package provides instances that allow generating clients from
<https://hackage.haskell.org/package/servant servant>
APIs that use
<https://hackage.haskell.org/package/servant-auth servant-auth's> @Auth@ combinator.
.
For a quick overview of the usage, see the <https://github.com/haskell-servant/servant/tree/master/servant-auth#readme README>.
category: Web, Servant, Authentication
homepage: https://github.com/haskell-servant/servant/tree/master/servant-auth#readme
bug-reports: https://github.com/haskell-servant/servant/issues
author: Julian K. Arni
maintainer: jkarni@gmail.com
copyright: (c) Julian K. Arni
license: BSD-3-Clause
license-file: LICENSE
tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1
build-type: Simple
extra-source-files:
CHANGELOG.md
source-repository head
type: git
location: https://github.com/haskell-servant/servant
library
hs-source-dirs:
src
default-extensions: ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
ghc-options: -Wall
build-depends:
base >= 4.10 && < 4.18
, bytestring >= 0.10.6.0 && < 0.12
, containers >= 0.5.6.2 && < 0.7
, servant-auth == 0.4.*
, servant >= 0.13 && < 0.20
, servant-client-core >= 0.13 && < 0.20
exposed-modules:
Servant.Auth.Client
Servant.Auth.Client.Internal
default-language: Haskell2010
test-suite spec
type: exitcode-stdio-1.0
main-is: Spec.hs
hs-source-dirs:
test
default-extensions: ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
ghc-options: -Wall
build-tool-depends: hspec-discover:hspec-discover >=2.5.5 && <2.10
-- dependencies with bounds inherited from the library stanza
build-depends:
base
, servant-client
, servant-auth
, servant
, servant-auth-client
-- test dependencies
build-depends:
hspec >= 2.5.5 && < 2.10
, QuickCheck >= 2.11.3 && < 2.15
, aeson >= 1.3.1.1 && < 3
, bytestring >= 0.10.6.0 && < 0.12
, http-client >= 0.5.13.1 && < 0.8
, http-types >= 0.12.2 && < 0.13
, servant-auth-server >= 0.4.2.0 && < 0.5
, servant-server >= 0.13 && < 0.20
, time >= 1.5.0.1 && < 1.13
, transformers >= 0.4.2.0 && < 0.6
, wai >= 3.2.1.2 && < 3.3
, warp >= 3.2.25 && < 3.4
, jose >= 0.10 && < 0.11
other-modules:
Servant.Auth.ClientSpec
default-language: Haskell2010

View file

@ -0,0 +1,3 @@
module Servant.Auth.Client (Token(..), Bearer) where
import Servant.Auth.Client.Internal (Bearer, Token(..))

View file

@ -0,0 +1,64 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
#if __GLASGOW_HASKELL__ == 800
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
#endif
module Servant.Auth.Client.Internal where
import qualified Data.ByteString as BS
import Data.Monoid
import Data.Proxy (Proxy (..))
import Data.String (IsString)
import GHC.Exts (Constraint)
import GHC.Generics (Generic)
import Servant.API ((:>))
import Servant.Auth
import Servant.Client.Core
import Data.Sequence ((<|))
-- | A simple bearer token.
newtype Token = Token { getToken :: BS.ByteString }
deriving (Eq, Show, Read, Generic, IsString)
type family HasBearer xs :: Constraint where
HasBearer (Bearer ': xs) = ()
HasBearer (JWT ': xs) = ()
HasBearer (x ': xs) = HasBearer xs
HasBearer '[] = BearerAuthNotEnabled
class BearerAuthNotEnabled
-- | @'HasBearer' auths@ is nominally a redundant constraint, but ensures we're not
-- trying to send a token to an API that doesn't accept them.
instance (HasBearer auths, HasClient m api) => HasClient m (Auth auths a :> api) where
type Client m (Auth auths a :> api) = Token -> Client m api
clientWithRoute m _ req (Token token)
= clientWithRoute m (Proxy :: Proxy api)
$ req { requestHeaders = ("Authorization", headerVal) <| requestHeaders req }
where
headerVal = "Bearer " <> token
#if MIN_VERSION_servant_client_core(0,14,0)
hoistClientMonad pm _ nt cl = hoistClientMonad pm (Proxy :: Proxy api) nt . cl
#endif
-- * Authentication combinators
-- | A Bearer token in the Authorization header:
--
-- @Authorization: Bearer <token>@
--
-- This can be any token recognized by the server, for example,
-- a JSON Web Token (JWT).
--
-- Note that, since the exact way the token is validated is not specified,
-- this combinator can only be used in the client. The server would not know
-- how to validate it, while the client does not care.
-- If you want to implement Bearer authentication in your server, you have to
-- choose a specific combinator, such as 'JWT'.
data Bearer

View file

@ -0,0 +1,161 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
module Servant.Auth.ClientSpec (spec) where
import Crypto.JOSE (JWK,
KeyMaterialGenParam (OctGenParam),
genJWK)
import Data.Aeson (FromJSON (..), ToJSON (..))
import qualified Data.ByteString.Lazy as BSL
import Data.Time (UTCTime, defaultTimeLocale,
parseTimeOrError)
import GHC.Generics (Generic)
import Network.HTTP.Client (Manager, defaultManagerSettings,
newManager)
import Network.HTTP.Types (status401)
import Network.Wai.Handler.Warp (testWithApplication)
import Servant
import Servant.Client (BaseUrl (..), Scheme (Http),
ClientError (FailureResponse),
#if MIN_VERSION_servant_client(0,16,0)
ResponseF(..),
#elif MIN_VERSION_servant_client(0,13,0)
GenResponse(..),
#elif MIN_VERSION_servant_client(0,12,0)
Response(..),
#endif
client)
import System.IO.Unsafe (unsafePerformIO)
import Test.Hspec
import Test.QuickCheck
#if MIN_VERSION_servant_client(0,13,0)
import Servant.Client (mkClientEnv, runClientM)
#elif MIN_VERSION_servant_client(0,9,0)
import Servant.Client (ClientEnv (..), runClientM)
#else
import Control.Monad.Trans.Except (runExceptT)
#endif
#if !MIN_VERSION_servant_server(0,16,0)
#define ClientError ServantError
#endif
import Servant.Auth.Client
import Servant.Auth.Server
import Servant.Auth.Server.SetCookieOrphan ()
spec :: Spec
spec = describe "The JWT combinator" $ do
hasClientSpec
------------------------------------------------------------------------------
-- * HasClient {{{
hasClientSpec :: Spec
hasClientSpec = describe "HasClient" $ around (testWithApplication $ return app) $ do
let mkTok :: User -> Maybe UTCTime -> IO Token
mkTok user mexp = do
Right tok <- makeJWT user jwtCfg mexp
return $ Token $ BSL.toStrict tok
it "succeeds when the token does not have expiry" $ \port -> property $ \user -> do
tok <- mkTok user Nothing
v <- getIntClient tok mgr (BaseUrl Http "localhost" port "")
v `shouldBe` Right (length $ name user)
it "succeeds when the token is not expired" $ \port -> property $ \user -> do
tok <- mkTok user (Just future)
v <- getIntClient tok mgr (BaseUrl Http "localhost" port "")
v `shouldBe` Right (length $ name user)
it "fails when token is expired" $ \port -> property $ \user -> do
tok <- mkTok user (Just past)
#if MIN_VERSION_servant_client(0,16,0)
Left (FailureResponse _ (Response stat _ _ _))
#elif MIN_VERSION_servant_client(0,12,0)
Left (FailureResponse (Response stat _ _ _))
#elif MIN_VERSION_servant_client(0,11,0)
Left (FailureResponse _ stat _ _)
#else
Left (FailureResponse stat _ _)
#endif
<- getIntClient tok mgr (BaseUrl Http "localhost" port "")
stat `shouldBe` status401
getIntClient :: Token -> Manager -> BaseUrl -> IO (Either ClientError Int)
#if MIN_VERSION_servant(0,13,0)
getIntClient tok m burl = runClientM (client api tok) (mkClientEnv m burl)
#elif MIN_VERSION_servant(0,9,0)
getIntClient tok m burl = runClientM (client api tok) (ClientEnv m burl)
#else
getIntClient tok m burl = runExceptT $ client api tok m burl
#endif
-- }}}
------------------------------------------------------------------------------
-- * API and Server {{{
type API = Auth '[JWT] User :> Get '[JSON] Int
api :: Proxy API
api = Proxy
theKey :: JWK
theKey = unsafePerformIO . genJWK $ OctGenParam 256
{-# NOINLINE theKey #-}
mgr :: Manager
mgr = unsafePerformIO $ newManager defaultManagerSettings
{-# NOINLINE mgr #-}
app :: Application
app = serveWithContext api ctx server
where
ctx = cookieCfg :. jwtCfg :. EmptyContext
jwtCfg :: JWTSettings
jwtCfg = defaultJWTSettings theKey
cookieCfg :: CookieSettings
cookieCfg = defaultCookieSettings
server :: Server API
server = getInt
where
getInt :: AuthResult User -> Handler Int
getInt (Authenticated u) = return . length $ name u
getInt _ = throwAll err401
-- }}}
------------------------------------------------------------------------------
-- * Utils {{{
past :: UTCTime
past = parseTimeOrError True defaultTimeLocale "%Y-%m-%d" "1970-01-01"
future :: UTCTime
future = parseTimeOrError True defaultTimeLocale "%Y-%m-%d" "2070-01-01"
-- }}}
------------------------------------------------------------------------------
-- * Types {{{
data User = User
{ name :: String
, _id :: String
} deriving (Eq, Show, Read, Generic)
instance FromJWT User
instance ToJWT User
instance FromJSON User
instance ToJSON User
instance Arbitrary User where
arbitrary = User <$> arbitrary <*> arbitrary
-- }}}

View file

@ -0,0 +1 @@
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}

View file

@ -0,0 +1 @@
:set -isrc -itest -idoctest/ghci-wrapper/src

View file

@ -0,0 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [PVP Versioning](https://pvp.haskell.org/).
## [Unreleased]
## [0.2.10.0] - 2018-06-18
### Added
- Support for GHC 8.4 by @phadej
- Changelog by @domenkozar

View file

@ -0,0 +1,31 @@
Copyright Julian K. Arni (c) 2015
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Julian K. Arni nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,33 @@
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wall #-}
module Main (main) where
#ifndef MIN_VERSION_cabal_doctest
#define MIN_VERSION_cabal_doctest(x,y,z) 0
#endif
#if MIN_VERSION_cabal_doctest(1,0,0)
import Distribution.Extra.Doctest ( defaultMainWithDoctests )
main :: IO ()
main = defaultMainWithDoctests "doctests"
#else
#ifdef MIN_VERSION_Cabal
-- If the macro is defined, we have new cabal-install,
-- but for some reason we don't have cabal-doctest in package-db
--
-- Probably we are running cabal sdist, when otherwise using new-build
-- workflow
#warning You are configuring this package without cabal-doctest installed. \
The doctests test-suite will not work as a result. \
To fix this, install cabal-doctest before configuring.
#endif
import Distribution.Simple
main :: IO ()
main = defaultMain
#endif

View file

@ -0,0 +1,84 @@
cabal-version: 2.2
name: servant-auth-docs
version: 0.2.10.0
synopsis: servant-docs/servant-auth compatibility
description: This package provides instances that allow generating docs from
<https://hackage.haskell.org/package/servant servant>
APIs that use
<https://hackage.haskell.org/package/servant-auth servant-auth's> @Auth@ combinator.
.
For a quick overview of the usage, see the <https://github.com/haskell-servant/servant/tree/master/servant-auth#readme README>.
category: Web, Servant, Authentication
homepage: https://github.com/haskell-servant/servant/tree/master/servant-auth#readme
bug-reports: https://github.com/haskell-servant/servant/issues
author: Julian K. Arni
maintainer: jkarni@gmail.com
copyright: (c) Julian K. Arni
license: BSD-3-Clause
license-file: LICENSE
tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1
build-type: Custom
extra-source-files:
CHANGELOG.md
custom-setup
setup-depends:
base, Cabal, cabal-doctest >=1.0.6 && <1.1
source-repository head
type: git
location: https://github.com/haskell-servant/servant
library
hs-source-dirs:
src
default-extensions: ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
ghc-options: -Wall
build-depends:
base >= 4.10 && < 4.18
, servant-docs >= 0.11.2 && < 0.13
, servant >= 0.13 && < 0.20
, servant-auth == 0.4.*
, lens >= 4.16.1 && <5.3
exposed-modules:
Servant.Auth.Docs
default-language: Haskell2010
test-suite doctests
type: exitcode-stdio-1.0
main-is: doctests.hs
build-depends:
base,
servant-auth-docs,
doctest >= 0.16 && < 0.21,
QuickCheck >= 2.11.3 && < 2.15,
template-haskell
ghc-options: -Wall -threaded
hs-source-dirs: test
default-language: Haskell2010
test-suite spec
type: exitcode-stdio-1.0
main-is: Spec.hs
hs-source-dirs:
test
default-extensions: ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
ghc-options: -Wall
build-tool-depends: hspec-discover:hspec-discover >=2.5.5 && <2.10
-- dependencies with bounds inherited from the library stanza
build-depends:
base
, text
, servant-docs
, servant
, servant-auth
, lens
-- test dependencies
build-depends:
servant-auth-docs
, hspec >= 2.5.5 && < 2.10
, QuickCheck >= 2.11.3 && < 2.15
default-language: Haskell2010

View file

@ -0,0 +1,96 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Servant.Auth.Docs
(
-- | The purpose of this package is provide the instance for 'servant-auth'
-- combinators needed for 'servant-docs' documentation generation.
--
-- >>> type API = Auth '[JWT, Cookie, BasicAuth] Int :> Get '[JSON] Int
-- >>> putStr $ markdown $ docs (Proxy :: Proxy API)
-- ## GET /
-- ...
-- ... Authentication
-- ...
-- This part of the API is protected by the following authentication mechanisms:
-- ...
-- * JSON Web Tokens ([JWTs](https://en.wikipedia.org/wiki/JSON_Web_Token))
-- * [Cookies](https://en.wikipedia.org/wiki/HTTP_cookie)
-- * [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
-- ...
-- Clients must supply the following data
-- ...
-- One of the following:
-- ...
-- * A JWT Token signed with this server's key
-- * Cookies automatically set by browsers, plus a header
-- * Cookies automatically set by browsers, plus a header
-- ...
-- * Re-export
JWT
, BasicAuth
, Cookie
, Auth
) where
import Control.Lens ((%~), (&), (|>))
import Data.List (intercalate)
import Data.Monoid
import Data.Proxy (Proxy (Proxy))
import Servant.API hiding (BasicAuth)
import Servant.Auth
import Servant.Docs hiding (pretty)
import Servant.Docs.Internal (DocAuthentication (..), authInfo)
instance (AllDocs auths, HasDocs api) => HasDocs (Auth auths r :> api) where
docsFor _ (endpoint, action) =
docsFor (Proxy :: Proxy api) (endpoint, action & authInfo %~ (|> info))
where
(intro, reqData) = pretty $ allDocs (Proxy :: Proxy auths)
info = DocAuthentication intro reqData
pretty :: [(String, String)] -> (String, String)
pretty [] = error "shouldn't happen"
pretty [(i, d)] =
( "This part of the API is protected by " <> i
, d
)
pretty rs =
( "This part of the API is protected by the following authentication mechanisms:\n\n"
++ " * " <> intercalate "\n * " (fst <$> rs)
, "\nOne of the following:\n\n"
++ " * " <> intercalate "\n * " (snd <$> rs)
)
class AllDocs (x :: [*]) where
allDocs :: proxy x
-- intro, req
-> [(String, String)]
instance (OneDoc a, AllDocs as) => AllDocs (a ': as) where
allDocs _ = oneDoc (Proxy :: Proxy a) : allDocs (Proxy :: Proxy as)
instance AllDocs '[] where
allDocs _ = []
class OneDoc a where
oneDoc :: proxy a -> (String, String)
instance OneDoc JWT where
oneDoc _ =
("JSON Web Tokens ([JWTs](https://en.wikipedia.org/wiki/JSON_Web_Token))"
, "A JWT Token signed with this server's key")
instance OneDoc Cookie where
oneDoc _ =
("[Cookies](https://en.wikipedia.org/wiki/HTTP_cookie)"
, "Cookies automatically set by browsers, plus a header")
instance OneDoc BasicAuth where
oneDoc _ =
( "[Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)"
, "Cookies automatically set by browsers, plus a header")
-- $setup
-- >>> instance ToSample Int where toSamples _ = singleSample 1729

View file

@ -0,0 +1 @@
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}

View file

@ -0,0 +1,12 @@
module Main where
import Build_doctests (flags, pkgs, module_sources)
import Data.Foldable (traverse_)
import Test.DocTest
main :: IO ()
main = do
traverse_ putStrLn args
doctest args
where
args = flags ++ pkgs ++ module_sources

Some files were not shown because too many files have changed in this diff Show more